high priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

All public methods on ReportHistoryService have at least one passing unit test per distinct logical branch
Pagination logic is tested: first page, subsequent pages, last page (fewer items than page size), and empty result set
Role enforcement is tested: coordinator role receives only own records, admin role receives all records, unauthenticated call throws AuthorizationException
Signed URL resolution is tested: valid URL returned on success, StorageException propagated on failure, null stored URL handled gracefully
Filter logic is tested for each filter dimension (year, period type, status) independently and in combination
ReportReexportCoordinator integration test covers happy path: period parameters loaded → pipeline invoked → history record updated → signed URL returned
Coordinator integration test verifies audit event is persisted with correct actor, action, and timestamp fields after successful re-export
Coordinator integration test covers pipeline failure: history record is NOT updated and audit event records failure state
All tests use mocked repository and storage client (no live Supabase calls in unit or integration tests)
Test coverage for ReportHistoryService public API reaches ≥ 90% line coverage
All tests pass in CI with `flutter test` and produce no warnings

Technical Requirements

frameworks
Flutter
flutter_test
mockito or mocktail
apis
ReportHistoryService public API
ReportReexportCoordinator public API
Supabase Storage (mocked)
data models
ReportHistoryRecord
ReportPeriodParameters
AuditEvent
PaginatedResult
performance requirements
Full test suite completes in under 30 seconds
No real network I/O — all external dependencies injected as mocks
security requirements
Tests must not embed real Supabase credentials or service-role keys
Role enforcement tests must assert that unauthorized access throws, not silently returns empty data

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Inject dependencies via constructor parameters — do not use service locators inside tests. Define abstract repository and storage interfaces if not already present so mocks can be generated. For the coordinator integration tests, instantiate the real coordinator with mocked dependencies rather than mocking the coordinator itself; this exercises the orchestration logic. Use `setUp` / `tearDown` to reset mock state between tests.

Verify interaction counts (e.g., `verify(() => mockRepo.updateRecord(...)).called(1)`) in addition to return value assertions to confirm side-effects fire exactly once. For audit event tests, capture the argument passed to the audit repository and assert individual fields rather than whole-object equality to keep tests resilient to non-relevant field additions.

Testing Requirements

Unit tests: one test file per class (report_history_service_test.dart, report_reexport_coordinator_test.dart). Use mocktail or mockito to mock the repository interface and Supabase storage client. Parameterised tests for filter combinations to avoid repetition. Integration tests for the coordinator should compose real service logic against mocked infrastructure (repository + storage) to exercise the full orchestration flow.

Assert on both return values and side-effects (audit persistence). Run with `flutter test --coverage` and enforce ≥ 90% coverage for both files.

Component
Report History Service
service low
Epic Risks (3)
high impact medium prob dependency

The ReportReexportCoordinator must invoke the Bufdir export pipeline defined in the bufdir-report-export feature. If that feature's internal API changes (renamed services, altered parameters), the re-export coordinator will break silently at runtime.

Mitigation & Contingency

Mitigation: Define a stable, versioned interface (abstract class or Dart interface) for the export pipeline entry point. The re-export coordinator depends only on this interface, not on concrete export service internals. Document the contract in both features.

Contingency: If the export pipeline breaks the re-export coordinator, fall back to surfacing a clear 'regeneration unavailable' message to the coordinator with instructions to use the primary export screen for the same period as a workaround, while the interface mismatch is fixed.

high impact low prob security

The audit trail must be immutable — coordinators must not be able to edit or delete past events. If the RLS policies allow UPDATE or DELETE on audit event rows, a coordinator could suppress evidence of a re-export or failed submission.

Mitigation & Contingency

Mitigation: Apply INSERT-only RLS policies to the audit events table (no UPDATE, no DELETE for any non-service-role user). Use a separate service-role key for writing audit events, never the user's JWT. Validate this in integration tests by asserting that UPDATE and DELETE calls from coordinator-role sessions are rejected with RLS errors.

Contingency: If immutability is compromised before detection, run a database audit comparing the audit log against the main history table timestamps to identify tampered records, restore from backup if needed, and issue a patch RLS migration immediately.

low impact low prob technical

The user stories require filter state (year, period type, status) to persist within a session so coordinators do not lose context when navigating away. Implementing this with Riverpod state management could cause stale filter state if the provider is not properly scoped to the session lifecycle.

Mitigation & Contingency

Mitigation: Scope the filter state provider to the router's history route scope, not globally. Use autoDispose with a keepAlive flag tied to the session so filters reset on logout but persist on tab switches within the same session.

Contingency: If filter state becomes stale or leaks between sessions, add an explicit reset in the logout handler that disposes all scoped providers. This is a UX degradation (coordinator must re-apply filters) rather than a data integrity issue.