---
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
