high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test file is located at test/widgets/feature_gate_widget_test.dart and runnable via flutter test
Test: renders child widget when the feature flag is enabled
Test: renders fallback widget when the feature flag is disabled
Test: renders fallback widget (not child) when the flag is in loading state (AsyncLoading)
Test: renders SizedBox.shrink() or empty when flag is disabled and no fallback is provided
Test: widget reactively updates from disabled→enabled when the provider state changes mid-test
Test: widget reactively updates from enabled→disabled when the provider state changes mid-test
Test: unknown or unregistered flag key does not throw an exception; widget renders fallback or empty
Test: AsyncError state renders fallback (not child) and does not throw
All tests use ProviderScope with overrides; no real Supabase or network calls are made
Tests pass consistently with no flakiness; zero use of real timers or sleeps

Technical Requirements

frameworks
Flutter
flutter_test
Riverpod
apis
FeatureFlagProvider (Riverpod AsyncNotifier or StateNotifier)
ProviderScope.overrides for mocking flag state
data models
FeatureFlag (id, isEnabled)
AsyncValue<FeatureFlag> states: AsyncData, AsyncLoading, AsyncError
performance requirements
All tests must complete in under 5 seconds total
No real async delays; use tester.pump() and tester.pumpAndSettle() correctly
security requirements
Tests must not hardcode real flag IDs from production; use clearly named test constants
ui components
FeatureGateWidget under test
Stub child widget (e.g., Text('child'))
Stub fallback widget (e.g., Text('fallback'))

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use ProviderContainer or ProviderScope.overrides with a manual StateProvider> to control the flag value imperatively within tests. For state-change tests, use a ref.read(provider.notifier).state = newValue pattern or replace the override using tester.runAsync. Ensure the FeatureGate widget's provider lookup uses the flag key as a parameter—test this via a family provider (featureFlagProvider(flagKey)). For the unknown key test, override the provider to return AsyncError(UnknownFlagException(), StackTrace.empty) and verify no exception propagates to the test runner.

Document each test case with a short // given / when / then comment block for readability.

Testing Requirements

This task IS the testing deliverable. All 8+ test cases listed in acceptance criteria must be implemented. Use group() blocks to organize by scenario. Each test must: pump a ProviderScope with an overridden featureFlagProvider returning the desired AsyncValue state, pump the FeatureGate widget with child and optional fallback, and assert find.text('child') or find.text('fallback') with findsOneWidget/findsNothing.

State-change tests must update the provider state mid-test using a StateController or by replacing the override and calling tester.pumpAndSettle(). Achieve 100% branch coverage on FeatureGate's build method.

Component
FeatureGate Widget
ui low
Epic Risks (3)
high impact low prob security

The feature flag admin screen allows persisting changes to organization_configs. If the role guard is implemented only client-side (checking role state in Riverpod), a user who manipulates their local role state could toggle flags for their organization without proper server-side authorization, potentially exposing features prematurely.

Mitigation & Contingency

Mitigation: Implement server-side authorization for flag update operations using Supabase RLS UPDATE policies that check the user's role in the memberships table. The client-side guard is UX only; the database enforces the actual restriction.

Contingency: If an unauthorized update is detected, audit the RLS policies and add a Supabase Edge Function as an authorization middleware for flag toggle operations, rejecting requests from non-admin role JWTs.

medium impact medium prob scope

Developers on other feature teams may use FeatureGate incorrectly — for example, wrapping business logic rather than UI, or using it before flag initialization completes — leading to features that are visible but non-functional or cause runtime errors when flags are queried in a loading state.

Mitigation & Contingency

Mitigation: Add assert statements in FeatureGate's build method that throw in debug mode if the provider is still in a loading state. Write developer documentation with a clear usage contract: FeatureGate is UI-only; logic gating must use the provider's isEnabled method directly. Include lint examples in the codebase.

Contingency: If misuse is found in code reviews, add a custom Dart lint rule via custom_lint that flags FeatureGate usage outside of the widget tree, and conduct a codebase audit to find existing violations.

medium impact low prob technical

If the audit log is stored in the same organization_configs table without pagination or archival strategy, high-frequency flag changes during pilot testing could produce an unbounded number of rows, degrading query performance on the admin screen.

Mitigation & Contingency

Mitigation: Store audit log entries in a separate feature_flag_audit_log table with an index on (organization_id, changed_at DESC). Implement cursor-based pagination in the repository and limit the initial load to 50 entries.

Contingency: If table size becomes a performance concern, add a Supabase scheduled function to archive entries older than 90 days to cold storage, and add a database index on changed_at for range queries.