Pre: Use deftest-db in tests.
This commit is contained in:
parent
8e8dd21164
commit
cfdce12c1a
2 changed files with 400 additions and 501 deletions
|
@ -97,7 +97,6 @@
|
||||||
new))]
|
new))]
|
||||||
(let [exec (partial s/execute! (:sqlite-connection db))
|
(let [exec (partial s/execute! (:sqlite-connection db))
|
||||||
part->vector (fn [[part {:keys [start idx]}]]
|
part->vector (fn [[part {:keys [start idx]}]]
|
||||||
(println "part->vector" part start idx)
|
|
||||||
[(sqlite-schema/->SQLite part) start idx])]
|
[(sqlite-schema/->SQLite part) start idx])]
|
||||||
;; TODO: allow inserting new parts.
|
;; TODO: allow inserting new parts.
|
||||||
;; TODO: think more carefully about allocating new parts and bitmasking part ranges.
|
;; TODO: think more carefully about allocating new parts and bitmasking part ranges.
|
||||||
|
|
|
@ -19,12 +19,12 @@
|
||||||
#?@(:clj [[datomish.jdbc-sqlite]
|
#?@(:clj [[datomish.jdbc-sqlite]
|
||||||
[datomish.pair-chan :refer [go-pair <?]]
|
[datomish.pair-chan :refer [go-pair <?]]
|
||||||
[tempfile.core :refer [tempfile with-tempfile]]
|
[tempfile.core :refer [tempfile with-tempfile]]
|
||||||
[datomish.test-macros :refer [deftest-async]]
|
[datomish.test-macros :refer [deftest-async deftest-db]]
|
||||||
[clojure.test :as t :refer [is are deftest testing]]
|
[clojure.test :as t :refer [is are deftest testing]]
|
||||||
[clojure.core.async :refer [go <! >!]]])
|
[clojure.core.async :refer [go <! >!]]])
|
||||||
#?@(:cljs [[datomish.promise-sqlite]
|
#?@(:cljs [[datomish.promise-sqlite]
|
||||||
[datomish.pair-chan]
|
[datomish.pair-chan]
|
||||||
[datomish.test-macros :refer-macros [deftest-async]]
|
[datomish.test-macros :refer-macros [deftest-async deftest-db]]
|
||||||
[datomish.node-tempfile :refer [tempfile]]
|
[datomish.node-tempfile :refer [tempfile]]
|
||||||
[cljs.test :as t :refer-macros [is are deftest testing async]]
|
[cljs.test :as t :refer-macros [is are deftest testing async]]
|
||||||
[cljs.core.async :as a :refer [<! >!]]]))
|
[cljs.core.async :as a :refer [<! >!]]]))
|
||||||
|
@ -81,541 +81,445 @@
|
||||||
:db.install/_attribute :db.part/db}
|
:db.install/_attribute :db.part/db}
|
||||||
])
|
])
|
||||||
|
|
||||||
(deftest-async test-add-one
|
(deftest-db test-add-one conn
|
||||||
(with-tempfile [t (tempfile)]
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
||||||
(let [conn (<? (d/<connect t))]
|
(let [{:keys [tx txInstant]} (<? (d/<transact! conn [[:db/add 0 :name "valuex"]]))]
|
||||||
(try
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
#{[0 :name "valuex"]}))
|
||||||
(let [{:keys [tx txInstant]} (<? (d/<transact! conn [[:db/add 0 :name "valuex"]]))]
|
(is (= (<? (<transactions-after (d/db conn) tx0))
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
[[0 :name "valuex" tx 1] ;; TODO: true, not 1.
|
||||||
#{[0 :name "valuex"]}))
|
[tx :db/txInstant txInstant tx 1]])))))
|
||||||
(is (= (<? (<transactions-after (d/db conn) tx0))
|
|
||||||
[[0 :name "valuex" tx 1] ;; TODO: true, not 1.
|
|
||||||
[tx :db/txInstant txInstant tx 1]]))))
|
|
||||||
(finally
|
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
||||||
(deftest-async test-add-two
|
(deftest-db test-add-two conn
|
||||||
(with-tempfile [t (tempfile)]
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))
|
||||||
(let [conn (<? (d/<connect t))]
|
{tx1 :tx txInstant1 :txInstant} (<? (d/<transact! conn [[:db/add 1 :name "Ivan"]]))
|
||||||
(try
|
{tx2 :tx txInstant2 :txInstant} (<? (d/<transact! conn [[:db/add 1 :name "Petr"]]))
|
||||||
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))
|
{tx3 :tx txInstant3 :txInstant} (<? (d/<transact! conn [[:db/add 1 :aka "Tupen"]]))
|
||||||
{tx1 :tx txInstant1 :txInstant} (<? (d/<transact! conn [[:db/add 1 :name "Ivan"]]))
|
{tx4 :tx txInstant4 :txInstant} (<? (d/<transact! conn [[:db/add 1 :aka "Devil"]]))]
|
||||||
{tx2 :tx txInstant2 :txInstant} (<? (d/<transact! conn [[:db/add 1 :name "Petr"]]))
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
{tx3 :tx txInstant3 :txInstant} (<? (d/<transact! conn [[:db/add 1 :aka "Tupen"]]))
|
#{[1 :name "Petr"]
|
||||||
{tx4 :tx txInstant4 :txInstant} (<? (d/<transact! conn [[:db/add 1 :aka "Devil"]]))]
|
[1 :aka "Tupen"]
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
[1 :aka "Devil"]}))
|
||||||
#{[1 :name "Petr"]
|
|
||||||
[1 :aka "Tupen"]
|
|
||||||
[1 :aka "Devil"]}))
|
|
||||||
|
|
||||||
(is (= (<? (<transactions-after (d/db conn) tx0))
|
(is (= (<? (<transactions-after (d/db conn) tx0))
|
||||||
[[1 :name "Ivan" tx1 1] ;; TODO: true, not 1.
|
[[1 :name "Ivan" tx1 1] ;; TODO: true, not 1.
|
||||||
[tx1 :db/txInstant txInstant1 tx1 1]
|
[tx1 :db/txInstant txInstant1 tx1 1]
|
||||||
[1 :name "Ivan" tx2 0]
|
[1 :name "Ivan" tx2 0]
|
||||||
[1 :name "Petr" tx2 1]
|
[1 :name "Petr" tx2 1]
|
||||||
[tx2 :db/txInstant txInstant2 tx2 1]
|
[tx2 :db/txInstant txInstant2 tx2 1]
|
||||||
[1 :aka "Tupen" tx3 1]
|
[1 :aka "Tupen" tx3 1]
|
||||||
[tx3 :db/txInstant txInstant3 tx3 1]
|
[tx3 :db/txInstant txInstant3 tx3 1]
|
||||||
[1 :aka "Devil" tx4 1]
|
[1 :aka "Devil" tx4 1]
|
||||||
[tx4 :db/txInstant txInstant4 tx4 1]])))
|
[tx4 :db/txInstant txInstant4 tx4 1]]))))
|
||||||
|
|
||||||
(finally
|
(deftest-db test-retract conn
|
||||||
(<? (d/<close conn)))))))
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))
|
||||||
|
{tx1 :tx txInstant1 :txInstant} (<? (d/<transact! conn [[:db/add 0 :x 123]]))
|
||||||
|
{tx2 :tx txInstant2 :txInstant} (<? (d/<transact! conn [[:db/retract 0 :x 123]]))]
|
||||||
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{}))
|
||||||
|
(is (= (<? (<transactions-after (d/db conn) tx0))
|
||||||
|
[[0 :x 123 tx1 1]
|
||||||
|
[tx1 :db/txInstant txInstant1 tx1 1]
|
||||||
|
[0 :x 123 tx2 0]
|
||||||
|
[tx2 :db/txInstant txInstant2 tx2 1]]))))
|
||||||
|
|
||||||
(deftest-async test-retract
|
(deftest-db test-id-literal-1 conn
|
||||||
(with-tempfile [t (tempfile)]
|
(let [tx0 (:tx (<? (d/<transact! conn test-schema)))
|
||||||
(let [conn (<? (d/<connect t))]
|
report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :x 0]
|
||||||
(try
|
[:db/add (d/id-literal :db.part/user -1) :y 1]
|
||||||
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))
|
[:db/add (d/id-literal :db.part/user -2) :y 2]
|
||||||
{tx1 :tx txInstant1 :txInstant} (<? (d/<transact! conn [[:db/add 0 :x 123]]))
|
[:db/add (d/id-literal :db.part/user -2) :y 3]]))]
|
||||||
{tx2 :tx txInstant2 :txInstant} (<? (d/<transact! conn [[:db/retract 0 :x 123]]))]
|
(is (= (keys (:tempids report)) ;; TODO: include values.
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
[(d/id-literal :db.part/user -1)
|
||||||
#{}))
|
(d/id-literal :db.part/user -2)]))
|
||||||
(is (= (<? (<transactions-after (d/db conn) tx0))
|
|
||||||
[[0 :x 123 tx1 1]
|
|
||||||
[tx1 :db/txInstant txInstant1 tx1 1]
|
|
||||||
[0 :x 123 tx2 0]
|
|
||||||
[tx2 :db/txInstant txInstant2 tx2 1]])))
|
|
||||||
(finally
|
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
||||||
(deftest-async test-id-literal-1
|
(let [eid1 (get-in report [:tempids (d/id-literal :db.part/user -1)])
|
||||||
(with-tempfile [t (tempfile)]
|
eid2 (get-in report [:tempids (d/id-literal :db.part/user -2)])]
|
||||||
(let [conn (<? (d/<connect t))]
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
(try
|
#{[eid1 :x 0]
|
||||||
(let [tx0 (:tx (<? (d/<transact! conn test-schema)))
|
[eid1 :y 1]
|
||||||
report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :x 0]
|
[eid2 :y 2]
|
||||||
[:db/add (d/id-literal :db.part/user -1) :y 1]
|
[eid2 :y 3]})))))
|
||||||
[:db/add (d/id-literal :db.part/user -2) :y 2]
|
|
||||||
[:db/add (d/id-literal :db.part/user -2) :y 3]]))]
|
|
||||||
(is (= (keys (:tempids report)) ;; TODO: include values.
|
|
||||||
[(d/id-literal :db.part/user -1)
|
|
||||||
(d/id-literal :db.part/user -2)]))
|
|
||||||
|
|
||||||
(let [eid1 (get-in report [:tempids (d/id-literal :db.part/user -1)])
|
(deftest-db test-unique conn
|
||||||
eid2 (get-in report [:tempids (d/id-literal :db.part/user -2)])]
|
(let [tx0 (:tx (<? (d/<transact! conn test-schema)))]
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
(testing "Multiple :db/unique values in tx-data violate unique constraint, no tempid"
|
||||||
#{[eid1 :x 0]
|
(is (thrown-with-msg?
|
||||||
[eid1 :y 1]
|
ExceptionInfo #"unique constraint"
|
||||||
[eid2 :y 2]
|
(<? (d/<transact! conn [[:db/add 1 :x 0]
|
||||||
[eid2 :y 3]}))))
|
[:db/add 2 :x 0]])))))
|
||||||
|
|
||||||
(finally
|
(testing "Multiple :db/unique values in tx-data violate unique constraint, tempid"
|
||||||
(<? (d/<close conn)))))))
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo #"unique constraint"
|
||||||
|
(<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :spouse "Dana"]
|
||||||
|
[:db/add (d/id-literal :db.part/user -2) :spouse "Dana"]])))))))
|
||||||
|
|
||||||
(deftest-async test-unique
|
(deftest-db test-valueType-keyword conn
|
||||||
(with-tempfile [t (tempfile)]
|
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1)
|
||||||
(let [conn (<? (d/<connect t))]
|
:db/ident :test/kw
|
||||||
(try
|
:db/unique :db.unique/identity
|
||||||
(let [tx0 (:tx (<? (d/<transact! conn test-schema)))]
|
:db/valueType :db.type/keyword}
|
||||||
(testing "Multiple :db/unique values in tx-data violate unique constraint, no tempid"
|
{:db/id :db.part/db :db.install/attribute (d/id-literal :db.part/user -1)}])))]
|
||||||
(is (thrown-with-msg?
|
|
||||||
ExceptionInfo #"unique constraint"
|
|
||||||
(<? (d/<transact! conn [[:db/add 1 :x 0]
|
|
||||||
[:db/add 2 :x 0]])))))
|
|
||||||
|
|
||||||
(testing "Multiple :db/unique values in tx-data violate unique constraint, tempid"
|
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :test/kw :test/kw1]]))
|
||||||
(is (thrown-with-msg?
|
eid (get-in report [:tempids (d/id-literal :db.part/user -1)])]
|
||||||
ExceptionInfo #"unique constraint"
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
(<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :spouse "Dana"]
|
#{[eid :test/kw ":test/kw1"]})) ;; Value is raw.
|
||||||
[:db/add (d/id-literal :db.part/user -2) :spouse "Dana"]]))))))
|
|
||||||
|
|
||||||
(finally
|
(testing "Adding the same value compares existing values correctly."
|
||||||
(<? (d/<close conn)))))))
|
(<? (d/<transact! conn [[:db/add eid :test/kw :test/kw1]]))
|
||||||
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{[eid :test/kw ":test/kw1"]}))) ;; Value is raw.
|
||||||
|
|
||||||
(deftest-async test-valueType-keyword
|
(testing "Upserting retracts existing value correctly."
|
||||||
(with-tempfile [t (tempfile)]
|
(<? (d/<transact! conn [[:db/add eid :test/kw :test/kw2]]))
|
||||||
(let [conn (<? (d/<connect t))]
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
(try
|
#{[eid :test/kw ":test/kw2"]}))) ;; Value is raw.
|
||||||
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1)
|
|
||||||
:db/ident :test/kw
|
|
||||||
:db/unique :db.unique/identity
|
|
||||||
:db/valueType :db.type/keyword}
|
|
||||||
{:db/id :db.part/db :db.install/attribute (d/id-literal :db.part/user -1)}])))]
|
|
||||||
|
|
||||||
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :test/kw :test/kw1]]))
|
(testing "Retracting compares values correctly."
|
||||||
eid (get-in report [:tempids (d/id-literal :db.part/user -1)])]
|
(<? (d/<transact! conn [[:db/retract eid :test/kw :test/kw2]]))
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
#{[eid :test/kw ":test/kw1"]})) ;; Value is raw.
|
#{}))))))
|
||||||
|
|
||||||
(testing "Adding the same value compares existing values correctly."
|
(deftest-db test-vector-upsert conn
|
||||||
(<? (d/<transact! conn [[:db/add eid :test/kw :test/kw1]]))
|
;; Not having DB-as-value really hurts us here. This test only works because all upserts
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
;; succeed on top of each other, so we never need to reset the underlying store.
|
||||||
#{[eid :test/kw ":test/kw1"]}))) ;; Value is raw.
|
(<? (d/<transact! conn test-schema))
|
||||||
|
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1"}
|
||||||
|
{:db/id 102 :name "Petr" :email "@2"}])))]
|
||||||
|
|
||||||
(testing "Upserting retracts existing value correctly."
|
(testing "upsert with tempid"
|
||||||
(<? (d/<transact! conn [[:db/add eid :test/kw :test/kw2]]))
|
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :name "Ivan"]
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
[:db/add (d/id-literal :db.part/user -1) :age 12]]))]
|
||||||
#{[eid :test/kw ":test/kw2"]}))) ;; Value is raw.
|
(is (= (<? (<shallow-entity (d/db conn) 101))
|
||||||
|
{:name "Ivan" :age 12 :email "@1"}))
|
||||||
|
(is (= (tempids report)
|
||||||
|
{-1 101}))))
|
||||||
|
|
||||||
(testing "Retracting compares values correctly."
|
(testing "upsert with tempid, order does not matter"
|
||||||
(<? (d/<transact! conn [[:db/retract eid :test/kw :test/kw2]]))
|
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :age 13]
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
[:db/add (d/id-literal :db.part/user -1) :name "Petr"]]))]
|
||||||
#{})))))
|
(is (= (<? (<shallow-entity (d/db conn) 102))
|
||||||
|
{:name "Petr" :age 13 :email "@2"}))
|
||||||
|
(is (= (tempids report)
|
||||||
|
{-1 102}))))
|
||||||
|
|
||||||
(finally
|
(testing "Conflicting upserts fail"
|
||||||
(<? (d/<close conn)))))))
|
(is (thrown-with-msg? Throwable #"Conflicting upsert"
|
||||||
|
(<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :name "Ivan"]
|
||||||
|
[:db/add (d/id-literal :db.part/user -1) :age 35]
|
||||||
|
[:db/add (d/id-literal :db.part/user -1) :name "Petr"]
|
||||||
|
[:db/add (d/id-literal :db.part/user -1) :age 36]])))))))
|
||||||
|
|
||||||
(deftest-async test-vector-upsert
|
(deftest-db test-map-upsert conn
|
||||||
(with-tempfile [t (tempfile)]
|
;; Not having DB-as-value really hurts us here. This test only works because all upserts
|
||||||
(let [conn (<? (d/<connect t))]
|
;; succeed on top of each other, so we never need to reset the underlying store.
|
||||||
(try
|
(<? (d/<transact! conn test-schema))
|
||||||
;; Not having DB-as-value really hurts us here. This test only works because all upserts
|
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1"}
|
||||||
;; succeed on top of each other, so we never need to reset the underlying store.
|
{:db/id 102 :name "Petr" :email "@2"}])))]
|
||||||
(<? (d/<transact! conn test-schema))
|
|
||||||
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1"}
|
|
||||||
{:db/id 102 :name "Petr" :email "@2"}])))]
|
|
||||||
|
|
||||||
(testing "upsert with tempid"
|
(testing "upsert with tempid"
|
||||||
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :name "Ivan"]
|
(let [tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 35}]))]
|
||||||
[:db/add (d/id-literal :db.part/user -1) :age 12]]))]
|
(is (= (<? (<shallow-entity (d/db conn) 101))
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 101))
|
{:name "Ivan" :email "@1" :age 35}))
|
||||||
{:name "Ivan" :age 12 :email "@1"}))
|
(is (= (tempids tx)
|
||||||
(is (= (tempids report)
|
{-1 101}))))
|
||||||
{-1 101}))))
|
|
||||||
|
|
||||||
(testing "upsert with tempid, order does not matter"
|
(testing "upsert by 2 attrs with tempid"
|
||||||
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :age 13]
|
(let [tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :email "@1" :age 35}]))]
|
||||||
[:db/add (d/id-literal :db.part/user -1) :name "Petr"]]))]
|
(is (= (<? (<shallow-entity (d/db conn) 101))
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 102))
|
{:name "Ivan" :email "@1" :age 35}))
|
||||||
{:name "Petr" :age 13 :email "@2"}))
|
(is (= (tempids tx)
|
||||||
(is (= (tempids report)
|
{-1 101}))))
|
||||||
{-1 102}))))
|
|
||||||
|
|
||||||
(testing "Conflicting upserts fail"
|
(testing "upsert with existing id"
|
||||||
(is (thrown-with-msg? Throwable #"Conflicting upsert"
|
(let [tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :age 36}]))]
|
||||||
(<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :name "Ivan"]
|
(is (= (<? (<shallow-entity (d/db conn) 101))
|
||||||
[:db/add (d/id-literal :db.part/user -1) :age 35]
|
{:name "Ivan" :email "@1" :age 36}))
|
||||||
[:db/add (d/id-literal :db.part/user -1) :name "Petr"]
|
(is (= (tempids tx)
|
||||||
[:db/add (d/id-literal :db.part/user -1) :age 36]]))))))
|
{}))))
|
||||||
(finally
|
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
||||||
(deftest-async test-map-upsert
|
(testing "upsert by 2 attrs with existing id"
|
||||||
(with-tempfile [t (tempfile)]
|
(let [tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1" :age 37}]))]
|
||||||
(let [conn (<? (d/<connect t))]
|
(is (= (<? (<shallow-entity (d/db conn) 101))
|
||||||
(try
|
{:name "Ivan" :email "@1" :age 37}))
|
||||||
;; Not having DB-as-value really hurts us here. This test only works because all upserts
|
(is (= (tempids tx)
|
||||||
;; succeed on top of each other, so we never need to reset the underlying store.
|
{}))))
|
||||||
(<? (d/<transact! conn test-schema))
|
|
||||||
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1"}
|
|
||||||
{:db/id 102 :name "Petr" :email "@2"}])))]
|
|
||||||
|
|
||||||
(testing "upsert with tempid"
|
(testing "upsert to two entities, resolve to same tempid, fails due to overlapping writes"
|
||||||
(let [tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 35}]))]
|
(is (thrown-with-msg? Throwable #"cardinality constraint"
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 101))
|
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 35}
|
||||||
{:name "Ivan" :email "@1" :age 35}))
|
{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 36}])))))
|
||||||
(is (= (tempids tx)
|
|
||||||
{-1 101}))))
|
|
||||||
|
|
||||||
(testing "upsert by 2 attrs with tempid"
|
(testing "upsert to two entities, two tempids, fails due to overlapping writes"
|
||||||
(let [tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :email "@1" :age 35}]))]
|
(is (thrown-with-msg? Throwable #"cardinality constraint"
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 101))
|
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 35}
|
||||||
{:name "Ivan" :email "@1" :age 35}))
|
{:db/id (d/id-literal :db.part/user -2) :name "Ivan" :age 36}])))))))
|
||||||
(is (= (tempids tx)
|
|
||||||
{-1 101}))))
|
|
||||||
|
|
||||||
(testing "upsert with existing id"
|
(deftest-db test-map-upsert-conflicts conn
|
||||||
(let [tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :age 36}]))]
|
;; Not having DB-as-value really hurts us here. This test only works because all upserts
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 101))
|
;; fail until the final one, so we never need to reset the underlying store.
|
||||||
{:name "Ivan" :email "@1" :age 36}))
|
(<? (d/<transact! conn test-schema))
|
||||||
(is (= (tempids tx)
|
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1"}
|
||||||
{}))))
|
{:db/id 102 :name "Petr" :email "@2"}])))]
|
||||||
|
|
||||||
(testing "upsert by 2 attrs with existing id"
|
;; TODO: improve error message to refer to upsert inputs.
|
||||||
(let [tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1" :age 37}]))]
|
(testing "upsert conficts with existing id"
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 101))
|
(is (thrown-with-msg? Throwable #"unique constraint"
|
||||||
{:name "Ivan" :email "@1" :age 37}))
|
(<? (d/<transact! conn [{:db/id 102 :name "Ivan" :age 36}])))))
|
||||||
(is (= (tempids tx)
|
|
||||||
{}))))
|
|
||||||
|
|
||||||
(testing "upsert to two entities, resolve to same tempid, fails due to overlapping writes"
|
;; TODO: improve error message to refer to upsert inputs.
|
||||||
(is (thrown-with-msg? Throwable #"cardinality constraint"
|
(testing "upsert conficts with non-existing id"
|
||||||
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 35}
|
(is (thrown-with-msg? Throwable #"unique constraint"
|
||||||
{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 36}])))))
|
(<? (d/<transact! conn [{:db/id 103 :name "Ivan" :age 36}])))))
|
||||||
|
|
||||||
(testing "upsert to two entities, two tempids, fails due to overlapping writes"
|
;; TODO: improve error message to refer to upsert inputs.
|
||||||
(is (thrown-with-msg? Throwable #"cardinality constraint"
|
(testing "upsert by 2 conflicting fields"
|
||||||
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :age 35}
|
(is (thrown-with-msg? Throwable #"Conflicting upsert"
|
||||||
{:db/id (d/id-literal :db.part/user -2) :name "Ivan" :age 36}]))))))
|
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :email "@2" :age 35}])))))
|
||||||
|
|
||||||
(finally
|
(testing "upsert by non-existing value resolves as update"
|
||||||
(<? (d/<close conn)))))))
|
(let [report (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :email "@3" :age 35}]))]
|
||||||
|
(is (= (<? (<shallow-entity (d/db conn) 101))
|
||||||
|
{:name "Ivan" :email "@3" :age 35}))
|
||||||
|
(is (= (tempids report)
|
||||||
|
{-1 101}))))))
|
||||||
|
|
||||||
(deftest-async test-map-upsert-conflicts
|
(deftest-db test-add-ident conn
|
||||||
(with-tempfile [t (tempfile)]
|
(is (= :test/ident (d/entid (d/db conn) :test/ident)))
|
||||||
(let [conn (<? (d/<connect t))]
|
|
||||||
(try
|
|
||||||
;; Not having DB-as-value really hurts us here. This test only works because all upserts
|
|
||||||
;; fail until the final one, so we never need to reset the underlying store.
|
|
||||||
(<? (d/<transact! conn test-schema))
|
|
||||||
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id 101 :name "Ivan" :email "@1"}
|
|
||||||
{:db/id 102 :name "Petr" :email "@2"}])))]
|
|
||||||
|
|
||||||
;; TODO: improve error message to refer to upsert inputs.
|
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/db -1) :db/ident :test/ident]]))
|
||||||
(testing "upsert conficts with existing id"
|
eid (get-in report [:tempids (d/id-literal :db.part/db -1)])]
|
||||||
(is (thrown-with-msg? Throwable #"unique constraint"
|
(is (= eid (d/entid (d/db conn) :test/ident)))
|
||||||
(<? (d/<transact! conn [{:db/id 102 :name "Ivan" :age 36}])))))
|
(is (= :test/ident (d/ident (d/db conn) eid))))
|
||||||
|
|
||||||
;; TODO: improve error message to refer to upsert inputs.
|
;; TODO: This should fail, but doesn't, due to stringification of :test/ident.
|
||||||
(testing "upsert conficts with non-existing id"
|
;; (is (thrown-with-msg?
|
||||||
(is (thrown-with-msg? Throwable #"unique constraint"
|
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got "
|
||||||
(<? (d/<transact! conn [{:db/id 103 :name "Ivan" :age 36}])))))
|
;; (<? (d/<transact! conn [[:db/retract 44 :db/ident :test/ident]]))))
|
||||||
|
|
||||||
;; TODO: improve error message to refer to upsert inputs.
|
;; ;; Renaming looks like retraction and then assertion.
|
||||||
(testing "upsert by 2 conflicting fields"
|
;; (is (thrown-with-msg?
|
||||||
(is (thrown-with-msg? Throwable #"Conflicting upsert"
|
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got"
|
||||||
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :email "@2" :age 35}])))))
|
;; (<? (d/<transact! conn [[:db/add 44 :db/ident :other-name]]))))
|
||||||
|
|
||||||
(testing "upsert by non-existing value resolves as update"
|
;; (is (thrown-with-msg?
|
||||||
(let [report (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :name "Ivan" :email "@3" :age 35}]))]
|
;; ExceptionInfo #"Re-asserting a :db/ident is not yet supported, got"
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 101))
|
;; (<? (d/<transact! conn [[:db/add 55 :db/ident :test/ident]]))))
|
||||||
{:name "Ivan" :email "@3" :age 35}))
|
)
|
||||||
(is (= (tempids report)
|
|
||||||
{-1 101})))))
|
|
||||||
|
|
||||||
(finally
|
(deftest-db test-add-schema conn
|
||||||
(<? (d/<close conn)))))))
|
(let [es [[:db/add :db.part/db :db.install/attribute (d/id-literal :db.part/db -1)]
|
||||||
|
{:db/id (d/id-literal :db.part/db -1)
|
||||||
(deftest-async test-add-ident
|
:db/ident :test/attr
|
||||||
(with-tempfile [t (tempfile)]
|
|
||||||
(let [conn (<? (d/<connect t))]
|
|
||||||
(try
|
|
||||||
(is (= :test/ident (d/entid (d/db conn) :test/ident)))
|
|
||||||
|
|
||||||
(let [report (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/db -1) :db/ident :test/ident]]))
|
|
||||||
eid (get-in report [:tempids (d/id-literal :db.part/db -1)])]
|
|
||||||
(is (= eid (d/entid (d/db conn) :test/ident)))
|
|
||||||
(is (= :test/ident (d/ident (d/db conn) eid))))
|
|
||||||
|
|
||||||
;; TODO: This should fail, but doesn't, due to stringification of :test/ident.
|
|
||||||
;; (is (thrown-with-msg?
|
|
||||||
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got "
|
|
||||||
;; (<? (d/<transact! conn [[:db/retract 44 :db/ident :test/ident]]))))
|
|
||||||
|
|
||||||
;; ;; Renaming looks like retraction and then assertion.
|
|
||||||
;; (is (thrown-with-msg?
|
|
||||||
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got"
|
|
||||||
;; (<? (d/<transact! conn [[:db/add 44 :db/ident :other-name]]))))
|
|
||||||
|
|
||||||
;; (is (thrown-with-msg?
|
|
||||||
;; ExceptionInfo #"Re-asserting a :db/ident is not yet supported, got"
|
|
||||||
;; (<? (d/<transact! conn [[:db/add 55 :db/ident :test/ident]]))))
|
|
||||||
|
|
||||||
(finally
|
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
||||||
(deftest-async test-add-schema
|
|
||||||
(with-tempfile [t (tempfile)]
|
|
||||||
(let [conn (<? (d/<connect t))]
|
|
||||||
(try
|
|
||||||
(let [es [[:db/add :db.part/db :db.install/attribute (d/id-literal :db.part/db -1)]
|
|
||||||
{:db/id (d/id-literal :db.part/db -1)
|
|
||||||
:db/ident :test/attr
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one}]
|
|
||||||
report (<? (d/<transact! conn es))
|
|
||||||
db-after (:db-after report)
|
|
||||||
tx (:tx db-after)]
|
|
||||||
|
|
||||||
(testing "New ident is allocated"
|
|
||||||
(is (some? (d/entid db-after :test/attr))))
|
|
||||||
|
|
||||||
(testing "Schema is modified"
|
|
||||||
(is (= (get-in db-after [:symbolic-schema :test/attr])
|
|
||||||
{:db/valueType :db.type/string,
|
|
||||||
:db/cardinality :db.cardinality/one})))
|
|
||||||
|
|
||||||
(testing "Schema is used in subsequent transaction"
|
|
||||||
(<? (d/<transact! conn [{:db/id 100 :test/attr "value 1"}]))
|
|
||||||
(<? (d/<transact! conn [{:db/id 100 :test/attr "value 2"}]))
|
|
||||||
(is (= (<? (<shallow-entity (d/db conn) 100))
|
|
||||||
{:test/attr "value 2"}))))
|
|
||||||
|
|
||||||
(finally
|
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
||||||
(deftest-async test-fulltext
|
|
||||||
(with-tempfile [t (tempfile)]
|
|
||||||
(let [conn (<? (d/<connect t))
|
|
||||||
schema [{:db/id (d/id-literal :db.part/db -1)
|
|
||||||
:db/ident :test/fulltext
|
|
||||||
:db/valueType :db.type/string
|
:db/valueType :db.type/string
|
||||||
:db/fulltext true
|
:db/cardinality :db.cardinality/one}]
|
||||||
:db/unique :db.unique/identity}
|
report (<? (d/<transact! conn es))
|
||||||
{:db/id :db.part/db :db.install/attribute (d/id-literal :db.part/db -1)}
|
db-after (:db-after report)
|
||||||
{:db/id (d/id-literal :db.part/db -2)
|
tx (:tx db-after)]
|
||||||
:db/ident :test/other
|
|
||||||
:db/valueType :db.type/string
|
(testing "New ident is allocated"
|
||||||
:db/fulltext true
|
(is (some? (d/entid db-after :test/attr))))
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/id :db.part/db :db.install/attribute (d/id-literal :db.part/db -2)}
|
(testing "Schema is modified"
|
||||||
]
|
(is (= (get-in db-after [:symbolic-schema :test/attr])
|
||||||
tx0 (:tx (<? (d/<transact! conn schema)))]
|
{:db/valueType :db.type/string,
|
||||||
(testing "Schema checks"
|
:db/cardinality :db.cardinality/one})))
|
||||||
(is (ds/fulltext? (d/schema (d/db conn))
|
|
||||||
(d/entid (d/db conn) :test/fulltext))))
|
(testing "Schema is used in subsequent transaction"
|
||||||
(try
|
(<? (d/<transact! conn [{:db/id 100 :test/attr "value 1"}]))
|
||||||
(testing "Can add fulltext indexed datoms"
|
(<? (d/<transact! conn [{:db/id 100 :test/attr "value 2"}]))
|
||||||
(let [{tx1 :tx txInstant1 :txInstant}
|
(is (= (<? (<shallow-entity (d/db conn) 100))
|
||||||
(<? (d/<transact! conn [[:db/add 101 :test/fulltext "test this"]]))]
|
{:test/attr "value 2"})))))
|
||||||
|
|
||||||
|
(deftest-db test-fulltext conn
|
||||||
|
(let [schema [{:db/id (d/id-literal :db.part/db -1)
|
||||||
|
:db/ident :test/fulltext
|
||||||
|
:db/valueType :db.type/string
|
||||||
|
:db/fulltext true
|
||||||
|
:db/unique :db.unique/identity}
|
||||||
|
{:db/id :db.part/db :db.install/attribute (d/id-literal :db.part/db -1)}
|
||||||
|
{:db/id (d/id-literal :db.part/db -2)
|
||||||
|
:db/ident :test/other
|
||||||
|
:db/valueType :db.type/string
|
||||||
|
:db/fulltext true
|
||||||
|
:db/cardinality :db.cardinality/one}
|
||||||
|
{:db/id :db.part/db :db.install/attribute (d/id-literal :db.part/db -2)}
|
||||||
|
]
|
||||||
|
tx0 (:tx (<? (d/<transact! conn schema)))]
|
||||||
|
(testing "Schema checks"
|
||||||
|
(is (ds/fulltext? (d/schema (d/db conn))
|
||||||
|
(d/entid (d/db conn) :test/fulltext))))
|
||||||
|
|
||||||
|
(testing "Can add fulltext indexed datoms"
|
||||||
|
(let [{tx1 :tx txInstant1 :txInstant}
|
||||||
|
(<? (d/<transact! conn [[:db/add 101 :test/fulltext "test this"]]))]
|
||||||
|
(is (= (<? (<fulltext-values (d/db conn)))
|
||||||
|
[[1 "test this"]]))
|
||||||
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{[101 :test/fulltext 1]})) ;; Values are raw; 1 is the rowid into fulltext_values.
|
||||||
|
(is (= (<? (<transactions-after (d/db conn) tx0))
|
||||||
|
[[101 :test/fulltext 1 tx1 1] ;; Values are raw; 1 is the rowid into fulltext_values.
|
||||||
|
[tx1 :db/txInstant txInstant1 tx1 1]]))
|
||||||
|
|
||||||
|
(testing "Can replace fulltext indexed datoms"
|
||||||
|
(let [{tx2 :tx txInstant2 :txInstant} (<? (d/<transact! conn [[:db/add 101 :test/fulltext "alternate thing"]]))]
|
||||||
(is (= (<? (<fulltext-values (d/db conn)))
|
(is (= (<? (<fulltext-values (d/db conn)))
|
||||||
[[1 "test this"]]))
|
[[1 "test this"]
|
||||||
|
[2 "alternate thing"]]))
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
#{[101 :test/fulltext 1]})) ;; Values are raw; 1 is the rowid into fulltext_values.
|
#{[101 :test/fulltext 2]})) ;; Values are raw; 2 is the rowid into fulltext_values.
|
||||||
(is (= (<? (<transactions-after (d/db conn) tx0))
|
(is (= (<? (<transactions-after (d/db conn) tx0))
|
||||||
[[101 :test/fulltext 1 tx1 1] ;; Values are raw; 1 is the rowid into fulltext_values.
|
[[101 :test/fulltext 1 tx1 1] ;; Values are raw; 1 is the rowid into fulltext_values.
|
||||||
[tx1 :db/txInstant txInstant1 tx1 1]]))
|
[tx1 :db/txInstant txInstant1 tx1 1]
|
||||||
|
[101 :test/fulltext 1 tx2 0] ;; Values are raw; 1 is the rowid into fulltext_values.
|
||||||
|
[101 :test/fulltext 2 tx2 1] ;; Values are raw; 2 is the rowid into fulltext_values.
|
||||||
|
[tx2 :db/txInstant txInstant2 tx2 1]]))
|
||||||
|
|
||||||
(testing "Can replace fulltext indexed datoms"
|
(testing "Can upsert keyed by fulltext indexed datoms"
|
||||||
(let [{tx2 :tx txInstant2 :txInstant} (<? (d/<transact! conn [[:db/add 101 :test/fulltext "alternate thing"]]))]
|
(let [{tx3 :tx txInstant3 :txInstant} (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user) :test/fulltext "alternate thing" :test/other "other"}]))]
|
||||||
(is (= (<? (<fulltext-values (d/db conn)))
|
(is (= (<? (<fulltext-values (d/db conn)))
|
||||||
[[1 "test this"]
|
[[1 "test this"]
|
||||||
[2 "alternate thing"]]))
|
[2 "alternate thing"]
|
||||||
|
[3 "other"]]))
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
#{[101 :test/fulltext 2]})) ;; Values are raw; 2 is the rowid into fulltext_values.
|
#{[101 :test/fulltext 2] ;; Values are raw; 2, 3 are the rowids into fulltext_values.
|
||||||
|
[101 :test/other 3]}))
|
||||||
(is (= (<? (<transactions-after (d/db conn) tx0))
|
(is (= (<? (<transactions-after (d/db conn) tx0))
|
||||||
[[101 :test/fulltext 1 tx1 1] ;; Values are raw; 1 is the rowid into fulltext_values.
|
[[101 :test/fulltext 1 tx1 1] ;; Values are raw; 1 is the rowid into fulltext_values.
|
||||||
[tx1 :db/txInstant txInstant1 tx1 1]
|
[tx1 :db/txInstant txInstant1 tx1 1]
|
||||||
[101 :test/fulltext 1 tx2 0] ;; Values are raw; 1 is the rowid into fulltext_values.
|
[101 :test/fulltext 1 tx2 0] ;; Values are raw; 1 is the rowid into fulltext_values.
|
||||||
[101 :test/fulltext 2 tx2 1] ;; Values are raw; 2 is the rowid into fulltext_values.
|
[101 :test/fulltext 2 tx2 1] ;; Values are raw; 2 is the rowid into fulltext_values.
|
||||||
[tx2 :db/txInstant txInstant2 tx2 1]]))
|
[tx2 :db/txInstant txInstant2 tx2 1]
|
||||||
|
[101 :test/other 3 tx3 1] ;; Values are raw; 3 is the rowid into fulltext_values.
|
||||||
|
[tx3 :db/txInstant txInstant3 tx3 1]]))
|
||||||
|
|
||||||
(testing "Can upsert keyed by fulltext indexed datoms"
|
))))))
|
||||||
(let [{tx3 :tx txInstant3 :txInstant} (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user) :test/fulltext "alternate thing" :test/other "other"}]))]
|
|
||||||
(is (= (<? (<fulltext-values (d/db conn)))
|
|
||||||
[[1 "test this"]
|
|
||||||
[2 "alternate thing"]
|
|
||||||
[3 "other"]]))
|
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
|
||||||
#{[101 :test/fulltext 2] ;; Values are raw; 2, 3 are the rowids into fulltext_values.
|
|
||||||
[101 :test/other 3]}))
|
|
||||||
(is (= (<? (<transactions-after (d/db conn) tx0))
|
|
||||||
[[101 :test/fulltext 1 tx1 1] ;; Values are raw; 1 is the rowid into fulltext_values.
|
|
||||||
[tx1 :db/txInstant txInstant1 tx1 1]
|
|
||||||
[101 :test/fulltext 1 tx2 0] ;; Values are raw; 1 is the rowid into fulltext_values.
|
|
||||||
[101 :test/fulltext 2 tx2 1] ;; Values are raw; 2 is the rowid into fulltext_values.
|
|
||||||
[tx2 :db/txInstant txInstant2 tx2 1]
|
|
||||||
[101 :test/other 3 tx3 1] ;; Values are raw; 3 is the rowid into fulltext_values.
|
|
||||||
[tx3 :db/txInstant txInstant3 tx3 1]]))
|
|
||||||
|
|
||||||
))))))
|
(testing "Can re-use fulltext indexed datoms"
|
||||||
|
(let [r (<? (d/<transact! conn [[:db/add 102 :test/other "test this"]]))]
|
||||||
|
(is (= (<? (<fulltext-values (d/db conn)))
|
||||||
|
[[1 "test this"]
|
||||||
|
[2 "alternate thing"]
|
||||||
|
[3 "other"]]))
|
||||||
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{[101 :test/fulltext 2]
|
||||||
|
[101 :test/other 3]
|
||||||
|
[102 :test/other 1]})) ;; Values are raw; 1, 2, 3 are the rowids into fulltext_values.
|
||||||
|
))
|
||||||
|
|
||||||
(testing "Can re-use fulltext indexed datoms"
|
(testing "Can retract fulltext indexed datoms"
|
||||||
(let [r (<? (d/<transact! conn [[:db/add 102 :test/other "test this"]]))]
|
(let [r (<? (d/<transact! conn [[:db/retract 101 :test/fulltext "alternate thing"]]))]
|
||||||
(is (= (<? (<fulltext-values (d/db conn)))
|
(is (= (<? (<fulltext-values (d/db conn)))
|
||||||
[[1 "test this"]
|
[[1 "test this"]
|
||||||
[2 "alternate thing"]
|
[2 "alternate thing"]
|
||||||
[3 "other"]]))
|
[3 "other"]]))
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
#{[101 :test/fulltext 2]
|
#{[101 :test/other 3]
|
||||||
[101 :test/other 3]
|
[102 :test/other 1]})) ;; Values are raw; 1, 3 are the rowids into fulltext_values.
|
||||||
[102 :test/other 1]})) ;; Values are raw; 1, 2, 3 are the rowids into fulltext_values.
|
))))
|
||||||
))
|
|
||||||
|
|
||||||
(testing "Can retract fulltext indexed datoms"
|
(deftest-db test-txInstant conn
|
||||||
(let [r (<? (d/<transact! conn [[:db/retract 101 :test/fulltext "alternate thing"]]))]
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))
|
||||||
(is (= (<? (<fulltext-values (d/db conn)))
|
{txa :tx txInstantA :txInstant} (<? (d/<transact! conn []))]
|
||||||
[[1 "test this"]
|
(testing ":db/txInstant is set by default"
|
||||||
[2 "alternate thing"]
|
(is (= (<? (<transactions-after (d/db conn) tx0))
|
||||||
[3 "other"]]))
|
[[txa :db/txInstant txInstantA txa 1]])))
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
|
||||||
#{[101 :test/other 3]
|
|
||||||
[102 :test/other 1]})) ;; Values are raw; 1, 3 are the rowids into fulltext_values.
|
|
||||||
))
|
|
||||||
|
|
||||||
(finally
|
;; TODO: range check txInstant values against DB clock.
|
||||||
(<? (d/<close conn)))))))
|
(testing ":db/txInstant can be set explicitly"
|
||||||
|
(let [{txb :tx txInstantB :txInstant} (<? (d/<transact! conn [[:db/add :db/tx :db/txInstant (+ txInstantA 1)]]))]
|
||||||
|
(is (= txInstantB (+ txInstantA 1)))
|
||||||
|
(is (= (<? (<transactions-after (d/db conn) txa))
|
||||||
|
[[txb :db/txInstant txInstantB txb 1]]))
|
||||||
|
|
||||||
(deftest-async test-txInstant
|
(testing ":db/txInstant can be set explicitly, with additional datoms"
|
||||||
(with-tempfile [t (tempfile)]
|
(let [{txc :tx txInstantC :txInstant} (<? (d/<transact! conn [[:db/add :db/tx :db/txInstant (+ txInstantB 2)]
|
||||||
(let [conn (<? (d/<connect t))
|
[:db/add :db/tx :x 123]]))]
|
||||||
{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
(is (= txInstantC (+ txInstantB 2)))
|
||||||
(try
|
(is (= (<? (<transactions-after (d/db conn) txb))
|
||||||
(let [{txa :tx txInstantA :txInstant} (<? (d/<transact! conn []))]
|
[[txc :db/txInstant txInstantC txc 1]
|
||||||
(testing ":db/txInstant is set by default"
|
[txc :x 123 txc 1]]))
|
||||||
(is (= (<? (<transactions-after (d/db conn) tx0))
|
|
||||||
[[txa :db/txInstant txInstantA txa 1]])))
|
|
||||||
|
|
||||||
;; TODO: range check txInstant values against DB clock.
|
(testing "additional datoms can be added, without :db/txInstant explicitly"
|
||||||
(testing ":db/txInstant can be set explicitly"
|
(let [{txd :tx txInstantD :txInstant} (<? (d/<transact! conn [[:db/add :db/tx :x 456]]))]
|
||||||
(let [{txb :tx txInstantB :txInstant} (<? (d/<transact! conn [[:db/add :db/tx :db/txInstant (+ txInstantA 1)]]))]
|
(is (= (<? (<transactions-after (d/db conn) txc))
|
||||||
(is (= txInstantB (+ txInstantA 1)))
|
[[txd :db/txInstant txInstantD txd 1]
|
||||||
(is (= (<? (<transactions-after (d/db conn) txa))
|
[txd :x 456 txd 1]]))))))))))
|
||||||
[[txb :db/txInstant txInstantB txb 1]]))
|
|
||||||
|
|
||||||
(testing ":db/txInstant can be set explicitly, with additional datoms"
|
(deftest-db test-no-tx conn
|
||||||
(let [{txc :tx txInstantC :txInstant} (<? (d/<transact! conn [[:db/add :db/tx :db/txInstant (+ txInstantB 2)]
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
||||||
[:db/add :db/tx :x 123]]))]
|
(testing "Cannot specificy an explicit tx"
|
||||||
(is (= txInstantC (+ txInstantB 2)))
|
(is (thrown-with-msg?
|
||||||
(is (= (<? (<transactions-after (d/db conn) txb))
|
ExceptionInfo #"Bad entity: too long"
|
||||||
[[txc :db/txInstant txInstantC txc 1]
|
(<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user) :x 0 10101]])))))))
|
||||||
[txc :x 123 txc 1]]))
|
|
||||||
|
|
||||||
(testing "additional datoms can be added, without :db/txInstant explicitly"
|
(deftest-db test-explode-sequences conn
|
||||||
(let [{txd :tx txInstantD :txInstant} (<? (d/<transact! conn [[:db/add :db/tx :x 456]]))]
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
||||||
(is (= (<? (<transactions-after (d/db conn) txc))
|
(testing ":db.cardinality/many sequences are accepted"
|
||||||
[[txd :db/txInstant txInstantD txd 1]
|
(<? (d/<transact! conn [{:db/id 101 :aka ["first" "second"]}]))
|
||||||
[txd :x 456 txd 1]])))))))))
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{[101 :aka "first"]
|
||||||
|
[101 :aka "second"]})))
|
||||||
|
|
||||||
(finally
|
(testing ":db.cardinality/many sequences are recursively applied, allowing unexpected sequence nesting"
|
||||||
(<? (d/<close conn)))))))
|
(<? (d/<transact! conn [{:db/id 102 :aka [[["first"]] ["second"]]}]))
|
||||||
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{[101 :aka "first"]
|
||||||
|
[101 :aka "second"]
|
||||||
|
[102 :aka "first"]
|
||||||
|
[102 :aka "second"]})))
|
||||||
|
|
||||||
(deftest-async test-no-tx
|
(testing ":db.cardinality/one sequences fail"
|
||||||
(with-tempfile [t (tempfile)]
|
(is (thrown-with-msg?
|
||||||
(let [conn (<? (d/<connect t))
|
ExceptionInfo #"Sequential values"
|
||||||
{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
(<? (d/<transact! conn [{:db/id 101 :email ["@1" "@2"]}])))))))
|
||||||
(try
|
|
||||||
(testing "Cannot specificy an explicit tx"
|
|
||||||
(is (thrown-with-msg?
|
|
||||||
ExceptionInfo #"Bad entity: too long"
|
|
||||||
(<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user) :x 0 10101]])))))
|
|
||||||
|
|
||||||
(finally
|
(deftest-db test-explode-maps conn
|
||||||
(<? (d/<close conn)))))))
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
||||||
|
(testing "nested maps are accepted"
|
||||||
|
(<? (d/<transact! conn [{:db/id 101 :friends {:name "Petr"}}]))
|
||||||
|
;; TODO: this works only because we have a single friend.
|
||||||
|
(let [{petr :friends} (<? (<shallow-entity (d/db conn) 101))]
|
||||||
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{[101 :friends petr]
|
||||||
|
[petr :name "Petr"]}))))
|
||||||
|
|
||||||
(deftest-async test-explode-sequences
|
(testing "recursively nested maps are accepted"
|
||||||
(with-tempfile [t (tempfile)]
|
(<? (d/<transact! conn [{:db/id 102 :friends {:name "Ivan" :friends {:name "Petr"}}}]))
|
||||||
(let [conn (<? (d/<connect t))
|
;; This would be much easier with `entity` and lookup refs.
|
||||||
{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
(let [{ivan :friends} (<? (<shallow-entity (d/db conn) 102))
|
||||||
(try
|
{petr :friends} (<? (<shallow-entity (d/db conn) ivan))]
|
||||||
(testing ":db.cardinality/many sequences are accepted"
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
(<? (d/<transact! conn [{:db/id 101 :aka ["first" "second"]}]))
|
#{[101 :friends petr]
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
[petr :name "Petr"]
|
||||||
#{[101 :aka "first"]
|
[102 :friends ivan]
|
||||||
[101 :aka "second"]})))
|
[ivan :name "Ivan"]
|
||||||
|
[ivan :friends petr]}))))
|
||||||
|
|
||||||
(testing ":db.cardinality/many sequences are recursively applied, allowing unexpected sequence nesting"
|
(testing "nested maps without :db.type/ref fail"
|
||||||
(<? (d/<transact! conn [{:db/id 102 :aka [[["first"]] ["second"]]}]))
|
(is (thrown-with-msg?
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
ExceptionInfo #"\{:db/valueType :db.type/ref\}"
|
||||||
#{[101 :aka "first"]
|
(<? (d/<transact! conn [{:db/id 101 :aka {:name "Petr"}}])))))))
|
||||||
[101 :aka "second"]
|
|
||||||
[102 :aka "first"]
|
|
||||||
[102 :aka "second"]})))
|
|
||||||
|
|
||||||
(testing ":db.cardinality/one sequences fail"
|
(deftest-db test-explode-reverse-refs conn
|
||||||
(is (thrown-with-msg?
|
(let [{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
||||||
ExceptionInfo #"Sequential values"
|
(testing "reverse refs are accepted"
|
||||||
(<? (d/<transact! conn [{:db/id 101 :email ["@1" "@2"]}])))))
|
(<? (d/<transact! conn [{:db/id 101 :name "Igor"}]))
|
||||||
|
(<? (d/<transact! conn [{:db/id 102 :name "Oleg" :_friends 101}]))
|
||||||
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
|
#{[101 :name "Igor"]
|
||||||
|
[102 :name "Oleg"]
|
||||||
|
[101 :friends 102]})))
|
||||||
|
|
||||||
(finally
|
(testing "reverse refs without :db.type/ref fail"
|
||||||
(<? (d/<close conn)))))))
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo #"\{:db/valueType :db.type/ref\}"
|
||||||
(deftest-async test-explode-maps
|
(<? (d/<transact! conn [{:db/id 101 :_aka 102}])))))))
|
||||||
(with-tempfile [t (tempfile)]
|
|
||||||
(let [conn (<? (d/<connect t))
|
|
||||||
{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
|
||||||
(try
|
|
||||||
(testing "nested maps are accepted"
|
|
||||||
(<? (d/<transact! conn [{:db/id 101 :friends {:name "Petr"}}]))
|
|
||||||
;; TODO: this works only because we have a single friend.
|
|
||||||
(let [{petr :friends} (<? (<shallow-entity (d/db conn) 101))]
|
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
|
||||||
#{[101 :friends petr]
|
|
||||||
[petr :name "Petr"]}))))
|
|
||||||
|
|
||||||
(testing "recursively nested maps are accepted"
|
|
||||||
(<? (d/<transact! conn [{:db/id 102 :friends {:name "Ivan" :friends {:name "Petr"}}}]))
|
|
||||||
;; This would be much easier with `entity` and lookup refs.
|
|
||||||
(let [{ivan :friends} (<? (<shallow-entity (d/db conn) 102))
|
|
||||||
{petr :friends} (<? (<shallow-entity (d/db conn) ivan))]
|
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
|
||||||
#{[101 :friends petr]
|
|
||||||
[petr :name "Petr"]
|
|
||||||
[102 :friends ivan]
|
|
||||||
[ivan :name "Ivan"]
|
|
||||||
[ivan :friends petr]}))))
|
|
||||||
|
|
||||||
(testing "nested maps without :db.type/ref fail"
|
|
||||||
(is (thrown-with-msg?
|
|
||||||
ExceptionInfo #"\{:db/valueType :db.type/ref\}"
|
|
||||||
(<? (d/<transact! conn [{:db/id 101 :aka {:name "Petr"}}])))))
|
|
||||||
|
|
||||||
(finally
|
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
||||||
(deftest-async test-explode-reverse-refs
|
|
||||||
(with-tempfile [t (tempfile)]
|
|
||||||
(let [conn (<? (d/<connect t))
|
|
||||||
{tx0 :tx} (<? (d/<transact! conn test-schema))]
|
|
||||||
(try
|
|
||||||
(testing "reverse refs are accepted"
|
|
||||||
(<? (d/<transact! conn [{:db/id 101 :name "Igor"}]))
|
|
||||||
(<? (d/<transact! conn [{:db/id 102 :name "Oleg" :_friends 101}]))
|
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
|
||||||
#{[101 :name "Igor"]
|
|
||||||
[102 :name "Oleg"]
|
|
||||||
[101 :friends 102]})))
|
|
||||||
|
|
||||||
(testing "reverse refs without :db.type/ref fail"
|
|
||||||
(is (thrown-with-msg?
|
|
||||||
ExceptionInfo #"\{:db/valueType :db.type/ref\}"
|
|
||||||
(<? (d/<transact! conn [{:db/id 101 :_aka 102}])))))
|
|
||||||
|
|
||||||
(finally
|
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
||||||
|
;; We don't use deftest-db in order to be able to re-open an on disk file.
|
||||||
(deftest-async test-next-eid
|
(deftest-async test-next-eid
|
||||||
(with-tempfile [t (tempfile)]
|
(with-tempfile [t (tempfile)]
|
||||||
(let [conn (<? (d/<connect t))
|
(let [conn (<? (d/<connect t))
|
||||||
|
@ -644,34 +548,30 @@
|
||||||
(finally
|
(finally
|
||||||
(<? (d/<close conn))))))))))
|
(<? (d/<close conn))))))))))
|
||||||
|
|
||||||
(deftest-async test-unique-value
|
(deftest-db test-unique-value conn
|
||||||
(with-tempfile [t (tempfile)]
|
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1)
|
||||||
(let [conn (<? (d/<connect t))]
|
:db/ident :test/x
|
||||||
(try
|
:db/unique :db.unique/value
|
||||||
(let [tx0 (:tx (<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1)
|
:db/valueType :db.type/long
|
||||||
:db/ident :test/x
|
:db.install/_attribute :db.part/db}
|
||||||
:db/unique :db.unique/value
|
{:db/id (d/id-literal :db.part/user -2)
|
||||||
:db/valueType :db.type/long
|
:db/ident :test/y
|
||||||
:db.install/_attribute :db.part/db}
|
:db/unique :db.unique/value
|
||||||
{:db/id (d/id-literal :db.part/user -2)
|
:db/valueType :db.type/long
|
||||||
:db/ident :test/y
|
:db.install/_attribute :db.part/db}])))]
|
||||||
:db/unique :db.unique/value
|
|
||||||
:db/valueType :db.type/long
|
|
||||||
:db.install/_attribute :db.part/db}])))]
|
|
||||||
|
|
||||||
(testing "can insert different :db.unique/value attributes with the same value"
|
(testing "can insert different :db.unique/value attributes with the same value"
|
||||||
(let [report1 (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :test/x 12345]]))
|
(let [report1 (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -1) :test/x 12345]]))
|
||||||
eid1 (get-in report1 [:tempids (d/id-literal :db.part/user -1)])
|
eid1 (get-in report1 [:tempids (d/id-literal :db.part/user -1)])
|
||||||
report2 (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -2) :test/y 12345]]))
|
report2 (<? (d/<transact! conn [[:db/add (d/id-literal :db.part/user -2) :test/y 12345]]))
|
||||||
eid2 (get-in report2 [:tempids (d/id-literal :db.part/user -2)])]
|
eid2 (get-in report2 [:tempids (d/id-literal :db.part/user -2)])]
|
||||||
(is (= (<? (<datoms-after (d/db conn) tx0))
|
(is (= (<? (<datoms-after (d/db conn) tx0))
|
||||||
#{[eid1 :test/x 12345]
|
#{[eid1 :test/x 12345]
|
||||||
[eid2 :test/y 12345]}))))
|
[eid2 :test/y 12345]}))))
|
||||||
|
|
||||||
(testing "can't upsert a :db.unique/value field"
|
(testing "can't upsert a :db.unique/value field"
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
ExceptionInfo #"unique constraint"
|
ExceptionInfo #"unique constraint"
|
||||||
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :test/x 12345 :test/y 99999}]))))))
|
(<? (d/<transact! conn [{:db/id (d/id-literal :db.part/user -1) :test/x 12345 :test/y 99999}])))))))
|
||||||
|
|
||||||
(finally
|
#_ (time (t/run-tests))
|
||||||
(<? (d/<close conn)))))))
|
|
||||||
|
|
Loading…
Reference in a new issue