(ns auto-ap.integration.graphql (:require [auto-ap.graphql :as sut] [venia.core :as v] [clojure.test :as t :refer [deftest is testing use-fixtures]] [datomic.api :as dc] [auto-ap.integration.util :refer [wrap-setup admin-token user-token]] [auto-ap.datomic :refer [conn]])) (defn new-client [args] (merge {:client/name "Test client" :client/code (.toString (java.util.UUID/randomUUID)) :client/locations ["AB"]} args)) (defn new-transaction [args] (merge {:transaction/amount 100.0 :transaction/date #inst "2021-01-01" :transaction/id (.toString (java.util.UUID/randomUUID))} args)) (defn new-invoice [args] (merge {:invoice/total 100.0 :invoice/date #inst "2021-01-01" :invoice/invoice-number (.toString (java.util.UUID/randomUUID))} args)) (use-fixtures :each wrap-setup) (deftest transaction-page (testing "transaction page" @(dc/transact conn [(new-client {:db/id "client"}) (new-transaction {:transaction/client "client"})]) (testing "It should find all transactions" (let [result (:transaction-page (:data (sut/query (admin-token) "{ transaction_page(filters: {client_id: null}) { count, start, data { id } }}")))] (is (= 1 (:count result))) (is (= 0 (:start result))) (is (= 1 (count (:data result)))))) (testing "Users should not see transactions they don't own" (let [result (:transaction-page (:data (sut/query (user-token) "{ transaction_page(filters: {client_id: null}) { count, start, data { id } }}")))] (is (= 0 (:count result))) (is (= 0 (:start result))) (is (= 0 (count (:data result)))))))) (deftest invoice-page (testing "invoice page" @(dc/transact conn [(new-client {:db/id "client"}) (new-invoice {:invoice/client "client" :invoice/status :invoice-status/paid})]) (testing "It should find all invoices" (let [result (first (:invoice-page (:data (sut/query (admin-token) "{ invoice_page(filters: {client_id: null, status:paid}) { count, start, invoices { id } }}"))))] (is (= 1 (:count result))) (is (= 0 (:start result))) (is (= 1 (count (:invoices result)))))) (testing "Users should not see transactions they don't own" (let [result (first (:invoice-page (:data (sut/query (user-token) "{ invoice_page(filters: {client_id: null}) { count, start, invoices { id } }}"))))] (is (= 0 (:count result))) (is (= 0 (:start result))) (is (= 0 (count (:data result)))))))) (deftest ledger-page (testing "ledger" (testing "it should find ledger entries" (let [result (:ledger-page (:data (sut/query (admin-token) "{ ledger_page(filters: {client_id: null}) { count, start, journal_entries { id } }}")))] (is (int? (:count result))) (is (int? (:start result))) (is (seqable? (:journal-entries result))))))) (deftest vendors (testing "vendors" (testing "it should find vendors" (let [result (:ledger-page (:data (sut/query (admin-token) "{ ledger_page(filters: {client_id: null}) { count, start, journal_entries { id } }}")))] (is (int? (:count result))) (is (int? (:start result))) (is (seqable? (:journal-entries result))))))) (deftest transaction-rule-page (testing "it should find rules" (let [result (-> (sut/query (admin-token) "{ transaction_rule_page(client_id: null) { count, start, transaction_rules { id } }}") :data :transaction-rule-page)] (is (int? (:count result))) (is (int? (:start result))) (is (seqable? (:transaction-rules result)))))) (deftest upsert-transaction-rule (let [{:strs [vendor-id account-id yodlee-merchant-id]} (-> @(dc/transact conn [{:vendor/name "Bryce's Meat Co" :db/id "vendor-id"} {:account/name "hello" :db/id "account-id"} {:yodlee-merchant/name "yodlee" :db/id "yodlee-merchant-id"}]) :tempids)] (testing "it should reject rules that don't add up to 100%" (let [q (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "UpsertTransactionRule"} :venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule {:transaction-rule {:accounts [{:account-id account-id :percentage "0.25" :location "Shared"}]}} [:id ]])}]})] (is (thrown? clojure.lang.ExceptionInfo (sut/query (admin-token) q))))) (testing "It should reject rules that are missing both description and merchant" (let [q (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "UpsertTransactionRule"} :venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule {:transaction-rule {:accounts [{:account-id account-id :percentage "1.0" :location "Shared"}]}} [:id ]])}]})] (is (thrown? clojure.lang.ExceptionInfo (sut/query (admin-token) q))))) (testing "it should add rules" (let [q (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "UpsertTransactionRule"} :venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule {:transaction-rule {:description "123" :yodlee-merchant-id yodlee-merchant-id :vendor-id vendor-id :transaction-approval-status :approved :accounts [{:account-id account-id :percentage "0.5" :location "Shared"} {:account-id account-id :percentage "0.5" :location "Shared"}]}} [:id :description :transaction-approval-status [:vendor [:name]] [:yodlee-merchant [:name]] [:accounts [:id :percentage [:account [:name]]]]]])}]}) result (-> (sut/query (admin-token) q) :data :upsert-transaction-rule)] (is (= "123" (:description result))) (is (= "Bryce's Meat Co" (-> result :vendor :name))) (is (= "yodlee" (-> result :yodlee-merchant :name))) (is (= :approved (:transaction-approval-status result))) (is (= "hello" (-> result :accounts (get 0) :account :name ))) (is (:id result)) (testing "it should unset removed fields" (let [q (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "UpsertTransactionRule"} :venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule {:transaction-rule {:id (:id result) :description "123" :vendor-id nil :accounts [{:id (-> result :accounts (get 0) :id) :account-id account-id :percentage "1.0" :location "Shared"}]}} [[:vendor [:name]]]])}]}) result (-> (sut/query (admin-token) q) :data :upsert-transaction-rule)] (is (nil? (:vendor result))))) (testing "it should delete removed rules" (let [q (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "UpsertTransactionRule"} :venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule {:transaction-rule {:id (:id result) :description "123" :vendor-id vendor-id :accounts [{:id (-> result :accounts (get 0) :id) :account-id account-id :percentage "1.0" :location "Shared"}]}} [[:accounts [:id :percentage [:account [:name]]]]]])}]}) result (-> (sut/query (admin-token) q) :data :upsert-transaction-rule)] (is (= 1 (count (:accounts result)))))))))) (deftest test-transaction-rule (testing "it should match rules" (let [matching-transaction @(dc/transact conn [{:transaction/description-original "matching-desc" :transaction/date #inst "2019-01-05T00:00:00.000-08:00" :transaction/client {:client/name "1" :db/id "client-1"} :transaction/bank-account {:db/id "bank-account-1" :bank-account/name "1"} :transaction/amount 1.00 :transaction/id "2019-01-05 matching-desc 1" :db/id "a"} {:transaction/description-original "nonmatching-desc" :transaction/client {:client/name "2" :db/id "client-2"} :transaction/bank-account {:db/id "bank-account-2" :bank-account/name "2"} :transaction/date #inst "2019-01-15T23:23:00.000-08:00" :transaction/amount 2.00 :transaction/id "2019-01-15 nonmatching-desc 2" :db/id "b"}]) {:strs [a b client-1 client-2 bank-account-1 bank-account-2]} (get-in matching-transaction [:tempids]) a (str a) b (str b) rule-test (fn [rule] (-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :query :operation/name "TestTransactionRule"} :venia/queries [{:query/data (sut/->graphql [:test-transaction-rule {:transaction-rule rule} [:id]])}]})) :data :test-transaction-rule))] (testing "based on date " (is (= [{:id b}] (rule-test {:dom-gte 14 :dom-lte 16}))) (is (= [{:id b}] (rule-test {:dom-gte 14}))) (is (= (set [{:id a} {:id b}]) (set (rule-test {:dom-lte 15})))) (is (= [{:id b}] (rule-test {:dom-gte 15}))) (is (= [{:id b}] (rule-test {:dom-gte 15 :dom-lte 15})))) (testing "based on description" (is (= [{:id a}] (rule-test {:description "^match"})))) (testing "based on amount" (is (= [{:id a}] (rule-test {:amount-gte 1.0 :amount-lte 1.0}))) (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-gte 1.0 }))) ) (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-lte 2.0 }))) )) (testing "based on client" (is (= [{:id a}] (rule-test {:client-id (str client-1)}))) (is (= [{:id b}] (rule-test {:client-id (str client-2)})))) (testing "based on bank account" (is (= [{:id a}] (rule-test {:bank-account-id (str bank-account-1)}))) (is (= [{:id b}] (rule-test {:bank-account-id (str bank-account-2)}))))))) (deftest test-match-transaction-rule (testing "it should apply a rules" (let [{:strs [transaction-id transaction-rule-id uneven-transaction-rule-id]} (-> @(dc/transact conn [{:transaction/description-original "matching-desc" :transaction/date #inst "2019-01-05T00:00:00.000-08:00" :transaction/client {:client/name "1" :db/id "client-1"} :transaction/bank-account {:db/id "bank-account-1" :bank-account/name "1"} :transaction/amount 1.00 :db/id "transaction-id"} {:db/id "transaction-rule-id" :transaction-rule/note "transaction rule note" :transaction-rule/description "matching-desc" :transaction-rule/accounts [{:transaction-rule-account/location "A" :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"} :transaction-rule-account/percentage 1.0}]} {:db/id "uneven-transaction-rule-id" :transaction-rule/note "transaction rule note" :transaction-rule/description "matching-desc" :transaction-rule/accounts [{:transaction-rule-account/location "A" :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"} :transaction-rule-account/percentage 0.3333333} {:transaction-rule-account/location "B" :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"} :transaction-rule-account/percentage 0.33333333} {:transaction-rule-account/location "c" :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"} :transaction-rule-account/percentage 0.333333}]}]) :tempids) rule-test (-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "MatchTransactionRules"} :venia/queries [{:query/data (sut/->graphql [:match-transaction-rules {:transaction-rule-id transaction-rule-id :transaction-ids [transaction-id]} [[:matched-rule [:id :note]] [:accounts [:id]] ]])}]})) :data :match-transaction-rules)] (is (= "transaction rule note" (-> rule-test first :matched-rule :note))) (is (= 1 (-> rule-test first :accounts count))) (testing "Should replace accounts when matching a second time" (let [rule-test (-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "MatchTransactionRules"} :venia/queries [{:query/data (sut/->graphql [:match-transaction-rules {:transaction-rule-id transaction-rule-id :transaction-ids [transaction-id]} [[:matched-rule [:id :note]] [:accounts [:id]] ]])}]})) :data :match-transaction-rules)] (is (= 1 (-> rule-test first :accounts count))))) (testing "Should round when the transaction can't be divided eventy" (let [rule-test (-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :mutation :operation/name "MatchTransactionRules"} :venia/queries [{:query/data (sut/->graphql [:match-transaction-rules {:transaction-rule-id uneven-transaction-rule-id :transaction-ids [transaction-id]} [[:matched-rule [:id :note]] [:accounts [:id :amount]] ]])}]})) :data :match-transaction-rules)] (is (= 3 (-> rule-test first :accounts count))) (is (= "0.33" (-> rule-test first :accounts (nth 0) :amount))) (is (= "0.33" (-> rule-test first :accounts (nth 1) :amount))) (is (= "0.34" (-> rule-test first :accounts (nth 2) :amount))))))))