Write unit tests for evaluator and repository
epic-organization-feature-flags-foundation-task-008 — Write comprehensive unit tests covering: RolloutEvaluator version comparison edge cases (equal, below, above minAppVersion), activation date boundary conditions, percentage rollout determinism by org ID, FeatureFlagRepository cache-hit and cache-miss paths, RLS-enforced org isolation (mock Supabase client returning rows for wrong org), and offline fallback behavior when the network is unavailable.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
For version comparison use semantic versioning split on '.' and compare integer tuples — do not use string lexicographic comparison as '10' < '9' lexicographically. For percentage rollout determinism use a stable hash of orgId (e.g., FNV-1a or dart:convert crc32) modulo 100 — document the chosen algorithm in a comment since changing it would invalidate existing rollout assignments. For cache TTL boundary testing, inject a Clock abstraction into the repository rather than calling DateTime.now() directly so tests can advance time without real delays. When mocking Supabase for RLS tests, have the mock return rows with a different organization_id and assert the repository filters them out — this tests the application-layer guard that complements database-level RLS.
Testing Requirements
Unit tests only — no integration or widget tests in this task. Use flutter_test with mocktail (preferred over mockito for null-safety). Organize tests in three describe-style groups: RolloutEvaluatorTest, FeatureFlagRepositoryCacheTest, FeatureFlagRepositoryIsolationTest. Each group must have a setUp that resets shared state.
Aim for 100% branch coverage on RolloutEvaluator and 90%+ line coverage on FeatureFlagRepository. Run with `flutter test --coverage` and verify coverage report before marking complete.
Supabase RLS policies for organization_configs may have gaps that allow cross-organization reads if the JWT claim for organization_id is absent or malformed, leading to data leakage between tenants.
Mitigation & Contingency
Mitigation: Implement RLS policies using auth.uid() joined against a memberships table to derive organization_id rather than trusting a client-supplied claim. Write integration tests that simulate a cross-org read attempt and assert it returns zero rows.
Contingency: If a gap is discovered post-launch, immediately disable the affected RLS policy, roll back the migration, and re-implement with a parameterized policy tested against all organization fixture data.
Dart does not have a built-in semantic version comparison library; a naive string comparison (e.g., '2.10.0' < '2.9.0' lexicographically) would cause rollout evaluator to produce incorrect eligibility results for organizations on different app versions.
Mitigation & Contingency
Mitigation: Use the pub.dev `pub_semver` package or implement a proper three-segment integer comparison. Add parameterized unit tests covering 20+ version pairs including double-digit minor/patch segments.
Contingency: If incorrect comparison is discovered in production, push a hotfix with corrected comparison logic and temporarily disable phase-gated flags until all affected organizations have updated to the corrected version.
Persistent local cache written to shared_preferences or Hive could become corrupted or deserialized incorrectly after an app update changes the FeatureFlag schema, causing startup crashes or all flags defaulting to disabled.
Mitigation & Contingency
Mitigation: Wrap all cache reads in try/catch with explicit fallback to the all-disabled default map. Version the cache key (e.g., `feature_flags_v2_{orgId}`) so schema changes automatically invalidate old entries.
Contingency: If cache corruption is detected in a release, publish an app update that clears the versioned cache key on first launch and re-fetches from Supabase.