--- title: feat: Add BDD tests for admin financial account creation (ssr) type: feat date: 2026-02-06 --- # Add BDD Tests for Admin Financial Account Creation (SSR) ## Overview This feature aims to add Behavior-Driven Development (BDD) tests for admin users creating financial accounts using Server-Side Rendering (SSR). The tests will cover the complete account creation flow, validation logic, duplicate detection, and Solr indexing integration. ## Problem Statement Currently, the project lacks BDD-style tests for admin financial account creation. Tests exist using Clojure.test but follow a unit/integration pattern rather than BDD Given-When-Then scenarios. This creates several challenges: 1. **Unclear test intent**: Tests verify database state directly without clear behavioral descriptions 2. **Difficulty for new developers**: BDD scenarios provide clearer user flow documentation 3. **Limited edge case coverage**: Current tests may miss important business logic edge cases 4. **No validation testing**: Duplicate detection and validation logic lacks comprehensive test coverage ## Proposed Solution Implement BDD-style tests for admin financial account creation following project conventions. Tests will: - Use Given-When-Then structure for clear behavioral descriptions - Test both success and failure scenarios - Verify database state changes after operations - Test Solr indexing after account creation - Test authorization (admin-only access) - Cover validation errors and duplicate detection ## Technical Considerations ### Architecture Impact - Tests will be added to `test/clj/auto_ap/ssr/admin/accounts_test.clj` - Tests will use existing test utilities: `wrap-setup`, `admin-token`, `setup-test-data` - Tests will verify database state using Datomic `dc/pull` and `dc/q` - Tests will follow project convention of testing user-observable behavior ### Performance Implications - Tests will use in-memory Datomic for fast iteration - Each test will run independently with its own setup/teardown - Solr indexing will be tested in-memory (using mocked Solr client) ### Security Considerations - Tests will use admin tokens (`admin-token` utility) - Non-admin access attempts will be tested and rejected - JWT validation will be tested for proper authorization ## Acceptance Criteria ### Functional Requirements - [ ] Test 1: Admin successfully creates account with valid data - Given: Admin is logged in with valid token - When: Admin submits valid account creation form - Then: Account is created successfully in database - Then: Account appears in account table - Then: Account is indexed in Solr search - [ ] Test 2: Account appears in database with correct attributes - Given: Account was created - When: Account is queried from database - Then: Account has correct numeric code, name, type, location - Then: Account has auto-generated ID - Then: Account timestamps are set correctly - [ ] Test 3: Validation errors for invalid data - Given: Admin submits invalid account form - When: Form validation fails - Then: Appropriate validation error is shown - Then: No account is created - [ ] Test 4: Duplicate numeric code detection - Given: Account with same numeric code already exists - When: Admin submits form with duplicate code - Then: Duplicate check fails - Then: Validation error is shown - Then: No account is created - [ ] Test 5: Duplicate account name detection - Given: Account with same name already exists - When: Admin submits form with duplicate name - Then: Duplicate check fails - Then: Validation error is shown - Then: No account is created - [ ] Test 6: Account searchability in Solr - Given: Account was created - When: Solr query is performed - Then: Account appears in search results - Then: Can search by numeric code - Then: Can search by account name - [ ] Test 7: Non-admin access is denied - Given: Non-admin user token - When: Non-admin attempts to create account - Then: Request is rejected with 403 Forbidden - Then: No account is created - [ ] Test 8: Client override validation - Given: Account creation form includes client overrides - When: Overrides contain duplicate client names - Then: Validation error is shown - Then: No account is created - [ ] Test 9: Account update functionality - Given: Account exists in database - When: Admin updates account attributes - Then: Account is updated successfully - Then: Solr index is updated - Then: Account in table reflects changes ### Non-Functional Requirements - [ ] Tests use existing test utilities (`wrap-setup`, `admin-token`, etc.) - [ ] Tests follow project BDD style conventions - [ ] Tests verify user-observable behavior (database state) - [ ] Tests are isolated with proper setup/teardown - [ ] Test execution time < 5 seconds per test - [ ] Tests use `lein test` selector for running ### Quality Gates - [ ] Test coverage for account creation flow > 80% - [ ] All tests pass on initial run - [ ] Tests run with `lein test :integration` and `lein test :functional` - [ ] Test file follows project naming conventions (`auto-ap.ssr.admin.accounts-test`) - [ ] Code formatting verified with `lein cljfmt check` ## Success Metrics - [ ] 9 BDD test scenarios implemented and passing - [ ] Clear Given-When-Then documentation for each test - [ ] Tests cover happy path, validation errors, and edge cases - [ ] No regression in existing account creation functionality - [ ] Tests provide clear documentation for developers - [ ] Tests can be run in parallel without conflicts ## Dependencies & Risks ### Prerequisites - Existing Datomic database schema for accounts - Existing SSR admin module (`src/clj/auto_ap/ssr/admin/accounts.clj`) - Existing test utilities in `test/clj/auto_ap/integration/util.clj` - In-memory Solr client for testing ### Potential Risks 1. **Time Risk**: Comprehensive BDD test coverage may take longer than estimated - Mitigation: Focus on critical tests first, add edge cases in follow-up PRs 2. **Complexity Risk**: Solr indexing may be difficult to test in isolation - Mitigation: Use mocked Solr client with in-memory index 3. **Regression Risk**: New tests may fail due to changes in production code - Mitigation: Run full test suite after each test implementation - Mitigation: Use feature flags for test environment 4. **Duplicate Detection Complexity**: Duplicate check logic may have edge cases - Mitigation: Review existing implementation, add targeted tests for each edge case ## Implementation Plan ### Phase 1: Foundation (1-2 hours) **Tasks:** 1. [ ] Review existing account creation code - Read `src/clj/auto_ap/ssr/admin/accounts.clj` - Identify `account-save` function and validation logic - Identify duplicate check logic - Identify Solr indexing logic 2. [ ] Review existing test patterns - Read `test/clj/auto_ap/ssr/invoice/new_invoice_wizard_test.clj` - Read `test/clj/auto_ap/integration/graphql/accounts.clj` - Understand `wrap-setup`, `admin-token`, `setup-test-data` utilities - Review test structure and conventions 3. [ ] Create test directory structure - Create `test/clj/auto_ap/ssr/admin/` directory if not exists - Verify namespace conventions and naming **Deliverable:** - Clear understanding of account creation flow - Test file template created - Setup environment ready ### Phase 2: Core Tests (3-4 hours) **Task 1: Account Creation Success Test** 4. [ ] Create basic test structure - Create `test/clj/auto_ap/ssr/admin/accounts_test.clj` - Define namespace with required imports - Set up test fixtures (`wrap-setup`, `admin-token`) 5. [ ] Implement Test 1: Admin creates account successfully ```clojure (deftest account-creation-success (testing "Admin should be able to create a new financial account" ;; Given: Admin is logged in (let [admin-identity (admin-token)] ;; When: Admin submits valid account creation form (let [form-params {:account/numeric-code 12345 :account/name "New Cash Account" :account/type :account-type/asset :account/location "B" :account/default-allowance :allowance/allowed} result (sut/account-save {:form-params form-params :request-method :post :identity admin-identity})] ;; Then: Account should be created successfully (is (= :success (:status result))) ;; And: Account should appear in database (let [db-after (dc/db conn) created-account (dc/pull db-after '[:db/id :account/code :account/name :account/numeric-code :account/location :account/type {[:account/type :xform iol-ion.query/ident] :db/ident}] (get result [:tempids "new"]))] (is (= 12345 (:account/numeric-code created-account))) (is (= "New Cash Account" (:account/name created-account))))))) ``` 6. [ ] Verify Test 1 passes - Run `lein test auto-ap.ssr.admin.accounts-test/account-creation-success` - Fix any failures - Verify test output is clear **Deliverable:** - Test 1 passes successfully - Basic test framework in place **Task 2: Account Database Verification Test** 7. [ ] Implement Test 2: Account appears in database ```clojure (deftest account-appears-in-database (testing "Created account should have correct attributes in database" (let [admin-identity (admin-token) form-params {:account/numeric-code 12346 :account/name "Cash Account" :account/type :account-type/asset :account/location "C"} result @(sut/account-save {:form-params form-params :request-method :post :identity admin-identity})] ;; Then: Account has correct attributes (let [db-after (dc/db conn) account-id (get result [:tempids "new"]) account (dc/pull db-after '[:db/id :account/code :account/name :account/numeric-code :account/location :account/type] account-id)] (is (= "Cash Account" (:account/name account))) (is (= 12346 (:account/numeric-code account))) (is (= "C" (:account/location account))))))) ``` 8. [ ] Implement helper function for cleanup - Create `setup-account-with-code` helper function - Create teardown logic to remove test accounts - Use test fixture for automatic cleanup 9. [ ] Verify Test 2 passes - Run test - Fix failures - Test cleanup works correctly **Deliverable:** - Test 2 passes successfully - Cleanup helper functions implemented **Task 3: Validation Error Tests** 10. [ ] Implement Test 3: Empty name validation error ```clojure (deftest account-creation-validation-error-empty-name (testing "Should show validation error when name is empty" (let [admin-identity (admin-token) form-params {:account/numeric-code 12347 :account/name "" :account/type :account-type/asset} result (sut/account-save {:form-params form-params :request-method :post :identity admin-identity})] (is (not= :success (:status result))) (is (some #(str/includes? (first %) "Name") (:form-errors result))))))) ``` 11. [ ] Implement Test 4: Invalid account type validation error ```clojure (deftest account-creation-validation-error-invalid-type (testing "Should show validation error when account type is invalid" (let [admin-identity (admin-token) form-params {:account/numeric-code 12348 :account/name "Test Account" :account/type :account-type/invalid-type} result (sut/account-save {:form-params form-params :request-method :post :identity admin-identity})] (is (not= :success (:status result))) (is (some #(str/includes? (first %) "Type") (:form-errors result))))))) ``` 12. [ ] Implement Test 5: Numeric code format validation - Test negative numbers - Test non-numeric characters - Test leading zeros - Test very long codes 13. [ ] Verify validation tests pass - Run each validation test - Fix failures - Verify error messages are clear **Deliverable:** - Tests 3, 4, 5 pass successfully - Validation error scenarios covered **Task 4: Duplicate Detection Tests** 14. [ ] Implement helper to create test account ```clojure (defn setup-account-with-code [code name type] (setup-test-data [{:db/id "existing-account-1" :account/numeric-code code :account/account-set "default" :account/name name :account/type type :account/location "A"}])) ``` 15. [ ] Implement Test 6: Duplicate numeric code detection ```clojure (deftest account-creation-duplicate-code (testing "Should detect and reject duplicate numeric code" (let [admin-identity (admin-token) _ (setup-account-with-code 12345 "Existing Account" :account-type/asset) form-params {:account/numeric-code 12345 :account/name "New Account" :account/type :account-type/asset} result (sut/account-save {:form-params form-params :request-method :post :identity admin-identity})] (is (not= :success (:status result))) (is (some #(str/includes? (first %) "code") (:form-errors result))) ;; Verify no new account was created (let [db-after (dc/db conn) accounts (dc/q '[:find ?e :where [?e :account/numeric-code 12345]] db-after)] (is (= 1 (count accounts))))))) ``` 16. [ ] Implement Test 7: Duplicate account name detection ```clojure (deftest account-creation-duplicate-name (testing "Should detect and reject duplicate account name" (let [admin-identity (admin-token) _ (setup-account-with-code 12346 "Cash Account" :account-type/asset) form-params {:account/numeric-code 12347 :account/name "Cash Account" :account/type :account-type/asset} result (sut/account-save {:form-params form-params :request-method :post :identity admin-identity})] (is (not= :success (:status result))) (is (some #(str/includes? (first %) "name") (:form-errors result))) ;; Verify no new account was created (let [db-after (dc/db conn) accounts (dc/q '[:find ?e :where [?e :account/name "Cash Account"]] db-after)] (is (= 1 (count accounts))))))) ``` 17. [ ] Implement Test 8: Case-insensitive duplicate detection - Test "Cash" vs "cash" duplicates - Test "CASH" vs "Cash" duplicates - Verify case-sensitivity handling 18. [ ] Verify duplicate detection tests pass - Run each duplicate test - Fix failures - Verify no account created on duplicates **Deliverable:** - Tests 6, 7, 8 pass successfully - Duplicate detection logic thoroughly tested **Task 5: Solr Indexing Tests** 19. [ ] Mock Solr client for testing ```clojure (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] (let [result @(sut/account-save {...})] ;; Test Solr index) ``` 20. [ ] Implement Test 9: Account appears in Solr search ```clojure (deftest account-creation-solr-indexing (testing "Created account should be searchable in Solr" (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] (let [admin-identity (admin-token) form-params {:account/numeric-code 12349 :account/name "Solr Test Account" :account/type :account-type/asset} result @(sut/account-save {:form-params form-params :request-method :post :identity admin-identity})] ;; Then: Account should be indexed in Solr (let [solr (auto-ap.solr/->InMemSolrClient (atom {})) search-results (solr/query {:query "Solr Test Account"})] (is (> (count search-results) 0))))))) ``` 21. [ ] Implement Test 10: Can search by numeric code - Test searching by numeric code - Verify exact match - Test search returns correct account 22. [ ] Implement Test 11: Solr index update on account update - Create account - Update account - Verify Solr index contains updated data - Verify old data removed 23. [ ] Verify Solr indexing tests pass - Run each Solr test - Fix failures - Verify index operations work correctly **Deliverable:** - Tests 9, 10, 11 pass successfully - Solr indexing thoroughly tested ### Phase 3: Authorization & Edge Cases (2-3 hours) **Task 6: Authorization Tests** 24. [ ] Implement Test 12: Non-admin access is denied ```clojure (deftest account-creation-non-admin-access-denied (testing "Non-admin users should not be able to create accounts" (let [user-identity {:user "TEST USER" :user/role "user" :user/name "TEST USER"} form-params {:account/numeric-code 12350 :account/name "Unauthorized Account" :account/type :account-type/asset} result (sut/account-save {:form-params form-params :request-method :post :identity user-identity})] (is (not= :success (:status result))) ;; Should return 403 or error (is (some #(str/includes? (first %) "not authorized") (:form-errors result))))))) ``` 25. [ ] Implement Test 13: Admin with invalid token - Test expired token - Test malformed token - Test missing token - Verify proper error handling 26. [ ] Verify authorization tests pass - Run each authorization test - Fix failures - Verify security constraints **Deliverable:** - Tests 12, 13 pass successfully - Authorization thoroughly tested **Task 7: Edge Cases** 27. [ ] Implement Test 14: Client override validation - Test duplicate client names in overrides - Test empty overrides - Test too many overrides - Test invalid client references 28. [ ] Implement Test 15: Account name edge cases - Test special characters - Test unicode characters - Test extremely long names - Test names with leading/trailing spaces 29. [ ] Implement Test 16: Numeric code edge cases - Test very long codes (near database limit) - Test zero - Test decimal numbers - Test codes with spaces 30. [ ] Implement Test 17: Transaction rollback on Solr failure - Simulate Solr failure - Verify Datomic transaction is rolled back - Verify no partial data created 31. [ ] Implement Test 18: Concurrent account creation - Test two admins creating accounts simultaneously - Verify no duplicate code/name conflicts - Test race condition handling 32. [ ] Verify edge case tests pass - Run each edge case test - Fix failures - Document any limitations **Deliverable:** - Tests 14, 15, 16, 17, 18 pass successfully - Edge cases thoroughly tested ### Phase 4: Refinement & Documentation (1-2 hours) **Task 8: Code Quality** 33. [ ] Run linting ```bash lein cljfmt check ``` 34. [ ] Fix formatting issues ```bash lein cljfmt fix ``` 35. [ ] Verify no syntax errors - Run `lein check` or `lein test` to catch any issues 36. [ ] Add test comments explaining BDD Given-When-Then flow - Document purpose of each test - Explain assumptions - Note any test limitations 37. [ ] Organize tests by feature - Group related tests together - Use clear headings - Add docstrings 38. [ ] Update test documentation - Document test utilities used - Explain test setup and teardown - Add reference to source code **Deliverable:** - Code formatted and linted - Well-documented tests - Clear test structure **Task 9: Integration Testing** 39. [ ] Run full test suite ```bash lein test ``` 40. [ ] Run integration tests only ```bash lein test :integration ``` 41. [ ] Run functional tests only ```bash lein test :functional ``` 42. [ ] Fix any failing tests - Analyze test failures - Fix implementation or test - Re-run until all tests pass 43. [ ] Verify test performance - Check execution time - Identify slow tests - Optimize if necessary **Deliverable:** - All tests pass - Tests run in acceptable time (< 2 minutes for full suite) **Task 10: Code Review Preparation** 44. [ ] Review test code quality - Check naming conventions - Verify test isolation - Ensure proper cleanup 45. [ ] Document test patterns - Note common test utilities used - Document testing conventions - Add examples for future tests 46. [ ] Create summary documentation - List all tests implemented - Explain test coverage - Document any test limitations - Provide guidance for running tests **Deliverable:** - Clean, maintainable test code - Comprehensive test documentation - Ready for code review ## References & Research ### Internal References - **Account Creation Source**: `src/clj/auto_ap/ssr/admin/accounts.clj:196-49` - Main `account-save` function - Form schema validation logic - Duplicate check implementation - Solr indexing logic - **Test Utilities**: `test/clj/auto_ap/integration/util.clj:1-117` - `wrap-setup` - Test database setup/teardown - `admin-token` - Admin authentication token creation - `setup-test-data` - Common test data creation - `test-account` - Helper for account test data - **Existing SSR Tests**: - `test/clj/auto_ap/ssr/invoice/new_invoice_wizard_test.clj` - SSR test patterns - `test/clj/auto_ap/ssr/ledger_test.clj` - Comprehensive test examples - `test/clj/auto_ap/integration/graphql/accounts.clj` - Integration test patterns - **Testing Conventions**: `.claude/skills/testing-conventions/SKILL.md` - Core principle: Test user-observable behavior - Database testing patterns - Test fixture usage - Helper function recommendations ### External References - **Clojure Testing**: [clojure.test documentation](https://clojure.org/guides/testing) - Test structure and patterns - Fixtures and setup/teardown - Assertions and test organization - **Datomic API**: [datomic.api documentation](https://docs.datomic.com/free/pro/api/datomic/api.html) - Database queries (`dc/q`, `dc/pull`) - Transaction operations - Entity manipulation - **BDD in Clojure**: [Cucumber Clojure](https://github.com/cucumber/clojure) (if needed) - If BDD framework is adopted - Alternative to Clojure.test patterns ### Related Work - **Previous Account Tests**: `test/clj/auto_ap/integration/graphql/accounts.clj` (215 lines) - Existing account-related tests for reference - GraphQL API patterns that may apply - **Admin Tests**: None found (admin functionality less tested) - This feature will be first comprehensive admin test suite - Opportunity to establish admin testing patterns ## Testing Conventions Applied Following project conventions: 1. **User-Observable Behavior**: Tests verify database state changes, not implementation details 2. **Given-When-Then Structure**: Tests document behavioral intent clearly 3. **Test Utilities**: Leverage existing `wrap-setup`, `admin-token`, `setup-test-data` 4. **Database Verification**: Use `dc/pull` and `dc/q` to verify state after operations 5. **Isolation**: Each test has proper setup and teardown 6. **Clarity**: Test names are descriptive and clear about intent 7. **Documentation**: Test comments explain BDD flow and assumptions ## Success Criteria Summary - ✅ 18 BDD test scenarios implemented and passing - ✅ Clear Given-When-Then documentation for each test - ✅ Tests cover happy path, validation errors, duplicates, Solr, authorization, edge cases - ✅ No regression in existing account creation functionality - ✅ Tests provide clear behavioral documentation for developers - ✅ Tests run in parallel without conflicts - ✅ Code formatted and linted - ✅ Full test suite passes ## Next Steps 1. **Review Plan**: Confirm scope and detail level 2. **Run Deepen Research**: Optionally enhance with best practices and performance analysis 3. **Start Implementation**: Begin with Phase 1 and iterate through phases 4. **Code Review**: Get feedback on test implementation 5. **Iterate**: Refine tests based on feedback