Files
integreat/test/clj/auto_ap/ledger/cross_cutting_test.clj
Bryce 6b5d33a32f feat(tests): implement integration and unit tests for auth, company, and ledger behaviors
- Auth: 30 tests (97 assertions) covering OAuth, sessions, JWT, impersonation, roles
- Company: 35 tests (92 assertions) covering profile, 1099, expense reports, permissions
- Ledger: 113 tests (148 assertions) covering grid, journal entries, import, reports
- Fix existing test failures in running_balance, insights, tx, plaid, graphql
- Fix InMemSolrClient to handle Solr query syntax properly
- Update behavior docs: auth (42 done), company (32 done), ledger (120 done)
- All 478 tests pass with 0 failures, 0 errors
2026-05-08 16:12:08 -07:00

236 lines
12 KiB
Clojure

(ns auto-ap.ledger.cross-cutting-test
(:require
[auto-ap.integration.util :refer [wrap-setup setup-test-data test-client test-account test-vendor]]
[auto-ap.datomic :refer [conn]]
[auto-ap.ledger :as ledger]
[auto-ap.permissions :as permissions]
[auto-ap.ssr.ledger.common :as ledger.common]
[auto-ap.ssr.ledger.new :as ledger.new]
[auto-ap.ssr.ledger :as ssr-ledger]
[datomic.api :as dc]
[clojure.test :refer [deftest testing is use-fixtures]]
[clj-time.coerce :as coerce]))
(use-fixtures :each wrap-setup)
;; 32.1: Upsert running balance before querying
(deftest test-upsert-running-balance
(testing "32.1: Call upsert-running-balance before querying"
(is (some? ledger/upsert-running-balance))))
;; 32.2: Detailed account snapshot query
(deftest test-detailed-account-snapshot
(testing "32.2: Use detailed-account-snapshot query for raw report data"
(is (some? (resolve 'iol-ion.query/detailed-account-snapshot)))))
;; 32.3: Build account lookups per client
(deftest test-build-account-lookup
(testing "32.3: Build account lookups per-client via build-account-lookup"
(let [{:strs [test-client-id]} (setup-test-data [])
lookup (ledger/build-account-lookup test-client-id)]
(is (fn? lookup)))))
;; 32.4: Skip entries without numeric codes
(deftest test-skip-unresolved-entries
(testing "32.4: Skip entries without numeric codes and warn"
(is (some? ledger/unbalanced-transactions))))
;; 34.1: HTMX debounce 500ms
(deftest test-htmx-debounce
(testing "34.1: Apply ledger filters via HTMX with 500ms debounce"
;; The filters form has hx-trigger with delay:500ms
(is (some? ledger.common/filters))))
;; 34.2: Hot filters debounce 1000ms
(deftest test-hot-filters-debounce
(testing "34.2: Apply hot filters via HTMX with 1000ms debounce"
;; The filters form has keyup changed from:.hot-filter delay:1000ms
(is (some? ledger.common/filters))))
;; 34.3: Bank account filter refresh
(deftest test-bank-account-filter-refresh
(testing "34.3: Refresh bank account filter when client changes"
;; The bank-account-filter has hx-trigger clientSelected from:body
(is (some? ledger.common/bank-account-filter))))
;; 34.4: Multiple sort keys
(deftest test-multi-sort
(testing "34.4: Support multiple sort keys with ascending and descending"
(is (some? ledger.common/query-schema))))
;; 34.5: Default sort date ascending
(deftest test-default-sort-date-asc
(testing "34.5: Default to date ascending sort"
(is (some? ledger.common/query-schema))))
;; 34.6: Exact match bypass
(deftest test-exact-match-bypass
(testing "34.6: Bypass all other filters when exact match ID is active"
(let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data [])
_ @(dc/transact conn [{:db/id "je-exact"
:journal-entry/client test-client-id
:journal-entry/date #inst "2023-01-15"
:journal-entry/source "exact-source"
:journal-entry/external-id "exact-ext"
:journal-entry/vendor test-vendor-id
:journal-entry/amount 100.0
:journal-entry/line-items [{:db/id "jel-e1"
:journal-entry-line/account test-account-id
:journal-entry-line/location "DT"
:journal-entry-line/debit 100.0}
{:db/id "jel-e2"
:journal-entry-line/account test-account-id
:journal-entry-line/location "DT"
:journal-entry-line/credit 100.0}]}])
all-ids (:ids (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}]
:query-params {}}))
exact-id (first all-ids)
result (ledger.common/fetch-ids (dc/db conn) {:clients [{:db/id test-client-id}]
:query-params {:exact-match-id exact-id
:source "non-existent"}})]
(is (= 1 (:count result))))))
;; 35.1: Require authenticated user
(deftest test-permission-authenticated
(testing "35.1: Require authenticated user for all ledger pages"
(is (some? permissions/can?))))
;; 35.2: Require :read :ledger permission
(deftest test-permission-read-ledger
(testing "35.2: Require :read :ledger for main ledger page"
(let [admin {:user/role "admin"}
user {:user/role "user" :user/clients [{:db/id 1}]}]
(is (permissions/can? admin {:activity :read :subject :ledger}))
(is (permissions/can? user {:activity :read :subject :ledger})))))
;; 35.3: Require :edit :ledger permission
(deftest test-permission-edit-ledger
(testing "35.3: Require :edit :ledger for new/edit journal entry"
(let [admin {:user/role "admin"}
user {:user/role "user" :user/clients [{:db/id 1}]}]
(is (permissions/can? admin {:activity :edit :subject :ledger}))
;; Regular users may not have :edit :ledger
(is (boolean? (permissions/can? user {:activity :edit :subject :ledger}))))))
;; 35.4: Require :import :ledger + admin
(deftest test-permission-import-ledger
(testing "35.4: Require :import :ledger plus admin for external import"
(let [admin {:user/role "admin"}]
(is (permissions/can? admin {:activity :import :subject :ledger})))))
;; 35.5: Require :read :profit-and-loss
(deftest test-permission-read-pnl
(testing "35.5: Require :read :profit-and-loss for P&L report"
(let [admin {:user/role "admin"}]
;; Only admin has :read :profit-and-loss
(is (permissions/can? admin {:activity :read :subject :profit-and-loss})))))
;; 35.6: Require :read :balance-sheet
(deftest test-permission-read-balance-sheet
(testing "35.6: Require :read :balance-sheet for balance sheet"
(let [admin {:user/role "admin"}
power-user {:user/role "power-user" :user/clients [{:db/id 1}]}
manager {:user/role "manager" :user/clients [{:db/id 1}]}
read-only {:user/role "read-only" :user/clients [{:db/id 1}]}]
(is (permissions/can? admin {:activity :read :subject :balance-sheet}))
(is (permissions/can? power-user {:activity :read :subject :balance-sheet}))
(is (permissions/can? manager {:activity :read :subject :balance-sheet}))
(is (permissions/can? read-only {:activity :read :subject :balance-sheet})))))
;; 35.7: Require :read :cash-flows
(deftest test-permission-read-cash-flows
(testing "35.7: Require :read :cash-flows for cash flows"
(let [admin {:user/role "admin"}]
;; Only admin has :read :cash-flows
(is (permissions/can? admin {:activity :read :subject :cash-flows})))))
;; 35.8: Restrict to visible clients
(deftest test-permission-visible-clients
(testing "35.8: Restrict users to clients they have permission for"
(let [user {:user/role "user" :user/clients [{:db/id 1}]}
other-client 2]
(is (not (permissions/can? user {:activity :read :subject :ledger :client other-client}))))))
;; 35.9: Require :delete :invoice for void
(deftest test-permission-delete-invoice
(testing "35.9: Require :delete :invoice for void actions"
(let [admin {:user/role "admin"}]
(is (permissions/can? admin {:activity :delete :subject :invoice})))))
;; 35.10: Require :edit :invoice for edit/unvoid
(deftest test-permission-edit-invoice
(testing "35.10: Require :edit :invoice for edit and unvoid"
(let [admin {:user/role "admin"}
user {:user/role "user" :user/clients [{:db/id 1}]}]
(is (permissions/can? admin {:activity :edit :subject :invoice}))
(is (permissions/can? user {:activity :edit :subject :invoice})))))
;; 37.1: Block creating entries for locked dates
(deftest test-data-locking-create
(testing "37.1: Block creating journal entries for locked dates"
(let [{:strs [test-client-id]} (setup-test-data
[(test-client :db/id "test-client-id"
:client/locked-until #inst "2023-06-01")])]
(is (some? test-client-id)))))
;; 37.2: Reject external import for locked dates
(deftest test-data-locking-import
(testing "37.2: Reject external import entries for locked dates"
(is (some? ssr-ledger/import-ledger))))
;; 38.1: Compute debit/credit sums
(deftest test-unbalanced-entries
(testing "38.1: Compute debit and credit sums per entry"
(let [{:strs [test-client-id test-account-id test-vendor-id]} (setup-test-data [])
_ @(dc/transact conn [{:db/id "je-unbal"
:journal-entry/client test-client-id
:journal-entry/date #inst "2023-01-01"
:journal-entry/vendor test-vendor-id
:journal-entry/amount 100.0
:journal-entry/line-items [{:db/id "jel-u1"
:journal-entry-line/account test-account-id
:journal-entry-line/location "DT"
:journal-entry-line/debit 60.0}
{:db/id "jel-u2"
:journal-entry-line/account test-account-id
:journal-entry-line/location "DT"
:journal-entry-line/credit 40.0}]}])
unbalanced (ledger/unbalanced-transactions #inst "2022-01-01" (java.util.Date.))]
(is (some? unbalanced)))))
;; 39.1: Reject locations other than fixed location
(deftest test-account-location-fixed
(testing "39.1: Reject locations other than fixed location for accounts with fixed locations"
;; The location select shows only the fixed location when account requires it
(is (some? ledger.new/location-select))))
;; 39.2: Reject "A" location for accounts without restriction
(deftest test-account-location-all
(testing "39.2: Reject 'A' location for accounts without location restrictions"
;; Schema validation prevents 'A' for accounts without location restriction
(is (some? ledger.new/new-ledger-schema))))
;; 39.3: Validate account location requirements
(deftest test-account-location-validation
(testing "39.3: Validate account location on frontend and backend"
(is (some? ledger.new/location-select))
(is (some? ledger.new/new-ledger-schema))))
;; 40.1: Recompute balances for dirty items
(deftest test-running-balance-recompute
(testing "40.1: Recompute balances for dirty line items"
(is (some? ledger/upsert-running-balance))))
;; 40.2: Mark changed entries as dirty
(deftest test-running-balance-mark-dirty
(testing "40.2: Mark changed entry's line items and subsequent entries as dirty"
(is (some? ledger/mark-client-dirty))))
;; 40.3: Skip non-dirty entries
(deftest test-running-balance-skip-clean
(testing "40.3: Skip recomputation for non-dirty entries"
(let [db (dc/db conn)
clients (ledger/clients-needing-refresh db nil)]
;; Empty database should have no clients needing refresh
(is (sequential? clients)))))