Add 8 BDD-style tests for the vendors module covering grid/list operations and vendor merge functionality. Tests follow established patterns from accounts_test.clj and include proper database verification. Tests Implemented: - vendor-grid-loads-with-empty-database - vendor-fetch-ids-returns-correct-structure - vendor-fetch-page-returns-vendors - vendor-hydrate-results-works - vendor-merge-transfers-references - vendor-merge-same-vendor-rejected - vendor-merge-invalid-vendor-handled - vendor-hydration-includes-all-fields Key Implementation Details: - Uses setup-test-data helper with unique temp IDs - Tests focus on public interface (fetch-page, merge-submit) - Follows BDD Given/When/Then pattern - All 8 tests passing (26 assertions) Documentation: - Created implementation plan in docs/plans/ - Documented solution patterns in docs/solutions/ - Created code review todos for future improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
26 KiB
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:
- Unclear test intent: Tests verify database state directly without clear behavioral descriptions
- Difficulty for new developers: BDD scenarios provide clearer user flow documentation
- Limited edge case coverage: Current tests may miss important business logic edge cases
- 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/pullanddc/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-tokenutility) - 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 testselector for running
Quality Gates
- Test coverage for account creation flow > 80%
- All tests pass on initial run
- Tests run with
lein test :integrationandlein 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
-
Time Risk: Comprehensive BDD test coverage may take longer than estimated
- Mitigation: Focus on critical tests first, add edge cases in follow-up PRs
-
Complexity Risk: Solr indexing may be difficult to test in isolation
- Mitigation: Use mocked Solr client with in-memory index
-
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
-
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:
-
Review existing account creation code
- Read
src/clj/auto_ap/ssr/admin/accounts.clj - Identify
account-savefunction and validation logic - Identify duplicate check logic
- Identify Solr indexing logic
- Read
-
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-datautilities - Review test structure and conventions
- Read
-
Create test directory structure
- Create
test/clj/auto_ap/ssr/admin/directory if not exists - Verify namespace conventions and naming
- Create
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
-
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)
- Create
-
Implement Test 1: Admin creates account successfully
(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))))))) -
Verify Test 1 passes
- Run
lein test auto-ap.ssr.admin.accounts-test/account-creation-success - Fix any failures
- Verify test output is clear
- Run
Deliverable:
- Test 1 passes successfully
- Basic test framework in place
Task 2: Account Database Verification Test
-
Implement Test 2: Account appears in database
(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))))))) -
Implement helper function for cleanup
- Create
setup-account-with-codehelper function - Create teardown logic to remove test accounts
- Use test fixture for automatic cleanup
- Create
-
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
-
Implement Test 3: Empty name validation error
(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))))))) -
Implement Test 4: Invalid account type validation error
(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))))))) -
Implement Test 5: Numeric code format validation
- Test negative numbers
- Test non-numeric characters
- Test leading zeros
- Test very long codes
-
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
-
Implement helper to create test account
(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"}])) -
Implement Test 6: Duplicate numeric code detection
(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))))))) -
Implement Test 7: Duplicate account name detection
(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))))))) -
Implement Test 8: Case-insensitive duplicate detection
- Test "Cash" vs "cash" duplicates
- Test "CASH" vs "Cash" duplicates
- Verify case-sensitivity handling
-
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
-
Mock Solr client for testing
(with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))] (let [result @(sut/account-save {...})] ;; Test Solr index) -
Implement Test 9: Account appears in Solr search
(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))))))) -
Implement Test 10: Can search by numeric code
- Test searching by numeric code
- Verify exact match
- Test search returns correct account
-
Implement Test 11: Solr index update on account update
- Create account
- Update account
- Verify Solr index contains updated data
- Verify old data removed
-
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
-
Implement Test 12: Non-admin access is denied
(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))))))) -
Implement Test 13: Admin with invalid token
- Test expired token
- Test malformed token
- Test missing token
- Verify proper error handling
-
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
-
Implement Test 14: Client override validation
- Test duplicate client names in overrides
- Test empty overrides
- Test too many overrides
- Test invalid client references
-
Implement Test 15: Account name edge cases
- Test special characters
- Test unicode characters
- Test extremely long names
- Test names with leading/trailing spaces
-
Implement Test 16: Numeric code edge cases
- Test very long codes (near database limit)
- Test zero
- Test decimal numbers
- Test codes with spaces
-
Implement Test 17: Transaction rollback on Solr failure
- Simulate Solr failure
- Verify Datomic transaction is rolled back
- Verify no partial data created
-
Implement Test 18: Concurrent account creation
- Test two admins creating accounts simultaneously
- Verify no duplicate code/name conflicts
- Test race condition handling
-
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
-
Run linting
lein cljfmt check -
Fix formatting issues
lein cljfmt fix -
Verify no syntax errors
- Run
lein checkorlein testto catch any issues
- Run
-
Add test comments explaining BDD Given-When-Then flow
- Document purpose of each test
- Explain assumptions
- Note any test limitations
-
Organize tests by feature
- Group related tests together
- Use clear headings
- Add docstrings
-
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
-
Run full test suite
lein test -
Run integration tests only
lein test :integration -
Run functional tests only
lein test :functional -
Fix any failing tests
- Analyze test failures
- Fix implementation or test
- Re-run until all tests pass
-
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
-
Review test code quality
- Check naming conventions
- Verify test isolation
- Ensure proper cleanup
-
Document test patterns
- Note common test utilities used
- Document testing conventions
- Add examples for future tests
-
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-savefunction - Form schema validation logic
- Duplicate check implementation
- Solr indexing logic
- Main
-
Test Utilities:
test/clj/auto_ap/integration/util.clj:1-117wrap-setup- Test database setup/teardownadmin-token- Admin authentication token creationsetup-test-data- Common test data creationtest-account- Helper for account test data
-
Existing SSR Tests:
test/clj/auto_ap/ssr/invoice/new_invoice_wizard_test.clj- SSR test patternstest/clj/auto_ap/ssr/ledger_test.clj- Comprehensive test examplestest/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
- Test structure and patterns
- Fixtures and setup/teardown
- Assertions and test organization
-
Datomic API: datomic.api documentation
- Database queries (
dc/q,dc/pull) - Transaction operations
- Entity manipulation
- Database queries (
-
BDD in Clojure: 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:
- User-Observable Behavior: Tests verify database state changes, not implementation details
- Given-When-Then Structure: Tests document behavioral intent clearly
- Test Utilities: Leverage existing
wrap-setup,admin-token,setup-test-data - Database Verification: Use
dc/pullanddc/qto verify state after operations - Isolation: Each test has proper setup and teardown
- Clarity: Test names are descriptive and clear about intent
- 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
- Review Plan: Confirm scope and detail level
- Run Deepen Research: Optionally enhance with best practices and performance analysis
- Start Implementation: Begin with Phase 1 and iterate through phases
- Code Review: Get feedback on test implementation
- Iterate: Refine tests based on feedback