high priority medium complexity testing pending testing specialist Tier 2

Acceptance Criteria

WizardDraftRepository unit tests cover: createDraft() returns a new WizardDraft with a UUID id, autoSave() serializes and persists draft state without data loss, loadDraft() returns null for an expired TTL, deleteDraft() removes the draft and loadDraft() returns null afterward
WizardDraftRepository RLS isolation test: authenticating as user A and attempting to loadDraft() with user B's draftId returns null (not an error, not user B's data)
WizardDraftRepository corruption test: injecting malformed JSON into the draft row causes loadDraft() to return null (graceful degradation) and logs a structured error — it does not throw an unhandled exception
WizardDraftRepository network failure test: simulating a Supabase timeout causes autoSave() to throw a typed RepositoryException (not a raw DioException or SocketException) with an errorCode that maps to a PlainLanguageErrorDisplay key
ErrorMessageRegistry unit tests cover: initialize() loads all error messages from the local asset bundle, lookup(knownCode) returns the correct ErrorMessage, lookup(unknownCode) returns the generic fallback ErrorMessage, applyOrgOverride(orgId, code) returns org-specific wording when an override exists
ErrorMessageRegistry test: loading from a corrupt or missing asset bundle causes a typed RegistryLoadException and the registry falls back to a hardcoded minimal set of critical error messages
HelpContentRegistry unit tests cover: initialize() loads help content from local assets, resolve(knownKey) returns the correct HelpContent, resolve(unknownKey) returns null (not an exception), applyOrgOverrides() applies terminology replacements in the resolved text without exceeding the 80-word limit
HelpContentRegistry override chain test: verifies priority order — org-specific override supersedes default content for the same helpKey
Overall line coverage ≥90% across all three repository/registry files as measured by flutter test --coverage
All integration tests use a dedicated Supabase test project with seeded test data and are isolated from production — connection string loaded from a .env.test file not committed to git

Technical Requirements

frameworks
Flutter
flutter_test
mockito or mocktail (for unit test mocks)
apis
Supabase REST API (test project)
WizardDraftRepository public interface
ErrorMessageRegistry public interface
HelpContentRegistry public interface
data models
WizardDraft
ErrorMessage
HelpContent
OrgTerminologyOverride
RepositoryException
RegistryLoadException
performance requirements
Unit test suite must complete in under 30 seconds total
Integration tests may take up to 60 seconds and should be tagged @Tags(['integration']) so they can be excluded from fast CI runs
security requirements
Integration test Supabase credentials must be stored in .env.test (gitignored) and loaded via String.fromEnvironment or flutter_dotenv — never hardcoded
RLS isolation tests must use two distinct test user JWTs provisioned in the test project — never impersonate users by bypassing RLS with the service role key in tests that are intended to verify RLS

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Start with unit tests first using mocked Supabase client — this gives fast feedback during development. Add integration tests last to validate the real network path. For the TTL expiry test, do not use real time delays — inject a clock abstraction (e.g., a DateTime Function() nowProvider parameter on WizardDraftRepository) and advance it in tests. For the RLS isolation test, create two Supabase test users with real JWTs signed by the test project's JWT secret — document the setup in a test/README.md file.

Use group() blocks in tests to separate CRUD, edge cases, network failures, and RLS sections for readable test output. Avoid snapshot-based tests (golden files) for data layer tests — these are pure logic tests.

Testing Requirements

This task IS the testing task. Structure tests in three test files: wizard_draft_repository_test.dart, error_message_registry_test.dart, help_content_registry_test.dart. Use mocktail to mock the Supabase client for unit tests so they run without a network connection. Use a real Supabase test project only for integration tests tagged @Tags(['integration']).

Apply setUp() and tearDown() to seed and clean test data for integration tests. Run coverage with: flutter test --coverage && genhtml coverage/lcov.info -o coverage/html and verify the line coverage badge in the CI summary. Document any lines intentionally excluded from coverage with // coverage:ignore-line and a comment explaining why.

Component
Wizard Draft Repository
data low
Epic Risks (4)
high impact medium prob technical

The error message registry and help content registry both depend on bundled JSON assets loaded at startup. If asset loading fails silently (e.g. malformed JSON, missing pubspec asset declaration), the entire plain-language layer falls back to empty strings or raw error codes, breaking the accessibility guarantee app-wide.

Mitigation & Contingency

Mitigation: Implement eager validation of both assets during app initialisation with an assertion failure in debug mode and a structured error log in release mode. Add integration tests that verify asset loading in the Flutter test harness on every CI run.

Contingency: Ship a hardcoded minimum-viable fallback message set directly in Dart code so the app always has at least a safe generic message, preventing a blank or code-only error surface.

medium impact medium prob dependency

The AccessibilityDesignTokenEnforcer relies on dart_code_metrics custom lint rules. If the lint toolchain is not already configured in the project's CI pipeline, integrating a new linting plugin may cause unexpected build failures or require significant CI configuration work beyond the estimated scope.

Mitigation & Contingency

Mitigation: Audit the existing dart_code_metrics configuration in the project before starting implementation. Scope the lint rules to a separate Dart package that can be integrated incrementally, starting with the most critical rule (hard-coded colors) and adding others in subsequent iterations.

Contingency: Fall back to Flutter test-level assertions (using the cognitive-accessibility-audit utility) to catch violations in CI if the lint plugin integration is delayed, preserving enforcement coverage without blocking the epic.

medium impact low prob technical

WizardDraftRepository must choose between shared_preferences and Hive for local persistence. Choosing the wrong store for the data volume (e.g. shared_preferences for complex nested wizard state) can lead to serialisation bugs or performance degradation, particularly on lower-end Android devices used by some NHF members.

Mitigation & Contingency

Mitigation: Define a clean repository interface first and implement shared_preferences as the initial backend. Profile serialisation round-trip time with a realistic wizard state payload (≈10 fields) before committing to either store.

Contingency: Swap the persistence backend behind the repository interface without touching wizard UI code, which is possible precisely because the repository abstraction isolates the storage detail.

medium impact high prob scope

The AccessibilityDesignTokenEnforcer scope could expand significantly if a large portion of existing widgets use hard-coded values. Discovering widespread violations during this epic would force either a major refactor or a decision to exclude legacy components, potentially reducing the enforcer's coverage and value.

Mitigation & Contingency

Mitigation: Run a preliminary audit of existing widgets using a simple grep for hard-coded hex colors and raw pixel values before implementation begins. Use the results to set a realistic remediation boundary for this epic and log all out-of-scope violations as tracked tech-debt items.

Contingency: Scope the enforcer to new and modified components only (via file-path filters in dart_code_metrics config), shipping a partial but immediately valuable coverage rather than blocking the epic on full-codebase remediation.