medium priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test file exists at test/services/report_schema_service_test.dart
Cache-hit test: mock cache returns valid schema → loader is never called → typed ReportSchema is returned
Cache-miss test: mock cache returns null → loader is called → result stored in cache → typed ReportSchema returned
TTL expiry test: mock cache returns schema with expired TTL → loader is called to refresh → updated schema returned
Background refresh test: mock cache returns schema in refresh window (TTL within last 20%) → schema returned immediately AND loader called asynchronously
Schema parse error test: loader returns malformed JSON → last cached schema returned if available
Schema parse error with no cache test: loader returns malformed JSON and cache is empty → SchemaParseException is thrown
Multi-org isolation test: fetching schema for org-B does not invoke or pollute cache entry for org-A
Schema version mismatch test: cached schema version differs from loader version → cache invalidated → fresh schema fetched and cached
Line coverage ≥ 90% as reported by flutter test --coverage
All mocks use mockito @GenerateMocks annotation and generated via build_runner

Technical Requirements

frameworks
Flutter
flutter_test
mockito
apis
IReportSchemaCache (mock)
IOrgFieldConfigLoader (mock)
data models
ReportSchema
SchemaVersion
CacheEntry
performance requirements
All unit tests must complete in under 2 seconds total
No real network calls or Supabase connections in any test
security requirements
Test data must not contain real PII — use synthetic org IDs and field names

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

If ReportSchemaService doesn't already accept a clock parameter for TTL calculations, add a ClockProvider abstraction (a simple DateTime Function() typedef) before writing tests — this avoids flaky time-dependent tests. Use mockito's when().thenAnswer() for async mocks returning Futures. For the background refresh test, use a Completer to capture whether the async loader was called after the initial return. Ensure that test helper factories (e.g., buildMockSchema({version: '1.0', orgId: 'org-a'})) are defined in test/helpers/ to keep test bodies readable.

Do not test private methods directly — test through the public API and rely on coverage to validate internals.

Testing Requirements

Pure unit tests using flutter_test and mockito. Use setUp() to create fresh mock instances before each test to prevent state leakage. Use verify() and verifyNever() from mockito to assert loader call counts. Use fake DateTime injection (pass clock as a parameter or use a Clock abstraction) to control TTL expiry without real time.sleep().

Run flutter test --coverage and verify lcov report shows ≥ 90% line coverage for report_schema_service.dart specifically. Group tests by scenario using group() blocks: 'cache hit', 'cache miss', 'TTL expiry', 'parse errors', 'multi-org isolation'.

Component
Report Schema Service
service medium
Epic Risks (2)
medium impact high prob technical

Flutter's speech_to_text package behaviour differs meaningfully between iOS and Android — microphone permission flows, locale availability, background audio session interference, and partial-result timing all vary. Inconsistent behaviour could make voice input unreliable for the primary audience (visually impaired peer mentors on iOS VoiceOver).

Mitigation & Contingency

Mitigation: Test speech-to-text-adapter on physical iOS and Android devices from the start, not just simulators. Write platform-specific test cases for permission flows and locale detection. Design the adapter's public interface to be platform-agnostic so that a native bridge could replace the package if needed.

Contingency: If speech_to_text proves unreliable on a platform, implement a native-speech-api-bridge (already identified in the component catalogue) as a drop-in replacement within the adapter, keeping the external interface unchanged so no UI code needs to change.

medium impact medium prob dependency

The coordinator task queue notification mechanism is not fully specified. If the queue system is owned by another team or uses an external service, way-forward-task-service may block on an undefined integration contract, delaying this epic.

Mitigation & Contingency

Mitigation: Define the task queue notification interface as an abstract Dart interface early in the epic. Implement a stub that writes a flag to the database so coordinator list queries can detect new tasks, deferring the real notification integration to a later epic.

Contingency: If the queue integration remains undefined at implementation time, ship way-forward-task-service with database persistence only and add a TODO-flagged notification hook. Coordinators will still see items on next page load; push notification delivery is deferred.