diff --git a/src/clj/chicken_master/api.clj b/src/clj/chicken_master/api.clj index 9962717..98573ee 100644 --- a/src/clj/chicken_master/api.clj +++ b/src/clj/chicken_master/api.clj @@ -9,16 +9,24 @@ {:headers {"Content-Type" "application/edn"} :body resp}) -(defn get-customers [user-id] (as-edn (customers/get-all user-id))) +(defn get-values [user-id kinds] + (let [getters {:customers customers/get-all + :products products/get-all + :order orders/get-all}] + (as-edn (reduce #(assoc %1 %2 ((getters %2) user-id)) {} kinds)))) + +(defn get-customers [user-id] (get-values user-id [:customers])) (defn add-customer [{:keys [body basic-authentication]}] (as-edn (some->> body :name (customers/create! basic-authentication)))) -(defn delete-customer [user-id id] (->> id edn/read-string (customers/delete! user-id) as-edn)) +(defn delete-customer [user-id id] + (->> id edn/read-string (customers/delete! user-id)) + (get-values user-id [:orders :customers])) -(defn get-products [user-id] (as-edn (products/get-all user-id))) +(defn get-products [user-id] (get-values user-id [:products])) (defn save-products [{:keys [body basic-authentication]}] (some->> body (products/update! basic-authentication) (assoc {} :products) as-edn)) -(defn get-orders [user-id] (as-edn (orders/get-all user-id))) +(defn get-orders [user-id] (get-values user-id [:orders])) (defn update-order [request] (let [user-id (:basic-authentication request) id (some-> request :route-params :id (Integer/parseInt)) @@ -28,10 +36,7 @@ (defn delete-order [user-id id] (->> id edn/read-string (orders/delete! user-id) as-edn)) (defn set-order-state [user-id id status] (as-edn (orders/change-state! user-id (edn/read-string id) status))) -(defn get-stock [user-id] - (as-edn - {:customers (:body (get-customers user-id)) - :products (:body (get-products user-id))})) +(defn get-stock [user-id] (get-values user-id [:customers :products])) (defroutes all-routes (GET "/stock" [:as {user-id :basic-authentication}] (get-stock user-id)) diff --git a/src/clj/chicken_master/customers.clj b/src/clj/chicken_master/customers.clj index a83a6df..8047798 100644 --- a/src/clj/chicken_master/customers.clj +++ b/src/clj/chicken_master/customers.clj @@ -1,13 +1,15 @@ (ns chicken-master.customers (:require [next.jdbc :as jdbc] [next.jdbc.sql :as sql] - [chicken-master.db :as db] - [chicken-master.orders :as orders])) + [chicken-master.db :as db])) (defn get-all [user-id] (->> (sql/query db/db-uri ["select * from customers where deleted is null AND user_id = ?" user-id]) (map (fn [{:customers/keys [id name]}] {:id id :name name})))) +(defn get-by-name [tx user-id name] + (:customers/id (db/get-by-id tx user-id :customers (:name name) :name))) + (defn create! [user-id name] (jdbc/execute! db/db-uri ["INSERT INTO customers (name, user_id) VALUES(?, ?) ON CONFLICT (name, user_id) DO UPDATE SET deleted = NULL" @@ -15,6 +17,4 @@ {:customers (get-all user-id)}) (defn delete! [user-id id] - (sql/update! db/db-uri :customers {:deleted true} {:id id :user_id user-id}) - {:orders (orders/get-all user-id) - :customers (get-all user-id)}) + (sql/update! db/db-uri :customers {:deleted true} {:id id :user_id user-id})) diff --git a/src/clj/chicken_master/orders.clj b/src/clj/chicken_master/orders.clj index c290926..9bfd142 100644 --- a/src/clj/chicken_master/orders.clj +++ b/src/clj/chicken_master/orders.clj @@ -4,9 +4,10 @@ [next.jdbc.sql :as sql] [chicken-master.db :as db] [chicken-master.products :as products] + [chicken-master.customers :as customers] [chicken-master.time :as t])) -(defn- upsert-order! [tx user-id customer-id {:keys [id day state notes]}] +(defn upsert-order! [tx user-id customer-id {:keys [id day state notes]}] (let [order {:customer_id customer-id :notes notes :status (some-> state name jdbc.types/as-other) @@ -58,7 +59,7 @@ (defn replace! [user-id {:keys [who products] :as order}] (jdbc/with-transaction [tx db/db-uri] (let [customer-id (or (:id who) - (:customers/id (db/get-by-id tx user-id :customers (:name who) :name))) + (customers/get-by-name tx user-id who)) products-map (products/products-map tx user-id products) previous-day (some->> order :id (db/get-by-id tx user-id :orders) :orders/order_date (.toInstant)) order-id (upsert-order! tx user-id customer-id order)] @@ -87,7 +88,7 @@ operator (condp = state "fulfilled" "-" "waiting" "+")] - (when (not= (:state order) state) + (when (not= (:state order) (keyword state)) (doseq [[prod amount] (:products order)] (jdbc/execute-one! tx [(str "UPDATE products SET amount = amount " operator " ? WHERE name = ?") diff --git a/test/clj/chicken_master/orders_test.clj b/test/clj/chicken_master/orders_test.clj new file mode 100644 index 0000000..48c03af --- /dev/null +++ b/test/clj/chicken_master/orders_test.clj @@ -0,0 +1,199 @@ +(ns clj.chicken-master.orders-test + (:require + [next.jdbc :as jdbc] + [next.jdbc.sql :as sql] + [chicken-master.orders :as sut] + [chicken-master.products :as products] + [clojure.string :as str] + [clojure.test :refer [deftest is testing]])) + +(defn raw-order-row [& {:keys [id notes status date user_id user_name products] + :or {id 1 notes "note" status "pending" date #inst "2020-01-01" + user_id 2 user_name "mr blobby" products {:eggs 12 :milk 3}}}] + (if products + (for [[product amount] products] + (merge #:orders{:id id :notes notes :status status :order_date date} + #:customers{:id user_id :name user_name} + {:products/name (name product) :order_products/amount amount})) + (merge #:orders{:id id :notes notes :status status :order_date date} + #:customers{:id user_id :name user_name} + {:products/name nil :order_products/amount nil}))) + +(deftest test-get-order + (testing "correct values returned" + (with-redefs [sql/query (fn [_ [query & params]] + (is (str/ends-with? query "WHERE o.id = ? AND o.user_id = ?")) + (is (= params [123 "1"])) + (raw-order-row))] + (is (= (sut/get-order :tx "1" 123) + {:id 1, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}})))) + + (testing "Only 1 item returned" + (with-redefs [sql/query (fn [_ [query & params]] + (is (str/ends-with? query "WHERE o.id = ? AND o.user_id = ?")) + (is (= params [123 "1"])) + (concat (raw-order-row) + (raw-order-row :id 21)))] + (is (= (sut/get-order :tx "1" 123) + {:id 1, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}))))) + +(deftest test-get-all + (testing "correct values returned" + (with-redefs [sql/query (fn [_ [query & params]] + (is (str/ends-with? query "WHERE o.user_id = ?")) + (is (= params ["1"])) + (concat + (raw-order-row :id 1 :status "waiting") + (raw-order-row :id 2 :date #inst "2020-01-03") + (raw-order-row :id 3 :user_id 43 :user_name "John") + (raw-order-row :id 4)))] + (is (= (sut/get-all "1") + {"2020-01-01" [{:id 1, :notes "note", :state :waiting, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}} + {:id 3, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 43, :name "John"}, + :products {:eggs 12 :milk 3}} + {:id 4, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}] + "2020-01-03" [{:id 2, :notes "note", :state :pending, :day "2020-01-03", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}]}))))) + +(deftest test-replace! + (testing "basic replace order" + (let [order {:id 1, :notes "note", :state :waiting, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}] + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + jdbc/execute-one! (constantly nil) + products/products-map (constantly {"eggs" 1 "milk" 2}) + sut/upsert-order! (fn [_ _ _ o] (is (= o order)) 1) + sql/delete! (fn [_ table by] + (is (= table :order_products)) + (is (= by {:order_id (:id order)}))) + sql/insert-multi! (fn [_ _ cols values] + (is (= cols [:order_id :product_id :amount])) + (is (= values [[1 1 12] [1 2 3]]))) + sql/query (constantly (concat + (raw-order-row :id 1 :status "waiting") + (raw-order-row :id 4)))] + (is (= (sut/replace! :user-id order) + {"2020-01-01" [{:id 1, :notes "note", :state :waiting, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}} + {:id 4, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}]}))))) + + (testing "replace order from different day" + (let [order {:id 1, :notes "note", :state :waiting, :day "2020-01-02", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}] + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + jdbc/execute-one! (constantly {:orders/order_date #inst "2020-01-01"}) + products/products-map (constantly {"eggs" 1 "milk" 2}) + sut/upsert-order! (fn [_ _ _ o] (is (= o order)) 1) + sql/delete! (fn [_ table by] + (is (= table :order_products)) + (is (= by {:order_id (:id order)}))) + sql/insert-multi! (fn [_ _ cols values] + (is (= cols [:order_id :product_id :amount])) + (is (= values [[1 1 12] [1 2 3]]))) + sql/query (constantly (concat + (raw-order-row :id 1 :status "waiting" :date #inst "2020-01-02") + (raw-order-row :id 4)))] + (is (= (sut/replace! :user-id order) + {"2020-01-01" [{:id 4, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}] + "2020-01-02" [{:id 1, :notes "note", :state :waiting, :day "2020-01-02", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}]}))))) + + (testing "unknown products are ignored" + (let [order {:id 1, :notes "note", :state :waiting, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}] + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + jdbc/execute-one! (constantly nil) + products/products-map (constantly {"eggs" 1 "candles" 2}) + sut/upsert-order! (fn [_ _ _ o] (is (= o order)) 1) + sql/delete! (fn [_ table by] + (is (= table :order_products)) + (is (= by {:order_id (:id order)}))) + sql/insert-multi! (fn [_ _ cols values] + (is (= cols [:order_id :product_id :amount])) + (is (= values [[1 1 12]]))) + sql/query (constantly (concat + (raw-order-row :id 1 :status "waiting") + (raw-order-row :id 4)))] + (is (= (sut/replace! :user-id order) + {"2020-01-01" [{:id 1, :notes "note", :state :waiting, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}} + {:id 4, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}]})))))) + +(deftest test-delete! + (testing "non deleted items from day are returned" + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + jdbc/execute-one! (constantly {:orders/order_date #inst "2020-01-01"}) + sql/delete! (fn [_ table by] + (is (= table :orders)) + (is (= by {:id 1 :user_id :user-id}))) + sql/query (constantly (raw-order-row :id 4))] + (is (= (sut/delete! :user-id 1) + {"2020-01-01" [{:id 4, :notes "note", :state :pending, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}]})))) + + (testing "nothing returned if no date set for the given order" + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + jdbc/execute-one! (constantly nil) + sql/delete! (fn [_ table by] + (is (= table :orders)) + (is (= by {:id 1 :user_id :user-id}))) + sql/query (constantly (raw-order-row :id 4))] + (is (nil? (sut/delete! :user-id 1)))))) + +(deftest test-change-state! + (testing "states get changed" + (let [updates (atom [])] + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + jdbc/execute-one! #(swap! updates conj %2) + sql/update! (fn [_ table _ val] + (is (= table :orders)) + (is (= val {:id 1}))) + sql/query (constantly (raw-order-row :id 1 :status "waiting"))] + (is (= (sut/change-state! :user-id 1 "fulfilled") + {"2020-01-01" [{:id 1, :notes "note", :state :waiting, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}]})) + (is (= @updates [["UPDATE products SET amount = amount - ? WHERE name = ?" 12 "eggs"] + ["UPDATE products SET amount = amount - ? WHERE name = ?" 3 "milk"]]))))) + + (testing "nothing happens if the state is already set" + (let [updates (atom [])] + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + jdbc/execute-one! #(swap! updates conj %2) + sql/query (constantly (raw-order-row :id 1 :status "waiting"))] + (is (= (sut/change-state! :user-id 1 "waiting") + {"2020-01-01" [{:id 1, :notes "note", :state :waiting, :day "2020-01-01", + :who {:id 2, :name "mr blobby"}, + :products {:eggs 12 :milk 3}}]})) + (is (= @updates []))))) + + (testing "unknown states cause an exception" + (with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) + sql/query (constantly (raw-order-row :id 1 :status "waiting"))] + (try + (sut/change-state! :user-id 1 "bla bla bla") + (is nil "The previous line should have failed") + (catch Exception _ :ok))))) diff --git a/test/cljs/chicken_master/calendar_test.cljs b/test/cljs/chicken_master/calendar_test.cljs index 5ecd0b3..2c53d3b 100644 --- a/test/cljs/chicken_master/calendar_test.cljs +++ b/test/cljs/chicken_master/calendar_test.cljs @@ -1,7 +1,8 @@ (ns chicken-master.sutendar-test (:require [chicken-master.calendar :as sut] - [cljs.test :refer-macros [deftest is testing]])) + [cljs.test :refer-macros [deftest is testing run-tests]])) + (deftest format-raw-order-test (testing "no products" @@ -43,3 +44,4 @@ "product-cow" "cow" "amount-cow" "0" "product-milk" "milk" "amount-milk" "3.2"}) {:who {:name "bla" :id 123} :notes "ble" :products {:eggs 12 :milk 3.2}})))) +(run-tests) diff --git a/test/cljs/chicken_master/products_test.cljs b/test/cljs/chicken_master/products_test.cljs index 3de92c1..3bca7ef 100644 --- a/test/cljs/chicken_master/products_test.cljs +++ b/test/cljs/chicken_master/products_test.cljs @@ -1,7 +1,7 @@ (ns chicken-master.products-test (:require [chicken-master.products :as sut] - [cljs.test :refer-macros [deftest is testing]])) + [cljs.test :refer-macros [deftest is testing run-tests]])) (deftest test-num-or-nil @@ -40,3 +40,5 @@ [:div {:class :input-item} [:label {:for :id} :label] [:input {:type :number :step :any :on-blur nil :defaultValue 12.123 :name :id :id :id}]])))) + +(run-tests)