critical priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

Integration test file exists at integration_test/foundation_smoke_test.dart and runs via flutter test integration_test/
SecureStorageAdapter test: writeOrgId('test-org-123') followed by readOrgId() returns 'test-org-123' without error
SecureStorageAdapter test: readOrgId() on fresh install returns null, not an exception
SupabaseRLSTenantConfigurator test: after applyTenantScope('test-org-123'), a Supabase query to a tenant-scoped table returns only rows where org_id='test-org-123' (requires test data seeded in Supabase test project or a mock Supabase client)
OrgBrandingCache test: prewarm() triggers one Supabase network call; second call to getBrandingTokens('test-org-123') returns cached data without a second network call (verified via call count on mock)
FeatureFlagProvider test: getFlags('test-org-123') returns a FlagSet with known flags for that org matching test fixture data
OrgCardWidget render test: widget pumped with branding tokens from OrgBrandingCache renders org name and applies primary color from tokens without layout errors
OnboardingProgressIndicator test: renders correctly in AuthPhase.emailPassword (3 labeled dots) and AuthPhase.bankId (3 labeled dots) within the same test session
All 6 scenario groups pass without any unhandled exceptions
Test uses a dedicated Supabase test project or a MockSupabaseClient — never the production Supabase instance
Test teardown clears all secure storage keys written during the test run

Technical Requirements

frameworks
Flutter
flutter_test
integration_test package
apis
Supabase REST API (test project or mock)
flutter_secure_storage
data models
Organization
OrgBrandingTokens
FeatureFlags
TenantScope
performance requirements
Full integration test suite completes within 60 seconds on CI
OrgBrandingCache second-call response time < 5ms (cache hit, no network)
security requirements
Test Supabase credentials stored in CI environment variables, never committed to source
Secure storage cleared in tearDown to prevent test data leakage between runs
Test org ID ('test-org-123') must not correspond to any real organization
ui components
OrgCardWidget
OnboardingProgressIndicator

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Structure the test file with a setUp block that initializes all foundation components in dependency order: (1) SecureStorageAdapter, (2) MockSupabaseClient or real client, (3) SupabaseRLSTenantConfigurator(client), (4) OrgBrandingCache(client), (5) FeatureFlagProvider(client). Use tearDown to clear secure storage: await storage.deleteAll(). For Supabase test project seed data: create a test org with id='test-org-123', branding tokens, and feature flags. For cache hit verification: inject a CallCountingSupabaseClient and assert invocationCount == 1 after both prewarm() and getBrandingTokens() calls.

This test is the gate before dependent epics — if it fails, downstream work is blocked. Consider marking it with @Tags(['smoke']) for CI pipeline filtering.

Testing Requirements

Integration tests using the flutter integration_test package (not just flutter_test). The test must run on a real device or emulator/simulator — use flutter test integration_test/ or flutter drive. For Supabase interactions: prefer a dedicated Supabase test project with seeded test data over mocking, but a MockSupabaseClient (implementing the SupabaseClient interface) is acceptable if a test project is unavailable. Verify cache behavior by wrapping the Supabase client in a SpyClient that counts method invocations.

Verify secure storage with real flutter_secure_storage on device. Each of the 6 scenarios should be its own testWidgets or test block with independent setup and teardown.

Component
Supabase RLS Tenant Scope Configurator
infrastructure medium
Dependencies (6)
Write Flutter widget tests for OnboardingProgressIndicator covering: correct step count and labels for email/password phase, correct step count and labels for BankID/Vipps phase, step dot fill/unfill on currentStep change, animated transition completion, semantic progress string emitted per step change, and WCAG color contrast for active vs. inactive dots. epic-organization-selection-and-onboarding-foundation-task-013 Write unit tests for OrgBrandingCache covering: cold-start fetch from Supabase, second-call memory-cache hit (no network), disk-cache restore on app restart, cache invalidation when org changes, and graceful fallback to default design tokens when network is unavailable. Mock the Supabase client and storage adapter. epic-organization-selection-and-onboarding-foundation-task-007 Write unit tests for FeatureFlagProvider covering: flag enabled/disabled states, TTL-based cache expiry triggering a re-fetch, org-switch invalidation clearing previous org flags, percentage rollout boundary conditions (0%, 50%, 100%), and graceful fallback to false when Supabase is unreachable. Use fake async and mock Supabase client. epic-organization-selection-and-onboarding-foundation-task-009 Write Flutter widget tests for OrgCardWidget covering: logo display with placeholder fallback, name truncation for long strings, active-state border appearance, tap callback invocation, semantic label correctness for VoiceOver/TalkBack, contrast ratio validation using the design token palette, and 48dp touch target compliance. Use flutter_test and accessibility audit helpers. epic-organization-selection-and-onboarding-foundation-task-011 Write unit tests for the SecureStorageAdapter using a mock/fake implementation. Cover write-then-read round-trips, overwrite semantics, missing-key reads returning null, delete idempotency, and error propagation for platform exceptions. Achieve 100% branch coverage on the adapter logic. epic-organization-selection-and-onboarding-foundation-task-003 Write integration tests verifying that after SupabaseRLSTenantConfigurator is invoked, subsequent Supabase queries are scoped to the selected organization and do not return records from other tenants. Use a test Supabase project with seeded multi-tenant data. Validate GDPR non-leakage between orgs. epic-organization-selection-and-onboarding-foundation-task-005
Epic Risks (3)
high impact medium prob technical

iOS Keychain and Android Keystore have meaningfully different failure modes and permission models. The secure storage plugin may throw platform-specific exceptions (e.g., biometric enrollment required, Keystore wipe after device re-enrolment) that crash higher-level flows if not caught at the adapter boundary.

Mitigation & Contingency

Mitigation: Wrap all storage plugin calls in try/catch at the adapter layer and expose a typed StorageResult<T> instead of throwing. Write integration tests on real device simulators for both platforms in CI using Fastlane. Document the exception matrix during spike.

Contingency: If a platform-specific failure cannot be handled gracefully, fall back to in-memory-only storage for the current session and surface a non-blocking warning to the user; log the event for investigation.

high impact medium prob integration

Setting a session-level Postgres variable (app.current_org_id) via a Supabase RPC requires that RLS policies on every table reference this variable. If the Supabase project schema has not yet defined these policies, the configurator will set the variable but queries will return unfiltered data, giving a false sense of security.

Mitigation & Contingency

Mitigation: Include a smoke-test RPC in the SupabaseRLSTenantConfigurator that verifies the variable is readable from a policy-scoped query before marking setup as complete. Coordinate with the database migration task to ensure RLS policies reference app.current_org_id before the configurator is shipped.

Contingency: If RLS policies are not in place at integration time, gate all data-fetching components behind a runtime check in SupabaseRLSTenantConfigurator.isRlsScopeVerified(); block data access and surface a developer warning until policies are confirmed.

medium impact medium prob technical

Fetching feature flags from Supabase on every cold start adds network latency before the first branded screen renders. On slow connections this may cause a perceptible blank-screen gap or cause the app to render with default (unflagged) state before flags arrive.

Mitigation & Contingency

Mitigation: Persist the last-known flag set to disk in the FeatureFlagProvider and serve stale-while-revalidate on startup. Gate flag refresh behind a configurable TTL (default 15 minutes) so network calls are not made on every launch.

Contingency: If stale flags cause a feature to appear that should be hidden, add a post-load re-evaluation pass that reconciles the live flag set with the rendered widget tree and triggers a targeted rebuild where needed.