From 75a91f7f7b6b475493a6953c47b39b6daf11cb2f Mon Sep 17 00:00:00 2001 From: didier amyot Date: Fri, 11 Sep 2020 07:25:08 -0400 Subject: [PATCH] going somewhere. --- src/clj/skirmish/ledger/data_layer.clj | 32 ++++++++++- test/clj/skirmish/ledger/test_core.clj | 80 ++++++++++++++++++-------- 2 files changed, 84 insertions(+), 28 deletions(-) diff --git a/src/clj/skirmish/ledger/data_layer.clj b/src/clj/skirmish/ledger/data_layer.clj index 73f4e71..dcbdbf1 100644 --- a/src/clj/skirmish/ledger/data_layer.clj +++ b/src/clj/skirmish/ledger/data_layer.clj @@ -1,6 +1,7 @@ (ns skirmish.ledger.data-layer (:require [datomic.api :as d] - [infix.macros :refer [infix]])) + [infix.macros :refer [infix]] + [slingshot.slingshot :refer [throw+]])) (defn get-all-account-public-key [db] (d/q '[:find ?pk :where [?e :skirmish.ledger/owner-public-key ?pk]] db)) @@ -123,6 +124,31 @@ res (assoc res (:to-account-id transaction) to-account)] res)) +(defn validate-transactions [transactions] + (loop [transactions transactions] + (when (seq transactions) + (let [transaction (first transactions)] + (when (not= (-> transaction :from-account :skirmish.ledger/sec) + (-> transaction :to-account :skirmish.ledger/sec)) + (throw+ {:error-type :4XX + :status 400 + :body {:error "Part of the transaction includes a transaction between two wallets of different underlying securities."}})) + (recur (rest transactions))))) + transactions) + +(defn validate-resulting-transactions [transactions] + (loop [transactions transactions] + (when (seq transactions) + (let [transaction (first transactions)] + (when (neg? (get transaction 3)) + (throw+ {:error-type :4XX + :status 400 + :body {:error "Transaction would result in a negative amount in a wallet."}}))) + (recur (rest transactions)))) + transactions) + (defn create-transactions [db transactions] - (let [transactions (map #(assoc-accounts db %) transactions)] - (into #{} (vals (reduce compute-transactions {} transactions))))) \ No newline at end of file + (let [transactions (map #(assoc-accounts db %) transactions) + transactions (validate-transactions transactions) + ret (vals (reduce compute-transactions {} transactions))] + (validate-resulting-transactions ret))) \ No newline at end of file diff --git a/test/clj/skirmish/ledger/test_core.clj b/test/clj/skirmish/ledger/test_core.clj index 119f9ed..7cd4f17 100644 --- a/test/clj/skirmish/ledger/test_core.clj +++ b/test/clj/skirmish/ledger/test_core.clj @@ -57,21 +57,22 @@ (->> create-security-4 cs (s/assert ::ok)) conn)) - -(defn create-some-accounts [conn] - (let [es (controllers/emit-securities conn) - emit-sec-req-1 {:body (edn->stream [100 "1-sec-1"]) :route-params {:sec-id "sec-1"}} - emit-sec-req-2 {:body (edn->stream [200 "2-sec-1"]) :route-params {:sec-id "sec-1"}} - emit-sec-req-3 {:body (edn->stream [1000 "1-sec-2"]) :route-params {:sec-id "sec-2"}} - emit-sec-req-4 {:body (edn->stream [2000 "2-sec-2"]) :route-params {:sec-id "sec-2"}}] - (->> emit-sec-req-1 es (s/assert ::ok)) - (->> emit-sec-req-2 es (s/assert ::ok)) - (->> emit-sec-req-3 es (s/assert ::ok)) - (->> emit-sec-req-4 es (s/assert ::ok)) +(defn create-some-accounts [conn n] + (let [create-emit-req (fn [amount account-id sec-id] {:body (edn->stream [amount account-id]) :route-params {:sec-id sec-id}}) + sec-1-reqs (for [i (range 1 (inc n))] (create-emit-req (* i 100) (str i "-sec-1") "sec-1")) + sec-2-reqs (for [i (range 1 (inc n))] (create-emit-req (* i 100) (str i "-sec-2") "sec-2")) + reqs (concat sec-1-reqs sec-2-reqs)] + (loop [reqs reqs] + (when (seq reqs) + (let [es (controllers/emit-securities conn) + req (first reqs)] + (->> req es (s/assert ::ok)) + (recur (rest reqs))))) conn)) -(def create-some-securities-and-accounts (comp create-some-accounts create-some-securities)) +(defn create-some-securities-and-accounts [conn n-accounts-by-currency] + (-> conn create-some-securities (create-some-accounts n-accounts-by-currency))) (facts "about the securities" @@ -173,24 +174,24 @@ (facts "about accounts" (fact "we should be able to get list of all accounts" - (let [conn (create-some-securities-and-accounts (create-conn)) + (let [conn (create-some-securities-and-accounts (create-conn) 2) req {:route-params {} :headers {"accept" "application/edn"}} - expt [{:account-public-key "1-sec-1" :url "http://localhost:1604/api/1/ledger/accounts/1-sec-1"} - {:account-public-key "1-sec-2" :url "http://localhost:1604/api/1/ledger/accounts/1-sec-2"} - {:account-public-key "2-sec-1" :url "http://localhost:1604/api/1/ledger/accounts/2-sec-1"} - {:account-public-key "2-sec-2" :url "http://localhost:1604/api/1/ledger/accounts/2-sec-2"}]] + expt #{{:account-public-key "1-sec-1" :url "http://localhost:1604/api/1/ledger/accounts/1-sec-1"} + {:account-public-key "1-sec-2" :url "http://localhost:1604/api/1/ledger/accounts/1-sec-2"} + {:account-public-key "2-sec-1" :url "http://localhost:1604/api/1/ledger/accounts/2-sec-1"} + {:account-public-key "2-sec-2" :url "http://localhost:1604/api/1/ledger/accounts/2-sec-2"}}] ((controllers/get-available-accounts conn) req) => (spec-check ::ok) - (-> ((controllers/get-available-accounts conn) req) body :accounts sec->hash-set) => (sec->hash-set expt))) + (-> ((controllers/get-available-accounts conn) req) body :accounts) => (just expt))) (fact "we should be able to get account" - (let [conn (create-some-securities-and-accounts (create-conn)) + (let [conn (create-some-securities-and-accounts (create-conn) 10) req {:route-params {:account-id "2-sec-1"} :headers {"accept" "application/edn"}}] ((controllers/get-account conn) req) => (spec-check ::ok) (body ((controllers/get-account conn) req)) => {:account {:account-public-key "2-sec-1" :amount 200 :sec-id :sec-1} :sec-url "/api/1/ledger/securities/sec-1"})) (fact "the interest should be payed when getting the account" - (let [conn (create-some-securities-and-accounts (create-conn)) + (let [conn (create-some-securities-and-accounts (create-conn) 10) req {:route-params {:account-id "2-sec-1"} :headers {"accept" "application/edn"}}] ((controllers/get-account conn) req) => (spec-check ::ok) (body ((controllers/get-account conn) req)) => {:account {:account-public-key "2-sec-1" :amount 200 :sec-id :sec-1} @@ -198,7 +199,7 @@ (fact "we should not get an account if it does not exists" - (let [conn (create-some-securities-and-accounts (create-conn)) + (let [conn (create-some-securities-and-accounts (create-conn) 10) req {:route-params {:account-id "2-sec-10"} :headers {"accept" "application/edn"}}] ((controllers/get-account conn) req) => (spec-check ::status-404)))) @@ -266,7 +267,7 @@ (facts "about transactions" (fact "about transferring an amount from one wallet to another" - (let [conn (create-some-securities-and-accounts (create-conn)) + (let [conn (create-some-securities-and-accounts (create-conn) 10) transaction {:from-account-id "1-sec-1" :to-account-id "2-sec-1" :amount 50} @@ -274,13 +275,42 @@ [eid1 eid2] (get-account-eids db "1-sec-1" "2-sec-1") expt #{[:db/add eid1 :skirmish.ledger/amount 50] [:db/add eid2 :skirmish.ledger/amount 250]}] - (data-layer/create-transactions db [transaction]) => expt)) ;expt)) + (data-layer/create-transactions db [transaction]) => (just expt))) (fact "about transferring an amount from one wallet to another" - (let [conn (create-some-securities-and-accounts (create-conn)) + (let [conn (create-some-securities-and-accounts (create-conn) 10) transaction {:from-account-id "1-sec-1" :to-account-id "1-sec-1" :amount 45} db (d/db conn) [eid1] (get-account-eids db "1-sec-1") expt #{[:db/add eid1 :skirmish.ledger/amount 100]}] - (data-layer/create-transactions db [transaction]) => expt))) + (data-layer/create-transactions db [transaction]) => (just expt))) + (fact "about transferring amounts between three wallets" + (let [conn (create-some-securities-and-accounts (create-conn) 10) + transaction-1 {:from-account-id "1-sec-1" + :to-account-id "2-sec-1" + :amount 50} + transaction-2 {:from-account-id "2-sec-1" + :to-account-id "3-sec-1" + :amount 250} + db (d/db conn) + [eid1 eid2 eid3] (get-account-eids db "1-sec-1" "2-sec-1" "3-sec-1") + expt #{[:db/add eid1 :skirmish.ledger/amount 50] + [:db/add eid2 :skirmish.ledger/amount 0] + [:db/add eid3 :skirmish.ledger/amount 550]}] + (data-layer/create-transactions db [transaction-1 transaction-2]) => (just expt))) + (fact "about transaction between different assets types" + (let [conn (create-some-securities-and-accounts (create-conn) 10) + transaction {:from-account-id "1-sec-1" + :to-account-id "1-sec-2" + :amount 50} + db (d/db conn)] + (data-layer/create-transactions db [transaction]) => (throws #"different underlying securities"))) + (fact "about transaction with negative results" + (let [conn (create-some-securities-and-accounts (create-conn) 10) + transaction {:from-account-id "1-sec-1" + :to-account-id "2-sec-1" + :amount 500} + db (d/db conn)] + (data-layer/create-transactions db [transaction]) => (throws #"negative amount in a wallet")))) +