2016-07-27 21:29:16 +00:00
|
|
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
(ns datomish.db-test
|
|
|
|
#?(:cljs
|
|
|
|
(:require-macros
|
|
|
|
[datomish.pair-chan :refer [go-pair <?]]
|
|
|
|
[datomish.node-tempfile-macros :refer [with-tempfile]]
|
|
|
|
[cljs.core.async.macros :as a :refer [go]]))
|
|
|
|
(:require
|
|
|
|
[datomish.util :as util #?(:cljs :refer-macros :clj :refer) [raise cond-let]]
|
|
|
|
[datomish.sqlite :as s]
|
|
|
|
[datomish.datom]
|
|
|
|
|
|
|
|
[datascript.core :as d]
|
|
|
|
[datascript.db :as db]
|
|
|
|
|
|
|
|
[datomish.db :as dm]
|
|
|
|
#?@(:clj [[datomish.pair-chan :refer [go-pair <?]]
|
|
|
|
[tempfile.core :refer [tempfile with-tempfile]]
|
|
|
|
[datomish.test-macros :refer [deftest-async]]
|
|
|
|
[clojure.test :as t :refer [is are deftest testing]]
|
|
|
|
[clojure.core.async :refer [go <! >!]]])
|
|
|
|
#?@(:cljs [[datomish.pair-chan]
|
|
|
|
[datomish.test-macros :refer-macros [deftest-async]]
|
|
|
|
[datomish.node-tempfile :refer [tempfile]]
|
|
|
|
[cljs.test :as t :refer-macros [is are deftest testing async]]
|
|
|
|
[cljs.core.async :as a :refer [<! >!]]]))
|
|
|
|
#?(:clj
|
|
|
|
(:import [clojure.lang ExceptionInfo]))
|
|
|
|
#?(:clj
|
|
|
|
(:import [datascript.db DB])))
|
|
|
|
|
|
|
|
(defn- <datoms [db]
|
|
|
|
(let [entids (zipmap (vals (dm/idents db)) (keys (dm/idents db)))]
|
|
|
|
(go-pair
|
|
|
|
(->>
|
|
|
|
(s/all-rows (:sqlite-connection db) ["SELECT e, a, v, tx FROM datoms"])
|
|
|
|
(<?)
|
|
|
|
(mapv #(vector (:e %) (entids (:a %)) (:v %)))
|
|
|
|
(filter #(not (= :db/txInstant (second %))))
|
|
|
|
(set)))))
|
|
|
|
|
|
|
|
(defn- <transactions [db]
|
|
|
|
(let [entids (zipmap (vals (dm/idents db)) (keys (dm/idents db)))]
|
|
|
|
(go-pair
|
|
|
|
(->>
|
|
|
|
(s/all-rows (:sqlite-connection db) ["SELECT e, a, v, tx, added FROM transactions ORDER BY tx ASC, e, a, v, added"])
|
|
|
|
(<?)
|
|
|
|
(mapv #(vector (:e %) (entids (:a %)) (:v %) (:tx %) (:added %)))))))
|
|
|
|
|
|
|
|
(defn tx [report]
|
|
|
|
(get-in report [:db-after :current-tx]))
|
|
|
|
|
2016-07-28 00:07:05 +00:00
|
|
|
(def test-schema
|
|
|
|
{:x {:db/unique :db.unique/identity
|
|
|
|
:db/valueType :db.type/integer}
|
|
|
|
:y {:db/cardinality :db.cardinality/many
|
|
|
|
:db/valueType :db.type/integer}
|
|
|
|
:name {:db/unique :db.unique/identity
|
|
|
|
:db/valueType :db.type/string}
|
|
|
|
:aka {:db/cardinality :db.cardinality/many
|
|
|
|
:db/valueType :db.type/string}})
|
|
|
|
|
2016-07-27 21:29:16 +00:00
|
|
|
(deftest-async test-add-one
|
|
|
|
(with-tempfile [t (tempfile)]
|
|
|
|
(let [c (<? (s/<sqlite-connection t))
|
2016-07-28 00:07:05 +00:00
|
|
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
2016-07-27 21:29:16 +00:00
|
|
|
conn (dm/connection-with-db db)
|
|
|
|
now 0xdeadbeef]
|
|
|
|
(try
|
|
|
|
(let [;; TODO: drop now, allow to set :db/txInstant.
|
2016-07-28 00:07:05 +00:00
|
|
|
report (<? (dm/<transact! conn [[:db/add 0 :name "valuex"]] now))
|
2016-07-27 21:29:16 +00:00
|
|
|
tx (tx report)]
|
|
|
|
(is (= (<? (<datoms (dm/db conn)))
|
2016-07-28 00:07:05 +00:00
|
|
|
#{[0 :name "valuex"]}))
|
2016-07-27 21:29:16 +00:00
|
|
|
(is (= (<? (<transactions (dm/db conn)))
|
2016-07-28 00:07:05 +00:00
|
|
|
[[0 :name "valuex" tx 1] ;; TODO: true, not 1.
|
2016-07-27 21:29:16 +00:00
|
|
|
[tx :db/txInstant now tx 1]])))
|
|
|
|
(finally
|
|
|
|
(<? (dm/close-db db)))))))
|
|
|
|
|
|
|
|
(deftest-async test-add-two
|
|
|
|
(with-tempfile [t (tempfile)]
|
|
|
|
(let [c (<? (s/<sqlite-connection t))
|
2016-07-28 00:07:05 +00:00
|
|
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
2016-07-27 21:29:16 +00:00
|
|
|
conn (dm/connection-with-db db)
|
|
|
|
now 0xdeadbeef]
|
|
|
|
(try
|
2016-07-28 00:07:05 +00:00
|
|
|
(let [tx1 (tx (<? (dm/<transact! conn [[:db/add 1 :name "Ivan"]] now)))
|
|
|
|
tx2 (tx (<? (dm/<transact! conn [[:db/add 1 :name "Petr"]] now)))
|
|
|
|
tx3 (tx (<? (dm/<transact! conn [[:db/add 1 :aka "Tupen"]] now)))
|
|
|
|
tx4 (tx (<? (dm/<transact! conn [[:db/add 1 :aka "Devil"]] now)))]
|
2016-07-27 21:29:16 +00:00
|
|
|
(is (= (<? (<datoms (dm/db conn)))
|
2016-07-28 00:07:05 +00:00
|
|
|
#{[1 :name "Petr"]
|
|
|
|
[1 :aka "Tupen"]
|
|
|
|
[1 :aka "Devil"]}))
|
2016-07-27 21:29:16 +00:00
|
|
|
|
|
|
|
(is (= (<? (<transactions (dm/db conn)))
|
2016-07-28 00:07:05 +00:00
|
|
|
[[1 :name "Ivan" tx1 1] ;; TODO: true, not 1.
|
2016-07-27 21:29:16 +00:00
|
|
|
[tx1 :db/txInstant now tx1 1]
|
2016-07-28 00:07:05 +00:00
|
|
|
[1 :name "Ivan" tx2 0]
|
|
|
|
[1 :name "Petr" tx2 1]
|
2016-07-27 21:29:16 +00:00
|
|
|
[tx2 :db/txInstant now tx2 1]
|
2016-07-28 00:07:05 +00:00
|
|
|
[1 :aka "Tupen" tx3 1]
|
2016-07-27 21:29:16 +00:00
|
|
|
[tx3 :db/txInstant now tx3 1]
|
2016-07-28 00:07:05 +00:00
|
|
|
[1 :aka "Devil" tx4 1]
|
2016-07-27 21:29:16 +00:00
|
|
|
[tx4 :db/txInstant now tx4 1]])))
|
|
|
|
(finally
|
|
|
|
(<? (dm/close-db db)))))))
|
|
|
|
|
|
|
|
;; TODO: fail multiple :add and :retract of the same datom in the same transaction.
|
|
|
|
(deftest-async test-retract
|
|
|
|
(with-tempfile [t (tempfile)]
|
|
|
|
(let [c (<? (s/<sqlite-connection t))
|
2016-07-28 00:07:05 +00:00
|
|
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
2016-07-27 21:29:16 +00:00
|
|
|
conn (dm/connection-with-db db)
|
|
|
|
now 0xdeadbeef]
|
|
|
|
(try
|
2016-07-28 00:07:05 +00:00
|
|
|
(let [txa (tx (<? (dm/<transact! conn [[:db/add 0 :x 123]] now)))
|
|
|
|
txb (tx (<? (dm/<transact! conn [[:db/retract 0 :x 123]] now)))]
|
2016-07-27 21:29:16 +00:00
|
|
|
(is (= (<? (<datoms db))
|
|
|
|
#{}))
|
|
|
|
(is (= (<? (<transactions db))
|
2016-07-28 00:07:05 +00:00
|
|
|
[[0 :x 123 txa 1] ;; TODO: true, not 1.
|
2016-07-27 21:29:16 +00:00
|
|
|
[txa :db/txInstant now txa 1]
|
2016-07-28 00:07:05 +00:00
|
|
|
[0 :x 123 txb 0]
|
2016-07-27 21:29:16 +00:00
|
|
|
[txb :db/txInstant now txb 1]])))
|
|
|
|
(finally
|
|
|
|
(<? (dm/close-db db)))))))
|
|
|
|
|
|
|
|
(deftest-async test-id-literal-1
|
|
|
|
(with-tempfile [t (tempfile)]
|
|
|
|
(let [c (<? (s/<sqlite-connection t))
|
2016-07-28 00:07:05 +00:00
|
|
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
2016-07-27 21:29:16 +00:00
|
|
|
conn (dm/connection-with-db db)
|
|
|
|
now -1]
|
|
|
|
(try
|
|
|
|
(let [report (<? (dm/<transact! conn [[:db/add (dm/id-literal :db.part/user -1) :x 0]
|
|
|
|
[:db/add (dm/id-literal :db.part/user -1) :y 1]
|
|
|
|
[:db/add (dm/id-literal :db.part/user -2) :y 2]
|
|
|
|
[:db/add (dm/id-literal :db.part/user -2) :y 3]] now))]
|
|
|
|
(is (= (keys (:tempids report)) ;; TODO: include values.
|
|
|
|
[(dm/id-literal :db.part/user -1)
|
|
|
|
(dm/id-literal :db.part/user -2)]))
|
|
|
|
|
|
|
|
(let [eid1 (get-in report [:tempids (dm/id-literal :db.part/user -1)])
|
|
|
|
eid2 (get-in report [:tempids (dm/id-literal :db.part/user -2)])]
|
|
|
|
(is (= (<? (<datoms db))
|
|
|
|
#{[eid1 :x 0]
|
|
|
|
[eid1 :y 1]
|
|
|
|
[eid2 :y 2]
|
|
|
|
[eid2 :y 3]}))))
|
|
|
|
|
|
|
|
(finally
|
|
|
|
(<? (dm/close-db db)))))))
|
2016-07-27 21:30:01 +00:00
|
|
|
|
|
|
|
(deftest-async test-add-ident
|
|
|
|
(with-tempfile [t (tempfile)]
|
|
|
|
(let [c (<? (s/<sqlite-connection t))
|
2016-07-28 00:07:05 +00:00
|
|
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
2016-07-27 21:30:01 +00:00
|
|
|
conn (dm/connection-with-db db)
|
|
|
|
now -1]
|
|
|
|
(try
|
2016-07-28 00:07:05 +00:00
|
|
|
(let [report (<? (dm/<transact! conn [[:db/add (dm/id-literal :db.part/db -1) :db/ident :test/ident]] now))
|
2016-07-27 21:30:01 +00:00
|
|
|
db-after (:db-after report)
|
|
|
|
tx (:current-tx db-after)]
|
2016-07-28 00:07:05 +00:00
|
|
|
(is (= (:test/ident (dm/idents db-after)) (get-in report [:tempids (dm/id-literal :db.part/db -1)]))))
|
2016-07-27 21:30:01 +00:00
|
|
|
|
2016-07-28 00:07:05 +00:00
|
|
|
;; TODO: This should fail, but doesn't, due to stringification of :test/ident.
|
2016-07-27 21:30:01 +00:00
|
|
|
;; (is (thrown-with-msg?
|
|
|
|
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got "
|
2016-07-28 00:07:05 +00:00
|
|
|
;; (<? (dm/<transact! conn [[:db/retract 44 :db/ident :test/ident]] now))))
|
2016-07-27 21:30:01 +00:00
|
|
|
|
|
|
|
;; ;; Renaming looks like retraction and then assertion.
|
|
|
|
;; (is (thrown-with-msg?
|
|
|
|
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got"
|
|
|
|
;; (<? (dm/<transact! conn [[:db/add 44 :db/ident :other-name]] now))))
|
|
|
|
|
|
|
|
;; (is (thrown-with-msg?
|
|
|
|
;; ExceptionInfo #"Re-asserting a :db/ident is not yet supported, got"
|
2016-07-28 00:07:05 +00:00
|
|
|
;; (<? (dm/<transact! conn [[:db/add 55 :db/ident :test/ident]] now))))
|
2016-07-27 21:30:01 +00:00
|
|
|
|
|
|
|
(finally
|
|
|
|
(<? (dm/close-db db)))))))
|