(ns auto-ap.test-server "Test server for browser automation tests (Playwright, etc.)" (:require [auto-ap.datomic :refer [conn transact-schema install-functions]] [auto-ap.handler :as handler] [auto-ap.integration.util :refer [setup-test-data test-client test-bank-account test-transaction test-payment test-invoice]] [auto-ap.routes.transactions :as route] [auto-ap.ssr.transaction.edit :as edit] [auto-ap.ssr.components.multi-modal :as mm] [auto-ap.ssr.utils :refer [wrap-entity wrap-schema-enforce]] [auto-ap.permissions :refer [wrap-must]] [auto-ap.datomic.transactions :as d-transactions] [auto-ap.datomic.accounts :as d-accounts] [auto-ap.session-version :as session-version] [datomic.api :as dc] [ring.adapter.jetty :refer [run-jetty]] [ring.middleware.edn :refer [wrap-edn-params]] [ring.middleware.multipart-params :as mp] [ring.middleware.params :refer [wrap-params]] [ring.middleware.session :refer [wrap-session]] [ring.middleware.session.cookie :refer [cookie-store]] [mount.core :as mount] [clj-time.core :as time] [buddy.sign.jwt :as jwt] [cheshire.core] [config.core :refer [env]])) (def test-identity-mode (atom :single-client)) (def test-transaction-id (atom nil)) (def test-account-ids (atom {})) (def test-client-ids (atom {})) (defn admin-identity [] (case @test-identity-mode :multi-client {:user "TEST ADMIN" :user/role "admin" :user/name "TEST ADMIN" :exp (time/plus (time/now) (time/days 1)) :user/clients [{:db/id (:test @test-client-ids) :client/code "TEST" :client/locations ["DT"]} {:db/id (:test2 @test-client-ids) :client/code "TEST2" :client/locations ["NY"]}]} ;; default single-client {:user "TEST ADMIN" :user/role "admin" :user/name "TEST ADMIN" :exp (time/plus (time/now) (time/days 1)) :user/clients [{:db/id (:test @test-client-ids) :client/code "TEST" :client/locations ["DT"]}]})) (defn wrap-test-auth [handler] (fn [request] (handler (assoc request :identity (admin-identity))))) (defn create-test-db [] (let [uri "datomic:mem://playwright-test"] (dc/delete-database uri) (dc/create-database uri) (let [test-conn (dc/connect uri)] ;; Must replace conn before install-functions since it uses the global var (alter-var-root #'auto-ap.datomic/conn (constantly test-conn)) (alter-var-root #'auto-ap.datomic/uri (constantly uri)) (transact-schema test-conn) (install-functions) test-conn))) (defn seed-test-data [conn] (let [tx-result @(dc/transact conn [(assoc (test-client :db/id "client-id" :client/code "TEST" :client/locations ["DT"]) :client/bank-accounts [(test-bank-account :db/id "bank-account-id")]) (test-client :db/id "client-id-2" :client/code "TEST2" :client/locations ["NY"]) {:db/id "account-id" :account/name "Test Account" :account/type :account-type/expense :account/numeric-code 50000 :account/applicability :account-applicability/global :account/default-allowance {:db/ident :allowance/allowed}} {:db/id "account-id-2" :account/name "Second Account" :account/type :account-type/expense :account/numeric-code 50001 :account/applicability :account-applicability/global :account/default-allowance {:db/ident :allowance/allowed}} {:db/id "account-id-fixed-loc" :account/name "Fixed Location Account" :account/type :account-type/expense :account/numeric-code 50002 :account/applicability :account-applicability/global :account/location "DT" :account/default-allowance {:db/ident :allowance/allowed}} {:db/id "ap-account-id" :account/name "Accounts Payable" :db/ident :account/accounts-payable :account/numeric-code 21000 :account/account-set "default" :account/applicability :account-applicability/global :account/default-allowance {:db/ident :allowance/allowed}} {:db/id "vendor-id" :vendor/name "Test Vendor" :vendor/default-account "account-id"} (test-transaction :db/id "transaction-id" :transaction/client "client-id" :transaction/bank-account "bank-account-id" :transaction/amount 100.0 :transaction/description-original "Test transaction" :transaction/memo "Monthly rent payment" :transaction/approval-status :transaction-approval-status/unapproved) (test-transaction :db/id "transaction-id-2" :transaction/client "client-id" :transaction/bank-account "bank-account-id" :transaction/amount 200.0 :transaction/description-original "Second transaction" :transaction/memo "Grocery shopping" :transaction/approval-status :transaction-approval-status/unapproved) (test-transaction :db/id "transaction-id-3" :transaction/client "client-id" :transaction/bank-account "bank-account-id" :transaction/amount 300.0 :transaction/description-original "Third transaction" :transaction/approval-status :transaction-approval-status/unapproved) ;; Transaction and payment for link testing (test-transaction :db/id "transaction-id-payment" :transaction/client "client-id" :transaction/bank-account "bank-account-id" :transaction/amount -100.0 :transaction/description-original "Transaction for payment link" :transaction/approval-status :transaction-approval-status/unapproved) (test-payment :db/id "payment-id" :payment/client "client-id" :payment/vendor "vendor-id" :payment/bank-account "bank-account-id" :payment/amount 100.0 :payment/status :payment-status/pending :payment/date #inst "2023-06-15") ;; Transaction and unpaid invoice for link testing (test-transaction :db/id "transaction-id-unpaid" :transaction/client "client-id" :transaction/bank-account "bank-account-id" :transaction/amount -150.0 :transaction/description-original "Transaction for unpaid invoice link" :transaction/approval-status :transaction-approval-status/unapproved) (test-transaction :db/id "transaction-id-feedback" :transaction/client "client-id" :transaction/bank-account "bank-account-id" :transaction/amount 400.0 :transaction/description-original "Transaction for feedback review" :transaction/approval-status :transaction-approval-status/requires-feedback) (test-invoice :db/id "invoice-unpaid-id" :invoice/client "client-id" :invoice/vendor "vendor-id" :invoice/total 150.0 :invoice/outstanding-balance 150.0 :invoice/status :invoice-status/unpaid :invoice/date #inst "2023-07-20" :invoice/invoice-number "UNPAID-001" :invoice/expense-accounts [{:invoice-expense-account/account "account-id" :invoice-expense-account/amount 150.0 :invoice-expense-account/location "DT"}])]) tempids (:tempids tx-result) tx-entity-id (get tempids "transaction-id")] (println "Test transaction entity ID:" tx-entity-id) (reset! test-account-ids {:test-account (get tempids "account-id") :second-account (get tempids "account-id-2") :fixed-location-account (get tempids "account-id-fixed-loc") :ap-account (get tempids "ap-account-id") :vendor (get tempids "vendor-id")}) (reset! test-client-ids {:test (get tempids "client-id") :test2 (get tempids "client-id-2")}) tx-entity-id)) (defn test-info-handler [request] {:status 200 :headers {"Content-Type" "application/json"} :body (cheshire.core/generate-string {:transactionId @test-transaction-id :accounts @test-account-ids :clientMode @test-identity-mode :clients (mapv :client/code (:clients request))})}) (defn test-set-client-mode-handler [request] (let [query-string (get request :query-string "") params (when (seq query-string) (into {} (for [param (clojure.string/split query-string #"&")] (let [[k v] (clojure.string/split param #"=")] [(keyword k) (java.net.URLDecoder/decode v "UTF-8")])))) mode (keyword (:mode params))] (reset! test-identity-mode mode) {:status 200 :headers {"Content-Type" "application/json"} :body (cheshire.core/generate-string {:mode mode})})) (defn wrap-test-info [handler] (fn [request] (cond (= "/test-info" (:uri request)) (test-info-handler request) (= "/test-set-client-mode" (:uri request)) (test-set-client-mode-handler request) :else (handler request)))) (defn test-app [] ;; Build app without auth middleware, inject test identity after all middleware (-> handler/route-handler (handler/wrap-hx-current-url-params) (handler/wrap-guess-route) (handler/wrap-logging) (handler/wrap-trim-clients) (handler/wrap-hydrate-clients) (handler/wrap-store-client-in-session) (handler/wrap-gunzip-jwt) ;; Skip wrap-authorization and wrap-authentication (session-version/wrap-session-version) (handler/wrap-idle-session-timeout) (wrap-session {:store (cookie-store {:key (byte-array [42, 52, -31, 101, -126, -33, -118, -69, -82, -59, -15, -69, -38, 103, -102, -1])})}) (wrap-params) (mp/wrap-multipart-params) (wrap-edn-params) (handler/wrap-error) wrap-test-auth wrap-test-info)) (defn start-test-server [] (let [test-conn (create-test-db) tx-id (seed-test-data test-conn)] (reset! test-transaction-id tx-id) (let [server (run-jetty (test-app) {:port 3333 :join? false})] (println "Test server started on http://localhost:3333") (println "Transaction entity ID:" tx-id) server))) (defn stop-test-server [server] (.stop server) (dc/delete-database "datomic:mem://playwright-test") (println "Test server stopped")) (defn -main [& _] (let [server (start-test-server)] (.addShutdownHook (Runtime/getRuntime) (Thread. #(stop-test-server server))) ;; Keep running @(promise)))