high priority low complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test 'record creation': createAuditRecord returns a BufdirExportAuditRecord with a non-null id, status 'initialised', and all required fields (organisationId, requestedByUserId, exportPeriod, createdAt) populated
Test 'valid status transitions': every documented valid transition (e.g., initialised → querying_activities, querying_activities → mapping_columns, …, mapping_columns → completed) succeeds without throwing
Test 'invalid status transitions': attempting to transition from 'completed' or 'failed' to any non-terminal status throws InvalidAuditStateException
Test 'backward transition blocked': attempting to transition from 'mapping_columns' back to 'initialised' throws InvalidAuditStateException
Test 'file reference attachment': attachFileRef stores storagePath, mimeType, fileSizeBytes, sha256Checksum, and exportMetadata on the record, all readable via getAuditRecord
Test 'immutability — no delete': calling a hypothetical deleteAuditRecord method (or any repository method that would remove the record) throws AuditImmutabilityException or is not exposed on the service at all
Test 'concurrent update handling': when two simultaneous updateStatus calls target the same auditId with conflicting statuses, the second call either succeeds with the later status or throws a ConcurrentModificationException — the record must not end up in an undefined state
All tests use FakeBufdirAuditRepository (an in-memory implementation of IBufdirAuditRepository) — no real Supabase calls
FakeBufdirAuditRepository is defined in test/fakes/ and reusable by other test files
Test suite achieves ≥90% line coverage on BufdirExportAuditService

Technical Requirements

frameworks
Flutter
flutter_test
Dart
data models
BufdirExportAuditRecord
AuditStatus (enum)
ExportFileReference
ExportMetadata
InvalidAuditStateException
AuditRecordNotFoundException
ConcurrentModificationException
performance requirements
Full test suite must complete in under 5 seconds
Concurrent update test must use Dart async (Future.wait) rather than actual threading to simulate concurrency
security requirements
Test fixtures must use synthetic UUIDs (e.g., '00000000-0000-0000-0000-000000000001') rather than real user or organisation IDs

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

The state machine transition graph should be explicitly encoded as a Map> allowedTransitions inside BufdirExportAuditService (or BufdirExportErrorClassifier) and tested directly. For the concurrent update test, since Dart is single-threaded, simulate by having FakeBufdirAuditRepository introduce a short await delay on the first call so the second call interleaves — use a completer or a custom Future.delayed(Duration.zero). Document any transitions that are intentionally NOT tested (e.g., internal-only transitions) with a // not tested: reason comment. Keep FakeBufdirAuditRepository strictly minimal — only implement the methods the service actually calls, and throw UnimplementedError for any method not yet needed, so the fake fails loudly if the service interface changes.

Testing Requirements

Pure unit tests using flutter_test. Organise into group() blocks: 'record creation', 'status transitions — valid', 'status transitions — invalid', 'file reference attachment', 'immutability', 'concurrent updates'. Build FakeBufdirAuditRepository as a class implementing IBufdirAuditRepository backed by a Map and a List of call log entries for verifying call order. For the concurrent update test, use Future.wait([service.updateStatus(id, statusA), service.updateStatus(id, statusB)]) and assert that the final stored record has exactly one of the two statuses and no fields are null.

Verify ≥90% line coverage via flutter test --coverage.

Component
Export Audit Log Service
service medium
Epic Risks (2)
medium impact medium prob scope

Bufdir's column schema may have per-field business rules (conditional required fields, cross-field validation, organisation-specific category taxonomies) that cannot be expressed in a simple key-value mapping configuration. If the configuration model is too simple, supporting NHF's specific requirements will require hardcoded organisation logic, undermining the configuration-driven design.

Mitigation & Contingency

Mitigation: Design the column configuration schema as a full JSON document supporting field-level transformation rules, conditional expressions, and org-specific value enumerations. Validate the design against a real NHF Bufdir Excel template before implementation begins.

Contingency: If the configuration model cannot express all required rules, implement a thin transformation plugin interface where org-specific logic can be added as a named Dart class registered against the organisation ID, with the JSON config covering only the common cases.

high impact medium prob technical

For large organisations like NHF with potentially tens of thousands of activity records, the full export pipeline (query + map + generate + bundle + upload) may exceed Supabase Edge Function execution time limits (typically 150s), causing silent timeouts that leave audit records in a pending state indefinitely.

Mitigation & Contingency

Mitigation: Implement the orchestrator as a background Dart isolate with progress streaming rather than a synchronous Edge Function call. Use chunked processing for the query and mapping phases to reduce peak memory usage. Profile against realistic NHF data volumes in a staging environment.

Contingency: If processing time cannot be reduced below the timeout threshold, implement an asynchronous job model where the export is queued, processed in the background, and the user is notified via push notification when the download is ready — treating it as an eventual rather than synchronous operation.