2019-04-18 21:15:15 +00:00
|
|
|
;; copyright (c) 2019 Sean Corfield, all rights reserved
|
|
|
|
|
|
|
|
|
|
(ns next.jdbc.sql-test
|
2019-04-19 04:35:38 +00:00
|
|
|
"Tests for the (private) SQL string building functions in next.jdbc.sql.
|
|
|
|
|
|
|
|
|
|
At some future date, tests for the syntactic sugar functions will be added."
|
2019-04-20 06:25:03 +00:00
|
|
|
(:require [clojure.test :refer [deftest is testing use-fixtures]]
|
2019-04-19 01:31:15 +00:00
|
|
|
[next.jdbc.quoted :refer [mysql sql-server]]
|
2019-05-22 04:45:05 +00:00
|
|
|
[next.jdbc.specs :as specs]
|
2019-04-20 06:25:03 +00:00
|
|
|
[next.jdbc.sql :as sql]
|
2019-04-22 00:10:29 +00:00
|
|
|
[next.jdbc.test-fixtures
|
|
|
|
|
:refer [with-test-db ds derby? sqlite?]]))
|
2019-04-20 06:25:03 +00:00
|
|
|
|
2019-05-29 16:04:21 +00:00
|
|
|
(set! *warn-on-reflection* true)
|
|
|
|
|
|
2019-04-20 06:25:03 +00:00
|
|
|
(use-fixtures :once with-test-db)
|
2019-04-19 01:31:15 +00:00
|
|
|
|
2019-05-22 04:45:05 +00:00
|
|
|
(specs/instrument)
|
|
|
|
|
|
2019-04-19 01:31:15 +00:00
|
|
|
(deftest test-by-keys
|
|
|
|
|
(testing ":where clause"
|
|
|
|
|
(is (= (#'sql/by-keys {:a nil :b 42 :c "s"} :where {})
|
|
|
|
|
["WHERE a IS NULL AND b = ? AND c = ?" 42 "s"])))
|
|
|
|
|
(testing ":set clause"
|
|
|
|
|
(is (= (#'sql/by-keys {:a nil :b 42 :c "s"} :set {})
|
|
|
|
|
["SET a = ?, b = ?, c = ?" nil 42 "s"]))))
|
|
|
|
|
|
|
|
|
|
(deftest test-as-keys
|
|
|
|
|
(is (= (#'sql/as-keys {:a nil :b 42 :c "s"} {})
|
|
|
|
|
"a, b, c")))
|
|
|
|
|
|
|
|
|
|
(deftest test-as-?
|
|
|
|
|
(is (= (#'sql/as-? {:a nil :b 42 :c "s"} {})
|
|
|
|
|
"?, ?, ?")))
|
|
|
|
|
|
|
|
|
|
(deftest test-for-query
|
|
|
|
|
(testing "by example"
|
2019-04-21 20:42:33 +00:00
|
|
|
(is (= (#'sql/for-query :user {:id 9}
|
|
|
|
|
{:table-fn sql-server :column-fn mysql :order-by [:a [:b :desc]]})
|
|
|
|
|
["SELECT * FROM [user] WHERE `id` = ? ORDER BY `a`, `b` DESC" 9]))
|
2019-04-19 01:31:15 +00:00
|
|
|
(is (= (#'sql/for-query :user {:id nil} {:table-fn sql-server :column-fn mysql})
|
|
|
|
|
["SELECT * FROM [user] WHERE `id` IS NULL"])))
|
|
|
|
|
(testing "by where clause"
|
2019-04-21 20:42:33 +00:00
|
|
|
(is (= (#'sql/for-query :user ["id = ? and opt is null" 9]
|
|
|
|
|
{:table-fn sql-server :column-fn mysql :order-by [:a [:b :desc]]})
|
|
|
|
|
[(str "SELECT * FROM [user] WHERE id = ? and opt is null"
|
|
|
|
|
" ORDER BY `a`, `b` DESC") 9]))))
|
2019-04-19 01:31:15 +00:00
|
|
|
|
|
|
|
|
(deftest test-for-delete
|
|
|
|
|
(testing "by example"
|
|
|
|
|
(is (= (#'sql/for-delete :user {:opt nil :id 9} {:table-fn sql-server :column-fn mysql})
|
|
|
|
|
["DELETE FROM [user] WHERE `opt` IS NULL AND `id` = ?" 9])))
|
|
|
|
|
(testing "by where clause"
|
|
|
|
|
(is (= (#'sql/for-delete :user ["id = ? and opt is null" 9] {:table-fn sql-server :column-fn mysql})
|
|
|
|
|
["DELETE FROM [user] WHERE id = ? and opt is null" 9]))))
|
|
|
|
|
|
|
|
|
|
(deftest test-for-update
|
2019-07-11 19:11:32 +00:00
|
|
|
(testing "empty example (would be a SQL error)"
|
|
|
|
|
(is (thrown? AssertionError ; changed in #44
|
|
|
|
|
(#'sql/for-update :user {:status 42} {} {:table-fn sql-server :column-fn mysql}))))
|
2019-04-19 01:31:15 +00:00
|
|
|
(testing "by example"
|
|
|
|
|
(is (= (#'sql/for-update :user {:status 42} {:id 9} {:table-fn sql-server :column-fn mysql})
|
|
|
|
|
["UPDATE [user] SET `status` = ? WHERE `id` = ?" 42 9])))
|
|
|
|
|
(testing "by where clause, with nil set value"
|
|
|
|
|
(is (= (#'sql/for-update :user {:status 42, :opt nil} ["id = ?" 9] {:table-fn sql-server :column-fn mysql})
|
|
|
|
|
["UPDATE [user] SET `status` = ?, `opt` = ? WHERE id = ?" 42 nil 9]))))
|
|
|
|
|
|
|
|
|
|
(deftest test-for-inserts
|
|
|
|
|
(testing "single insert"
|
|
|
|
|
(is (= (#'sql/for-insert :user {:id 9 :status 42 :opt nil} {:table-fn sql-server :column-fn mysql})
|
|
|
|
|
["INSERT INTO [user] (`id`, `status`, `opt`) VALUES (?, ?, ?)" 9 42 nil])))
|
|
|
|
|
(testing "multi-row insert"
|
|
|
|
|
(is (= (#'sql/for-insert-multi :user [:id :status]
|
|
|
|
|
[[42 "hello"]
|
|
|
|
|
[35 "world"]
|
|
|
|
|
[64 "dollars"]]
|
|
|
|
|
{:table-fn sql-server :column-fn mysql})
|
|
|
|
|
["INSERT INTO [user] (`id`, `status`) VALUES (?, ?), (?, ?), (?, ?)" 42 "hello" 35 "world" 64 "dollars"]))))
|
2019-04-20 06:25:03 +00:00
|
|
|
|
|
|
|
|
(deftest test-query
|
|
|
|
|
(let [rs (sql/query (ds) ["select * from fruit order by id"])]
|
|
|
|
|
(is (= 4 (count rs)))
|
|
|
|
|
(is (every? map? rs))
|
|
|
|
|
(is (every? meta rs))
|
|
|
|
|
(is (= 1 (:FRUIT/ID (first rs))))
|
|
|
|
|
(is (= 4 (:FRUIT/ID (last rs))))))
|
|
|
|
|
|
|
|
|
|
(deftest test-find-by-keys
|
|
|
|
|
(let [rs (sql/find-by-keys (ds) :fruit {:appearance "yellow"})]
|
|
|
|
|
(is (= 1 (count rs)))
|
|
|
|
|
(is (every? map? rs))
|
|
|
|
|
(is (every? meta rs))
|
|
|
|
|
(is (= 2 (:FRUIT/ID (first rs))))))
|
|
|
|
|
|
|
|
|
|
(deftest test-get-by-id
|
|
|
|
|
(let [row (sql/get-by-id (ds) :fruit 3)]
|
|
|
|
|
(is (map? row))
|
|
|
|
|
(is (= "Peach" (:FRUIT/NAME row))))
|
|
|
|
|
(let [row (sql/get-by-id (ds) :fruit "juicy" :appearance {})]
|
|
|
|
|
(is (map? row))
|
|
|
|
|
(is (= 4 (:FRUIT/ID row)))
|
|
|
|
|
(is (= "Orange" (:FRUIT/NAME row))))
|
|
|
|
|
(let [row (sql/get-by-id (ds) :fruit "Banana" :FRUIT/NAME {})]
|
|
|
|
|
(is (map? row))
|
|
|
|
|
(is (= 2 (:FRUIT/ID row)))))
|
|
|
|
|
|
|
|
|
|
(deftest test-update!
|
|
|
|
|
(try
|
|
|
|
|
(is (= {:next.jdbc/update-count 1}
|
|
|
|
|
(sql/update! (ds) :fruit {:appearance "brown"} {:id 2})))
|
|
|
|
|
(is (= "brown" (:FRUIT/APPEARANCE
|
|
|
|
|
(sql/get-by-id (ds) :fruit 2))))
|
|
|
|
|
(finally
|
|
|
|
|
(sql/update! (ds) :fruit {:appearance "yellow"} {:id 2})))
|
|
|
|
|
(try
|
|
|
|
|
(is (= {:next.jdbc/update-count 1}
|
|
|
|
|
(sql/update! (ds) :fruit {:appearance "green"}
|
|
|
|
|
["name = ?" "Banana"])))
|
|
|
|
|
(is (= "green" (:FRUIT/APPEARANCE
|
|
|
|
|
(sql/get-by-id (ds) :fruit 2))))
|
|
|
|
|
(finally
|
|
|
|
|
(sql/update! (ds) :fruit {:appearance "yellow"} {:id 2}))))
|
|
|
|
|
|
|
|
|
|
(deftest test-insert-delete
|
|
|
|
|
(testing "single insert/delete"
|
2019-04-22 00:10:29 +00:00
|
|
|
(is (= (cond (derby?)
|
|
|
|
|
{:1 5M}
|
|
|
|
|
(sqlite?)
|
|
|
|
|
{(keyword "last_insert_rowid()") 5}
|
|
|
|
|
:else
|
|
|
|
|
{:FRUIT/ID 5})
|
2019-04-21 06:42:22 +00:00
|
|
|
(sql/insert! (ds) :fruit
|
|
|
|
|
{:name "Kiwi" :appearance "green & fuzzy"
|
|
|
|
|
:cost 100 :grade 99.9})))
|
2019-04-20 06:25:03 +00:00
|
|
|
(is (= 5 (count (sql/query (ds) ["select * from fruit"]))))
|
|
|
|
|
(is (= {:next.jdbc/update-count 1}
|
|
|
|
|
(sql/delete! (ds) :fruit {:id 5})))
|
|
|
|
|
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
|
|
|
|
(testing "multiple insert/delete"
|
2019-04-22 00:10:29 +00:00
|
|
|
(is (= (cond (derby?)
|
|
|
|
|
[{:1 nil}] ; WTF Apache Derby?
|
|
|
|
|
(sqlite?)
|
|
|
|
|
[{(keyword "last_insert_rowid()") 8}]
|
|
|
|
|
:else
|
|
|
|
|
[{:FRUIT/ID 6} {:FRUIT/ID 7} {:FRUIT/ID 8}])
|
2019-04-21 06:42:22 +00:00
|
|
|
(sql/insert-multi! (ds) :fruit
|
|
|
|
|
[:name :appearance :cost :grade]
|
|
|
|
|
[["Kiwi" "green & fuzzy" 100 99.9]
|
|
|
|
|
["Grape" "black" 10 50]
|
|
|
|
|
["Lemon" "yellow" 20 9.9]])))
|
2019-04-20 06:25:03 +00:00
|
|
|
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
|
|
|
|
(is (= {:next.jdbc/update-count 1}
|
|
|
|
|
(sql/delete! (ds) :fruit {:id 6})))
|
|
|
|
|
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
|
|
|
|
(is (= {:next.jdbc/update-count 2}
|
|
|
|
|
(sql/delete! (ds) :fruit ["id > ?" 4])))
|
2019-07-11 19:11:32 +00:00
|
|
|
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
2019-07-11 19:34:02 +00:00
|
|
|
(testing "multiple insert/delete with sequential cols/rows" ; per #43
|
|
|
|
|
(is (= (cond (derby?)
|
|
|
|
|
[{:1 nil}] ; WTF Apache Derby?
|
|
|
|
|
(sqlite?)
|
|
|
|
|
[{(keyword "last_insert_rowid()") 11}]
|
|
|
|
|
:else
|
|
|
|
|
[{:FRUIT/ID 9} {:FRUIT/ID 10} {:FRUIT/ID 11}])
|
|
|
|
|
(sql/insert-multi! (ds) :fruit
|
|
|
|
|
'(:name :appearance :cost :grade)
|
|
|
|
|
'(("Kiwi" "green & fuzzy" 100 99.9)
|
|
|
|
|
("Grape" "black" 10 50)
|
|
|
|
|
("Lemon" "yellow" 20 9.9)))))
|
|
|
|
|
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
|
|
|
|
(is (= {:next.jdbc/update-count 1}
|
|
|
|
|
(sql/delete! (ds) :fruit {:id 9})))
|
|
|
|
|
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
|
|
|
|
(is (= {:next.jdbc/update-count 2}
|
|
|
|
|
(sql/delete! (ds) :fruit ["id > ?" 4])))
|
|
|
|
|
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
2019-07-11 19:11:32 +00:00
|
|
|
(testing "empty insert-multi!" ; per #44
|
|
|
|
|
(is (= [] (sql/insert-multi! (ds) :fruit
|
|
|
|
|
[:name :appearance :cost :grade]
|
|
|
|
|
[])))))
|
2019-07-03 01:50:25 +00:00
|
|
|
|
|
|
|
|
(deftest no-empty-example-maps
|
|
|
|
|
(is (thrown? clojure.lang.ExceptionInfo
|
|
|
|
|
(sql/find-by-keys (ds) :fruit {})))
|
|
|
|
|
(is (thrown? clojure.lang.ExceptionInfo
|
|
|
|
|
(sql/update! (ds) :fruit {} {})))
|
|
|
|
|
(is (thrown? clojure.lang.ExceptionInfo
|
|
|
|
|
(sql/delete! (ds) :fruit {}))))
|
2019-07-11 19:11:32 +00:00
|
|
|
|
|
|
|
|
(deftest no-empty-order-by
|
|
|
|
|
(is (thrown? clojure.lang.ExceptionInfo
|
|
|
|
|
(sql/find-by-keys (ds) :fruit
|
|
|
|
|
{:name "Apple"}
|
|
|
|
|
{:order-by []}))))
|