Skip to content

Commit

Permalink
Add with-conforms to speculatively transact norms
Browse files Browse the repository at this point in the history
with-conforms is a variation of ensure-conforms
that uses datmic.api/with to speculatively transact norms
to a db.
Its use is to try norms on top of an existing database
without modifiying it
  • Loading branch information
Chris Blom committed Nov 5, 2015
1 parent 41994ee commit de58701
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/io/rkn/conformity.clj
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,42 @@
([conn conformity-attr norm-map norm-names]
(ensure-conformity-schema conn conformity-attr)
(reduce-norms [] conn conformity-attr norm-map norm-names)))

(defn- speculative-conn
"Creates a mock datomic.Connection that speculatively applies transactions using datomic.api/with"
[db]
(let [state (atom {:db-after db})
wrap-listenable-future (fn [value]
(reify datomic.ListenableFuture
(get [this] value)
(get [this timeout time-unit] value)
(toString [this] (prn-str value))))]
(reify datomic.Connection
(db [_] (:db-after @state))
(transact [_ tx-data]
(let [tx-result-after (swap! state #(d/with (:db-after %) tx-data))]
(wrap-listenable-future tx-result-after)))
(sync [_] (wrap-listenable-future (:db-after @state)))
(sync [_ t] (wrap-listenable-future (:db-after @state)))
(syncSchema [_ t] (wrap-listenable-future (:db-after @state))))))

(defn with-conforms
"Variation of ensure-conforms that speculatively ensures norm are conformed to
On success, returns a map with:
:db the resulting database that conforms the the provided norms
:result a vector of maps with values for :norm-name, :tx-index,
and :tx-result for each transaction that improved the db's conformity.
On failure, throws an ex-info with a reason and data about any partial
success before the failure."
([db norm-map]
(with-conforms db norm-map (keys norm-map)))
([db norm-map norm-names]
(with-conforms db default-conformity-attribute norm-map norm-names))
([db conformity-attr norm-map norm-names]
(let [conn (speculative-conn db)]
(ensure-conformity-schema conn conformity-attr)
(let [result (reduce-norms [] conn conformity-attr norm-map norm-names)]
{:db (d/db conn)
:result result}))))
49 changes: 49 additions & 0 deletions test/io/rkn/conformity_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,55 @@
(ensure-conforms conn {:test4/norm1 {:txes []}}
[:test4/norm1]))))))

(deftest test-with-conforms
(testing "speculatively installs all expected norms"

(testing "without explicit norms list"
(let [{:keys [db result]} (with-conforms (d/db (fresh-conn)) sample-norms-map1)]
(is (= (set (map (juxt :norm-name :tx-index) result))
(set [[:test1/norm1 0] [:test1/norm1 1] [:test1/norm2 0]])))
(is (has-attribute? db :test/attribute1))
(is (has-attribute? db :test/attribute2))
(is (has-attribute? db :test/attribute3))
(is (empty? (:result (with-conforms db sample-norms-map1))))))

(testing "can add db/unique after an avet index add"
(let [{:keys [db result]} (with-conforms (d/db (fresh-conn)) sample-norms-map5)]
(is (has-attribute? db :test/unique-attribute))))

(testing "with explicit norms list"
(let [{:keys [db result]} (with-conforms (d/db (fresh-conn)) sample-norms-map2 [:test2/norm1])]
(is (= (map (juxt :norm-name :tx-index) result)
[[:test2/norm1 0]]))
(is (has-attribute? db :test/attribute1))
(is (not (has-attribute? db :test/attribute2)))
(is (empty? (:result (with-conforms db sample-norms-map2 [:test2/norm1])))))

(testing "and requires"
(let [{:keys [db result]} (with-conforms (d/db (fresh-conn)) sample-norms-map3 [:test3/norm2])]
(is (= (map (juxt :norm-name :tx-index) result)
[[:test3/norm1 0] [:test3/norm1 1] [:test3/norm2 0]]))
(is (has-attribute? db :test/attribute1))
(is (has-attribute? db :test/attribute2))
(is (has-attribute? db :test/attribute3))
(is (empty? (:result (with-conforms db sample-norms-map3
[:test3/norm2]))))))))

(testing "throws exception if norm-map lacks transactions for a norm"
(let [db (d/db (fresh-conn))]
(is (thrown-with-msg? clojure.lang.ExceptionInfo
#"No transactions provided for norm :test4/norm1"
(with-conforms db {}
[:test4/norm1])))
(is (thrown-with-msg? clojure.lang.ExceptionInfo
#"No transactions provided for norm :test4/norm1"
(with-conforms db {:test4/norm1 {}}
[:test4/norm1])))
(is (thrown-with-msg? clojure.lang.ExceptionInfo
#"No transactions provided for norm :test4/norm1"
(with-conforms db {:test4/norm1 {:txes []}}
[:test4/norm1]))))))

(deftest test-conforms-to?
(let [tx-count (count (:txes (sample-norms-map1 :test1/norm1)))]
(testing "returns true if a norm is already installed"
Expand Down

0 comments on commit de58701

Please sign in to comment.