Unit Test XledgerExporter and DynamicsExporter
epic-accounting-system-export-engine-task-011 — Write unit tests for both XledgerExporter and DynamicsExporter. For each exporter: test field mapping correctness using fixture claim records, test validation errors for each required field when missing or malformed, test CSV/JSON payload structure matches target system specification, and test that the AccountingExporter interface contract is satisfied. Use flutter_test and mock dependencies for Chart of Accounts Mapper and File Generator.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
The most important tests are the validation tests — they document the contract between the exporter and its callers. For each required field, write a parameterised test that sets that field to null/invalid and asserts a specific ValidationError type is returned. Use Dart's test package's group() and test() functions to keep test organisation readable. For CSV payload tests, parse the output string into a list of maps (header row as keys) before asserting field values — this is more robust than positional assertions.
For JSON payload tests, use Dart's jsonDecode and assert on the decoded Map
Testing Requirements
Unit tests only (no integration tests in this task). Use flutter_test with mocktail for all dependency mocking. Organise tests into two files: xledger_exporter_test.dart and dynamics_exporter_test.dart. Each file contains groups: 'field mapping', 'validation', 'payload structure', 'interface contract'.
Use a shared test fixture factory (test/fixtures/claim_fixture.dart) to create consistent claim records. For payload structure tests, parse the generated CSV/JSON and assert each required field is present with the correct value — do not use string matching on the full payload. Aim for 100% branch coverage on both exporter classes.
The Xledger CSV/JSON import specification may not be available in full detail at implementation time. If the field format, column ordering, encoding requirements, or required fields differ from assumptions, the generated file will be rejected by Xledger on first production use.
Mitigation & Contingency
Mitigation: Obtain the official Xledger import specification document from Blindeforbundet before starting XledgerExporter implementation. Build a dedicated acceptance test that validates a sample export file against all documented constraints.
Contingency: If the spec arrives late, implement a configurable column-mapping layer so that field order and names can be adjusted via configuration without code changes. Ship a file-based export that coordinators can manually verify before connecting to Xledger import.
The atomic claim-marking transaction in Double-Export Guard could fail under high concurrency if two coordinators trigger an export for overlapping date ranges simultaneously, potentially allowing duplicate exports to proceed past the guard.
Mitigation & Contingency
Mitigation: Use a database-level advisory lock or a SELECT FOR UPDATE on the relevant claim rows within the export transaction to serialize concurrent exports per organization. Add an integration test that simulates concurrent export triggers.
Contingency: If locking proves problematic at the database level, implement an application-level distributed lock using a Supabase row in a dedicated export_locks table with an expiry timestamp and automatic cleanup on failure.
HLF's Dynamics portal API endpoint may not be available or documented in time for Phase 1, leaving DynamicsExporter unable to be validated against a real system and potentially shipping with an incorrect field schema.
Mitigation & Contingency
Mitigation: Design DynamicsExporter for file-based export first (CSV/JSON download), with the API push implemented behind a feature flag. Request a Dynamics test environment or sandbox from HLF as early as possible.
Contingency: Ship DynamicsExporter as a file export only for Phase 1. Phase the API push integration into a follow-on task once the Dynamics sandbox is available, using the same AccountingExporter interface with no breaking changes.