diff --git a/test/views/honeysql/honeysql_analysis_tests.clj b/test/views/honeysql/honeysql_analysis_tests.clj new file mode 100644 index 0000000..14ccf64 --- /dev/null +++ b/test/views/honeysql/honeysql_analysis_tests.clj @@ -0,0 +1,98 @@ +(ns views.honeysql.honeysql-analysis-tests + (:use + clojure.test + views.honeysql.test-fixtures + views.honeysql.util) + (:require + [honeysql.core :as hsql])) + +(deftest simple-from-clause + (is (= #{:foo} + (query-tables + {:select [:a :b :c] + :from [:foo]})))) + +(deftest from-clause-with-multiple-tables + (is (= #{:foo :bar} + (query-tables + {:select [:foo.a :foo.b :foo.c :bar.d] + :from [:foo :bar]})))) + +(deftest inner-join + (is (= #{:foo :bar} + (query-tables + {:select [:a :b :c] + :from [:foo] + :join [:bar [:= :d :a]]})))) + +(deftest left-join + (is (= #{:foo :bar} + (query-tables + {:select [:a :b :c] + :from [:foo] + :left-join [:bar [:= :d :a]]})))) + +(deftest right-join + (is (= #{:foo :bar} + (query-tables + {:select [:a :b :c] + :from [:foo] + :right-join [:bar [:= :d :a]]})))) + +(deftest table-names-with-aliases + (is (= #{:foo :bar} + (query-tables + {:select [:a :b :c] + :from [[:foo :f]] + :join [[:bar :b] [:= :b.foo_id :f.foo_id]]})))) + +(deftest where-clause-subquery + (is (= #{:foo :bar} + (query-tables + {:select [:*] + :from [[:foo :f]] + :where [:in :f.foo_id {:select [:b.foo_id] + :from [[:bar :b]]}]})))) + +(deftest insert-query + (is (= #{:foo} + (query-tables + {:insert-into :foo + :values [{:a "a" :b "b" :c "c"}]})))) + +(deftest update-query + (is (= #{:foo} + (query-tables + {:update :foo + :set {:a "aaa" :b "bbb" :c "ccc"} + :where [:= :foo_id 42]})))) + +(deftest delete-query + (is (= #{:foo} + (query-tables + {:delete-from :foo + :where [:= :foo_id 42]})))) + +(deftest with-clause + ; this query shamelessly taken from https://www.postgresql.org/docs/8.4/static/queries-with.html + ; and converted to honeysql for this test + ; NOTE: tables returned include the names of the CTE temp tables which is + ; probably not really desired behaviour. should fix this. + ; however, for now this test does demonstrate it is correctly looking at sub-queries + ; inside of the with-clause. + (is (= #{:orders :top_regions :regional_sales} + (query-tables + {:with [[:regional_sales + {:select [:region [(hsql/call :sum :amount) "total_sales"]] + :from [:orders] + :group-by [:region]}] + [:top_regions + {:select [:region] + :from [:regional_sales] + :where [:> :total_sales {:select [(hsql/raw "SUM(total_sales) / 10")] + :from [:regional_sales]}]}]] + :select [:region :product [(hsql/call :sum :quantity) "product_units"] [(hsql/call :sum :amount) "product_sales"]] + :from [:orders] + :where [:in :region {:select [:region] + :from [:top_regions]}] + :group-by [:region :product]})))) diff --git a/test/views/honeysql/test_fixtures.clj b/test/views/honeysql/test_fixtures.clj new file mode 100644 index 0000000..1f08b7e --- /dev/null +++ b/test/views/honeysql/test_fixtures.clj @@ -0,0 +1,31 @@ +(ns views.honeysql.test-fixtures + (:use + clojure.test + views.honeysql.core)) + +(def test-db {:not-a-real-db-conn true}) + +(def test-view-system (atom {})) + +(defn reset-test-view-system-fixture [f] + (reset! test-view-system {}) + (f)) + +(def redefs-called (atom {})) + +(defn ->redef-fn + [name & [return-value]] + (fn [& args] + (swap! redefs-called update-in [name] + #((fnil conj []) % (vec args))) + return-value)) + +(defn called-with-args? + [fn-name-kw & args] + (->> (get @redefs-called fn-name-kw) + (some #(= % (vec args))) + (boolean))) + +(defn not-called? + [fn-name-kw] + (not (contains? @redefs-called fn-name-kw))) diff --git a/test/views/honeysql/vexec_tests.clj b/test/views/honeysql/vexec_tests.clj new file mode 100644 index 0000000..a0dde78 --- /dev/null +++ b/test/views/honeysql/vexec_tests.clj @@ -0,0 +1,53 @@ +(ns views.honeysql.vexec-tests + (:use + clojure.test + views.honeysql.test-fixtures + views.honeysql.core) + (:require + [clojure.java.jdbc :as jdbc] + [views.core :as views] + [honeysql.core :as hsql])) + +(defn vexec-redefs-fixture [f] + (reset! redefs-called {}) + (with-redefs + [jdbc/insert! (->redef-fn :jdbc/insert! :jdbc/insert!-return-value) + jdbc/execute! (->redef-fn :jdbc/execute! :jdbc/execute!-return-value) + views/put-hints! (->redef-fn :views/put-hints!)] + (f))) + + +(use-fixtures :each reset-test-view-system-fixture vexec-redefs-fixture) + +(deftest vexec-runs-query-and-puts-hints + (let [sql {:insert-into :example + :values [{:field1 "test" :field2 "N" :field3 nil}]} + result (vexec! test-view-system test-db sql)] + (is (called-with-args? :jdbc/insert! test-db (:insert-into sql) (first (:values sql)))) + (is (= :jdbc/insert!-return-value result)) + (is (called-with-args? :views/put-hints! test-view-system [(views/hint nil #{:example} hint-type)])))) + +(deftest namespace-is-passed-along-to-hints-via-vexec + (let [sql {:insert-into :example + :values [{:field1 "test" :field2 "N" :field3 nil}]} + result (vexec! test-view-system test-db :foobar sql)] + (is (called-with-args? :jdbc/insert! test-db (:insert-into sql) (first (:values sql)))) + (is (= :jdbc/insert!-return-value result)) + (is (called-with-args? :views/put-hints! test-view-system [(views/hint :foobar #{:example} hint-type)])))) + +(deftest vexec-runs-update-queries + (let [sql {:update :example + :set {:field1 "foo" :field2 "bar" :field3 "baz"} + :where [:= :id 42]} + result (vexec! test-view-system test-db sql)] + (is (called-with-args? :jdbc/execute! test-db (hsql/format sql))) + (is (= :jdbc/execute!-return-value result)) + (is (called-with-args? :views/put-hints! test-view-system [(views/hint nil #{:example} hint-type)])))) + +(deftest vexec-runs-delete-queries + (let [sql {:delete-from :example + :where [:= :id 42]} + result (vexec! test-view-system test-db sql)] + (is (called-with-args? :jdbc/execute! test-db (hsql/format sql))) + (is (= :jdbc/execute!-return-value result)) + (is (called-with-args? :views/put-hints! test-view-system [(views/hint nil #{:example} hint-type)])))) diff --git a/test/views/honeysql/view_tests.clj b/test/views/honeysql/view_tests.clj new file mode 100644 index 0000000..50e7bcf --- /dev/null +++ b/test/views/honeysql/view_tests.clj @@ -0,0 +1,85 @@ +(ns views.honeysql.view-tests + (:use + clojure.test + views.honeysql.test-fixtures + views.honeysql.core + views.honeysql.view + views.protocols) + (:require + [clojure.java.jdbc :as jdbc] + [views.core :as views] + [honeysql.core :as hsql])) + +(defn view-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 reset-test-view-system-fixture view-redefs-fixture) + +(deftest basic-sql-view-works + (let [sql-fn (fn [] {:select [:*] + :from [:foobar]}) + 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 (called-with-args? :jdbc/query test-db (hsql/format (sql-fn)) {})))) + +(deftest basic-sql-view-works-with-parameters + (let [sql-fn (fn [a b] + {:select [:*] + :from [:foobar] + :where [:and + [:= :a a] + [:= :b 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 (called-with-args? :jdbc/query test-db (hsql/format (sql-fn 1 2)) {})))) + +(deftest basic-sql-view-works-with-namespace + (let [sql-fn (fn [] {:select [:*] + :from [:foobar]}) + 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 (called-with-args? :jdbc/query test-db (hsql/format (sql-fn)) {})))) + +(deftest view-db-fn-is-used-when-provided + (let [alt-test-db {:alternate-test-db-conn true} + db-fn (fn [namespace] alt-test-db) + sql-fn (fn [] {:select [:*] + :from [:foobar]}) + sql-view (view :test-view db-fn sql-fn)] + (is (= :jdbc/query-return-value (data sql-view nil []))) + (is (called-with-args? :jdbc/query alt-test-db (hsql/format (sql-fn)) {})))) + +(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) + sql-fn (fn [] {:select [:*] + :from [:foobar]}) + sql-view (view :test-view db-fn sql-fn)] + (is (= :jdbc/query-return-value (data sql-view test-namespace []))) + (is (called-with-args? :jdbc/query alt-test-db (hsql/format (sql-fn)) {})))) + +(deftest row-and-result-set-fns-are-passed-to-jdbc + (let [row-fn (fn [row] row) + result-set-fn (fn [results] results) + sql-fn (fn [] {:select [:*] + :from [:foobar]}) + sql-view (view :test-view test-db sql-fn {:row-fn row-fn + :result-set-fn result-set-fn})] + (is (= :jdbc/query-return-value (data sql-view nil []))) + (is (called-with-args? :jdbc/query test-db (hsql/format (sql-fn)) {:row-fn row-fn + :result-set-fn result-set-fn}))))