high priority medium complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test: app foreground event triggers a version check call on the repository
Test: connectivity-restore event triggers a version check call on the repository
Test: when version check returns upToDate, the OrganizationLabelsNotifier.reload() is NOT called
Test: when version check returns refreshRequired, the OrganizationLabelsNotifier.reload() IS called exactly once
Test: first network failure triggers retry after base backoff delay (e.g., 2s)
Test: second consecutive failure doubles the backoff delay (e.g., 4s)
Test: after max retry count is reached, no further retries are scheduled
Test: a second trigger arriving while a sync is already in-flight does not start a second concurrent sync
Test: calling dispose() cancels any pending retry timer — no callback fires after dispose
All timing is controlled with FakeAsync — no real time delays in tests
Mock/fake objects used for all external dependencies (repository, notifier, connectivity stream)

Technical Requirements

frameworks
Flutter
flutter_test
apis
Supabase (stubbed)
Connectivity plugin (stubbed)
data models
TerminologySyncService
TerminologyVersionCheckResult
OrganizationLabelsNotifier
performance requirements
All timing-sensitive tests use FakeAsync to run without wall-clock delays
Test suite completes in under 5 seconds
security requirements
No real network calls or Supabase tokens in test fixtures

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

TerminologySyncService should accept its dependencies (repository, notifier, connectivity stream, clock/timer factory) via constructor injection to make it fully testable without real implementations. The deduplication flag should be a simple boolean `_syncInProgress` guard checked at the top of the sync method. Exponential backoff: use a `_retryCount` counter reset on success and incremented on failure; delay = min(baseDelay * 2^retryCount, maxDelay). Timer should be stored as a nullable field and cancelled on dispose.

Use a `_disposed` flag checked in the retry callback to guard against post-dispose execution.

Testing Requirements

Pure unit tests using flutter_test and the fake_async package for timer control. Use mocktail to stub TerminologyRepository (version check) and a mock OrganizationLabelsNotifier. Inject a StreamController for the connectivity stream to simulate restore events. For backoff tests, use FakeAsync.elapse() to advance time and verify the number of version check calls via verify().

For deduplication, trigger two sync events in rapid succession before the first completes and assert the repository is called only once. For dispose/cancel, call dispose() during a pending backoff and elapse past the backoff window — assert no additional calls fire.

Component
Terminology Sync Service
service medium
Epic Risks (3)
high impact medium prob technical

When a user switches organization context (e.g., a coordinator with multi-org access), a race condition between the outgoing organization's map disposal and the incoming organization's fetch could briefly expose the wrong organization's terminology to the widget tree.

Mitigation & Contingency

Mitigation: Implement an explicit loading state in OrganizationLabelsNotifier that widgets check before rendering any resolved labels. The provider graph should cancel the previous organization's fetch via Riverpod's ref.onDispose before initiating the next.

Contingency: If the race manifests in production, fall back to English defaults during the transition window and emit a Sentry error event for investigation; the UX impact is a brief English flash rather than wrong-org terminology.

high impact low prob security

Supabase Row Level Security policies on organization_configs may inadvertently restrict the authenticated user from reading their own organization's labels JSONB column, causing silent empty maps that appear as English fallbacks.

Mitigation & Contingency

Mitigation: Write and test explicit RLS policies that grant SELECT on the labels column to any authenticated user whose organization_id matches. Add an integration test that verifies label fetch succeeds for each role (peer mentor, coordinator, admin).

Contingency: If RLS blocks are discovered in production, temporarily escalate label fetch to a service-role edge function while the RLS policy is corrected, ensuring no labels are exposed cross-organization.

medium impact medium prob scope

A peer mentor who installs the app for the first time with no internet connection will have no cached terminology map and will see only English defaults, which may be confusing for organizations like NHF that use Norwegian-specific role names exclusively.

Mitigation & Contingency

Mitigation: Bundle a default fallback terminology map for each known organization as a compile-time asset (Dart asset file) so that even fresh installs without connectivity render correct organizational terminology immediately.

Contingency: If bundled assets are out of date, display a one-time informational banner noting that terminology will update on next connectivity restore, with no functional blocking of the app.