add tests

This commit is contained in:
Gered 2016-05-31 18:20:09 -04:00
parent aa58b3116c
commit 0bf94fa60f
6 changed files with 250 additions and 5 deletions

View file

@ -9,7 +9,11 @@
[com.github.jsqlparser/jsqlparser "0.9.5"]]
:profiles {:provided
{:dependencies
[[org.clojure/clojure "1.8.0"]
[org.clojure/java.jdbc "0.6.1"]
[gered/views "1.5-SNAPSHOT"]]}})
{:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/java.jdbc "0.6.1"]
[gered/views "1.5-SNAPSHOT"]]}
:test
{:dependencies [[pjstadig/humane-test-output "0.8.0"]]
:injections [(require 'pjstadig.humane-test-output)
(pjstadig.humane-test-output/activate!)]}})

View file

@ -48,7 +48,7 @@
(if-not stmt-type
(throw (new Exception "Unsupported SQL query. Only SELECT, INSERT, UPDATE and DELETE queries are supported!"))
{:type stmt-type
:returning? (sql-stmt-returning? stmt stmt-type)
:returning? (boolean (sql-stmt-returning? stmt stmt-type))
:tables (get-query-tables-set stmt)})))
(defonce query-info-cache (atom {}))

View file

@ -0,0 +1,51 @@
(ns views.sql.sql-analysis-tests
(:use
clojure.test
views.sql.test-fixtures
views.sql.core))
(use-fixtures :each clear-query-cache-fixture)
;; NOTE: the purpose of the tests which look at analysis results of SQL queries
;; is __NOT__ to test how good the support of JSqlParser is, but just
;; to ensure that the views.sql abstraction over it works.
(deftest select-query-analyzed-correctly
(let [info (query-info "SELECT * FROM book WHERE price > 100.00 ORDER BY title")]
(is (= :select (:type info)))
(is (= true (:returning? info)))
(is (= #{:book} (:tables info)))))
(deftest insert-query-analyzed-correctly
(let [info (query-info "INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL)")]
(is (= :insert (:type info)))
(is (= false (:returning? info)))
(is (= #{:example} (:tables info)))))
(deftest insert-query-with-returning-clause-analyzed-correctly
(let [info (query-info "INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL) RETURNING *")]
(is (= :insert (:type info)))
(is (= true (:returning? info)))
(is (= #{:example} (:tables info)))))
(deftest update-query-analyzed-correctly
(let [info (query-info "UPDATE example SET field1 = 'updated value' WHERE field2 = 'N'")]
(is (= :update (:type info)))
(is (= false (:returning? info)))
(is (= #{:example} (:tables info)))))
(deftest delete-query-analyzed-correctly
(let [info (query-info "DELETE FROM example WHERE field2 = 'N'")]
(is (= :delete (:type info)))
(is (= false (:returning? info)))
(is (= #{:example} (:tables info)))))
(deftest unsupported-query-types-throw-exception
(is (thrown? Exception (query-info "ALTER TABLE example ADD column4 NUMBER(3) NOT NULL"))))
(deftest multiple-query-tables-are-included-correctly
(let [info (query-info "SELECT * FROM book JOIN publisher ON book.publisher_id = publisher.id WHERE price > 100.00 ORDER BY title")]
(is (= :select (:type info)))
(is (= true (:returning? info)))
(is (= #{:book :publisher} (:tables info)))))

View file

@ -0,0 +1,16 @@
(ns views.sql.test-fixtures
(:use
clojure.test
views.sql.core))
(def test-db {:not-a-real-db-conn true})
(def test-view-system (atom {}))
(defn clear-query-cache-fixture [f]
(reset! query-info-cache {})
(f))
(defn reset-test-view-system-fixture [f]
(reset! test-view-system {})
(f))

View file

@ -0,0 +1,78 @@
(ns views.sql.vexec-tests
(:use
clojure.test
views.sql.test-fixtures
views.sql.core)
(:require
[clojure.java.jdbc :as jdbc]
[views.core :as views]))
(def redefs-called (atom {}))
(defn ->redef-fn
[name & [return-value]]
(fn [& args]
(swap! redefs-called assoc name (vec args))
return-value))
(defn vexec-redefs-fixture [f]
(reset! redefs-called {})
(with-redefs
[jdbc/query (->redef-fn :jdbc/query :jdbc/query-return-value)
jdbc/execute! (->redef-fn :jdbc/execute! :jdbc/execute!-return-value)
views/put-hints! (->redef-fn :views/put-hints!)]
(f)))
(use-fixtures :each clear-query-cache-fixture reset-test-view-system-fixture vexec-redefs-fixture)
(deftest vexec-runs-query-and-puts-hints
(let [sqlvec ["INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL)"]
result (vexec! test-view-system test-db sqlvec)]
(is (= [test-db sqlvec] (:jdbc/execute! @redefs-called)))
(is (= :jdbc/execute!-return-value result))
(is (= [test-view-system [(views/hint nil #{:example} hint-type)]]
(:views/put-hints! @redefs-called)))))
(deftest vexec-runs-query-with-returning-clause
(let [sqlvec ["INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL) RETURNING *"]
result (vexec! test-view-system test-db sqlvec)]
(is (= [test-db sqlvec] (:jdbc/query @redefs-called)))
(is (= :jdbc/query-return-value result))
(is (= [test-view-system [(views/hint nil #{:example} hint-type)]]
(:views/put-hints! @redefs-called)))))
(deftest namespace-is-passed-along-to-hints-via-vexec
(let [sqlvec ["INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL)"]
result (vexec! test-view-system test-db sqlvec {:namespace :foobar})]
(is (= [test-db sqlvec] (:jdbc/execute! @redefs-called)))
(is (= :jdbc/execute!-return-value result))
(is (= [test-view-system [(views/hint :foobar #{:example} hint-type)]]
(:views/put-hints! @redefs-called)))))
(deftest manually-provided-hints-to-vexec-are-passed-to-views-system
(let [sqlvec ["INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL)"]
result (vexec! test-view-system test-db sqlvec [:foo :bar])]
(is (= [test-db sqlvec] (:jdbc/execute! @redefs-called)))
(is (= :jdbc/execute!-return-value result))
(is (= [test-view-system [(views/hint nil #{:foo :bar} hint-type)]]
(:views/put-hints! @redefs-called)))))
(deftest manually-provided-hints-to-vexec-also-requires-specifying-returning-option
(let [sqlvec ["INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL) RETURNING *"]
result (vexec! test-view-system test-db sqlvec [:foo :bar])]
; means jdbc/execute! was called which is correct behaviour since the returning option was not specified.
; if jdbc/execute! was really called it would throw an exception since the INSERT query used has a RETURNING clause
(is (= [test-db sqlvec] (:jdbc/execute! @redefs-called)))
(is (= :jdbc/execute!-return-value result))
(is (= [test-view-system [(views/hint nil #{:foo :bar} hint-type)]]
(:views/put-hints! @redefs-called))))
; manually reset some things
(reset! redefs-called {})
(reset! query-info-cache {})
(let [sqlvec ["INSERT INTO example (field1, field2, field3) VALUES ('test', 'N', NULL) RETURNING *"]
result (vexec! test-view-system test-db sqlvec [:foo :bar] {:returning? true})]
(is (= [test-db sqlvec] (:jdbc/query @redefs-called)))
(is (= :jdbc/query-return-value result))
(is (= [test-view-system [(views/hint nil #{:foo :bar} hint-type)]]
(:views/put-hints! @redefs-called)))))

View file

@ -0,0 +1,96 @@
(ns views.sql.view-test
(:import (views.protocols IView))
(:use
clojure.test
views.sql.test-fixtures
views.sql.core
views.sql.view
views.protocols)
(:require
[clojure.java.jdbc :as jdbc]
[views.core :as views]))
(def redefs-called (atom {}))
(defn ->redef-fn
[name & [return-value]]
(fn [& args]
(swap! redefs-called assoc name (vec args))
return-value))
(defn vexec-redefs-fixture [f]
(reset! redefs-called {})
(with-redefs
[jdbc/query (->redef-fn :jdbc/query :jdbc/query-return-value)
views/put-hints! (->redef-fn :views/put-hints!)]
(f)))
(use-fixtures :each clear-query-cache-fixture reset-test-view-system-fixture vexec-redefs-fixture)
(deftest basic-sql-view-works
(let [sqlvec ["SELECT * FROM foobar"]
sql-fn (fn [] sqlvec)
sql-view (view :test-view test-db sql-fn)]
(is (satisfies? IView sql-view))
(is (= :test-view (id sql-view)))
(is (= true (relevant? sql-view nil [] [(views/hint nil #{:foobar} hint-type)])))
(is (= false (relevant? sql-view nil [] [(views/hint nil #{:baz} hint-type)])))
(is (= :jdbc/query-return-value (data sql-view nil [])))
(is (= [test-db sqlvec {}]
(:jdbc/query @redefs-called)))))
(deftest basic-sql-view-works-with-parameters
(let [sqlvec ["SELECT * FROM foobar"]
sql-fn (fn [a b] (into sqlvec [a b]))
sql-view (view :test-view test-db sql-fn)]
(is (= true (relevant? sql-view nil [1 2] [(views/hint nil #{:foobar} hint-type)])))
(is (= :jdbc/query-return-value (data sql-view nil [1 2])))
(is (= [test-db (into sqlvec [1 2]) {}]
(:jdbc/query @redefs-called)))))
(deftest basic-sql-view-works-with-namespace
(let [sqlvec ["SELECT * FROM foobar"]
sql-fn (fn [] sqlvec)
sql-view (view :test-view test-db sql-fn)]
(is (= true (relevant? sql-view :abc [] [(views/hint :abc #{:foobar} hint-type)])))
(is (= false (relevant? sql-view :123 [] [(views/hint :abc #{:foobar} hint-type)])))
(is (= :jdbc/query-return-value (data sql-view nil [])))
(is (= [test-db sqlvec {}]
(:jdbc/query @redefs-called)))))
(deftest view-db-fn-is-used-when-provided
(let [alt-test-db {:alternate-test-db-conn true}
db-fn (fn [namespace] alt-test-db)
sqlvec ["SELECT * FROM foobar"]
sql-fn (fn [] sqlvec)
sql-view (view :test-view db-fn sql-fn)]
(is (= :jdbc/query-return-value (data sql-view nil [])))
(is (= [alt-test-db sqlvec {}]
(:jdbc/query @redefs-called)))))
(deftest view-db-fn-is-passed-namespace
(let [test-namespace :test-namespace
alt-test-db {:alternate-test-db-conn true}
db-fn (fn [namespace]
(is (= namespace :test-namespace))
alt-test-db)
sqlvec ["SELECT * FROM foobar"]
sql-fn (fn [] sqlvec)
sql-view (view :test-view db-fn sql-fn)]
(is (= :jdbc/query-return-value (data sql-view test-namespace [])))
(is (= [alt-test-db sqlvec {}]
(:jdbc/query @redefs-called)))))
(deftest manually-specified-view-hints-are-used-correctly
(with-redefs
[query-tables (->redef-fn :query-tables)]
(let [sqlvec ["SELECT * FROM foobar"]
sql-fn (fn [] sqlvec)
sql-view (view :test-view test-db sql-fn [:foo :bar])]
(is (= false (relevant? sql-view nil [] [(views/hint nil #{:foobar} hint-type)])))
(is (= true (relevant? sql-view nil [] [(views/hint nil #{:foo} hint-type)])))
(is (= true (relevant? sql-view nil [] [(views/hint nil #{:bar} hint-type)])))
(is (= true (relevant? sql-view nil [] [(views/hint nil #{:bar} hint-type)
(views/hint nil #{:foo} hint-type)])))
(is (not (contains? @redefs-called :query-tables))))))