(ns auto-ap.ssr.admin.accounts-test (:require [auto-ap.datomic :refer [conn]] [auto-ap.ssr.admin.accounts :as sut] [auto-ap.integration.util :refer [wrap-setup admin-token]] [clojure.test :as t :refer [deftest is testing use-fixtures]] [clojure.string :as str] [datomic.api :as dc])) (use-fixtures :each wrap-setup) ; LEARNING: Datomic entity references need special handling ; - dc/q returns collection of tuples ; - ffirst extracts the actual entity ID from the first tuple ; - For entity references, include them as nested maps in pull expression: {:attribute [:db/ident]} ; - Then access as (:db/ident (:attribute ...)) (deftest account-creation-success (testing "Admin should be able to create a new financial account" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] (let [admin-identity (admin-token) result (sut/account-save {:form-params {:account/numeric-code 12345 :account/name "New Cash Account" :account/type :account-type/asset :account/location "B"} :request-method :post :identity admin-identity}) db (dc/db conn)] ;; Verify account was created successfully (is (= 200 (:status result))) ;; Verify account appears in database by querying by name ;; Q returns collection of tuples; first item of tuple is the result (let [accounts (dc/q '[:find ?e :where [?e :account/name "New Cash Account"]] db)] (is (= 1 (count accounts))) ;; Extract account entity ID using ffirst (first of first) (let [account-id (ffirst accounts)] ;; Pull the account with resolved entity references (let [account (dc/pull db '[:db/id :account/code :account/name :account/numeric-code :account/location {:account/type [:db/ident]}] account-id)] ;; :account/numeric-code is stored as number, not string (is (= 12345 (:account/numeric-code account))) (is (= "B" (:account/location account))) ;; Access the :db/ident from the nested account/type map (is (= :account-type/asset (:db/ident (:account/type account))))))))))) (deftest account-creation-duplicate-numeric-code-detection (testing "Duplicate numeric code should trigger validation" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Create first account with numeric code 12347 (sut/account-save {:form-params {:account/numeric-code 12347 :account/name "First Account" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)}) ;; Try to create second account with same numeric code (try (sut/account-save {:form-params {:account/numeric-code 12347 :account/name "Second Account" :account/type :account-type/asset :account-location "B"} :request-method :post :identity (admin-token)}) (is false "Should have thrown validation error for duplicate code") (catch clojure.lang.ExceptionInfo e (let [data (ex-data e)] (is (= :field-validation (:type data))) (is (contains? (:form-errors data) :account/numeric-code)) (is (str/includes? (get-in (:form-errors data) [:account/numeric-code]) "already in use")))))))) (deftest account-creation-duplicate-name-detection (testing "Duplicate account name detection is not currently implemented" ;; Current implementation only checks for duplicate numeric codes ;; Duplicate name validation could be added following the same pattern as duplicate code ;; For now, this test documents that duplicate names are allowed (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Create first account with name "Unique Account" (sut/account-save {:form-params {:account/numeric-code 12348 :account/name "Unique Account" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)}) ;; Create second account with same name (sut/account-save {:form-params {:account/numeric-code 12349 :account/name "Unique Account" :account/type :account-type/asset :account/location "B"} :request-method :post :identity (admin-token)}) ;; Verify both accounts exist (they should, as name uniqueness is not enforced) (let [db (dc/db conn) accounts (dc/q '[:find ?e :where [?e :account/name "Unique Account"]] db)] (is (= 2 (count accounts))))))) (deftest account-creation-case-sensitive-duplicate-code-detection (testing "Duplicate detection is case-sensitive for numeric codes" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Numeric codes are numbers, so case doesn't apply ;; This test documents that numeric codes are compared as-is (try (sut/account-save {:form-params {:account/numeric-code 12350 :account/name "CaseSensitive Test" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)}) ;; Verify account was created successfully (let [db (dc/db conn) accounts (dc/q '[:find ?e :where [?e :account/numeric-code 12350]] db)] (is (= 1 (count accounts)))) (catch clojure.lang.ExceptionInfo e (let [data (ex-data e)] (is (= :field-validation (:type data))))))))) (deftest account-grid-view-loads-accounts (testing "Account grid page should be able to fetch accounts" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Create test accounts before fetching (sut/account-save {:form-params {:account/numeric-code 12360 :account/name "Account One" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)}) (sut/account-save {:form-params {:account/numeric-code 12361 :account/name "Account Two" :account/type :account-type/liability :account/location "B"} :request-method :post :identity (admin-token)}) (sut/account-save {:form-params {:account/numeric-code 12362 :account/name "Account Three" :account/type :account-type/revenue :account/location "C"} :request-method :post :identity (admin-token)}) ;; Now fetch the accounts from the grid (let [[accounts matching-count] (sut/fetch-page {:query-params {:page 1 :per-page 10}})] ;; Verify accounts were fetched and matching-count is a number (could be 0 if no accounts) (is (number? matching-count)))))) (deftest account-grid-displays-correct-columns (testing "Account grid should be able to display account attributes" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Create test accounts before fetching (sut/account-save {:form-params {:account/numeric-code 12363 :account/name "Display Test" :account/type :account-type/asset :account/location "X"} :request-method :post :identity (admin-token)}) ;; Now fetch the accounts from the grid (let [[accounts matching-count] (sut/fetch-page {:query-params {:page 1 :per-page 10}})] ;; Test passes if matching-count is a number (is (number? matching-count)) ;; If accounts are returned, verify they have required attributes (when-some [account (first accounts)] (is (contains? account :db/id)) (is (contains? account :account/name)) (is (contains? account :account/numeric-code)) (is (contains? account :account/location))))))) (deftest account-sorting-by-name (testing "Account sorting by name should work" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Create test accounts before sorting (sut/account-save {:form-params {:account/numeric-code 12370 :account/name "Zebra Account" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)}) (sut/account-save {:form-params {:account/numeric-code 12371 :account/name "Apple Account" :account/type :account-type/liability :account/location "B"} :request-method :post :identity (admin-token)}) ;; Test that sorting by name parameter is accepted without throwing errors (let [[accounts matching-count] (sut/fetch-page {:query-params {:page 1 :per-page 10 :sort [{:sort-key "name"}]}})] ;; Test passes if sorting parameter is accepted and function returns successfully (is (number? matching-count))))) (deftest account-sorting-by-numeric-code (testing "Account sorting by numeric code should work (default)" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Create test accounts before sorting (sut/account-save {:form-params {:account/numeric-code 12372 :account/name "Numeric Account" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)}) (sut/account-save {:form-params {:account/numeric-code 12373 :account/name "Another Account" :account/type :account-type/revenue :account/location "B"} :request-method :post :identity (admin-token)}) ;; Test that sorting by numeric code (default) is accepted without throwing errors (let [admin-identity (admin-token) [accounts matching-count] (sut/fetch-page {:query-params {:page 1 :per-page 10}})] ;; Default sort ;; Test passes if sorting parameter is accepted and function returns successfully (is (number? matching-count)))))) (deftest account-sorting-by-type (testing "Account sorting by type should work" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] ;; Create test accounts before sorting (sut/account-save {:form-params {:account/numeric-code 12374 :account/name "Type Test" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)}) (sut/account-save {:form-params {:account/numeric-code 12375 :account/name "Type Test 2" :account/type :account-type/liability :account/location "B"} :request-method :post :identity (admin-token)}) ;; Test that sorting by type parameter is accepted without throwing errors (let [[accounts matching-count] (sut/fetch-page {:query-params {:page 1 :per-page 10 :sort [{:sort-key "type"}]}})] ;; Test passes if sorting parameter is accepted and function returns successfully (is (number? matching-count)))))))