high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test file exists at test/features/recruitment/infrastructure/referral_code_repository_test.dart
Test: createReferralCode returns a ReferralCode domain object when the Supabase insert succeeds
Test: createReferralCode first calls deactivateExistingActiveCode (UPDATE ... SET is_active=false) before inserting the new code — verified by stub call order
Test: createReferralCode when no existing active code exists skips the deactivation call and proceeds directly to insert
Test: lookupByCodeString returns a populated ReferralCode when the Supabase SELECT returns a matching row
Test: lookupByCodeString returns null when the SELECT returns an empty list
Test: lookupByCodeString returns null (or throws a typed NotFoundException) when the SELECT returns a 404-equivalent Supabase error
Test: network error during createReferralCode is mapped to a domain-specific NetworkException (not a raw Supabase exception leaking to callers)
Test: database constraint violation (e.g. unique violation on code string) during createReferralCode is mapped to a domain-specific ConflictException
All tests pass with flutter test and produce zero warnings
Branch coverage reported by lcov or flutter test --coverage is at or above 90% for referral_code_repository.dart

Technical Requirements

frameworks
flutter_test
mocktail (preferred) or mockito for mock generation
supabase_flutter (Supabase client interface to mock)
apis
Supabase PostgREST client (mocked)
Supabase error types (PostgrestException)
data models
ReferralCode
ReferralCodeCreateParams
performance requirements
All unit tests must complete in under 2 seconds total — no real network calls
Mocks must be reset between test cases to prevent state bleed
security requirements
Test data must not contain real member IDs or personal data — use UUIDs like 00000000-0000-0000-0000-000000000001

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

With mocktail, stub the Supabase query builder chain carefully — the builder pattern (from().select().eq().single()) requires each chained method to return the next mock. Consider wrapping the Supabase client calls in a thin data-source abstraction (ReferralCodeDataSource) if the chain mocking becomes brittle; this also makes the repository easier to test long-term. For error mapping tests, throw a PostgrestException with the appropriate error code (e.g. '23505' for unique violation) from the mock and assert the repository wraps it in the correct domain exception type.

Avoid asserting on exact SQL strings — assert on the domain objects returned and the exceptions thrown. Run 'flutter test --coverage && genhtml coverage/lcov.info -o coverage/html' locally to visualise branch coverage before committing.

Testing Requirements

This task IS the testing deliverable. Use the flutter_test package with mocktail for mocking. Structure tests with group() blocks per public method (createReferralCode, lookupByCodeString, deactivateExistingActiveCode if public). Each group should contain happy-path, not-found, and error-mapping test cases.

Use setUp() to initialise a fresh mock and repository instance before each test. Generate a coverage report with 'flutter test --coverage' and verify the lcov.info shows ≥90% branch coverage for the repository file. If using mockito, run build_runner to generate mocks before committing. Do not use integration_test package — these are pure unit tests with no platform dependencies.

Component
Referral Code Repository
data low
Epic Risks (3)
high impact medium prob technical

iOS Universal Links and Android App Links have distinct configuration requirements (apple-app-site-association, assetlinks.json, entitlements). A misconfiguration causes the OS to open the referral URL in a browser instead of the app, completely breaking the onboarding funnel for new members on one platform.

Mitigation & Contingency

Mitigation: Configure both Universal Links and App Links from the start of this epic using the project's existing Supabase-hosted domain. Write an E2E test on both simulators that taps a referral URL and asserts the onboarding screen is reached. Document the required server-side JSON files alongside the migration.

Contingency: If platform deep-link configuration cannot be resolved before the UI epics need the handler, implement a fallback custom-scheme URI (e.g., likeperson://referral?code=XYZ) that works unconditionally, and schedule Universal/App Link fix as a follow-up task.

high impact high prob security

Referral click events must be writable without an authenticated session (a new member who has not yet registered is tapping the link). Standard Supabase RLS cannot grant anonymous inserts without opening a security hole. If this is not solved early it blocks the entire attribution pipeline.

Mitigation & Contingency

Mitigation: Design referral_events writes to go exclusively through a Supabase Edge Function that validates the referral code exists and is active before inserting. The Edge Function uses the service-role key server-side; the client only calls the function endpoint. This is documented in the feature spec.

Contingency: If the Edge Function approach is delayed, temporarily allow anon inserts restricted by a CHECK constraint that event_type = 'click' and new_member_id IS NULL, then tighten to Edge Function writes in a follow-up migration before the feature goes to production.

medium impact low prob dependency

The qr_flutter package version pinned in pubspec may conflict with the current Flutter SDK version or with other packages in the monorepo, causing build failures that block QR code delivery.

Mitigation & Contingency

Mitigation: Verify qr_flutter compatibility against the project's Flutter SDK version as the very first task in this epic. If a conflict exists, resolve it before any other work proceeds.

Contingency: If qr_flutter cannot be made compatible, evaluate mobile_scanner (already likely in pubspec for QR scanning) which also supports generation, or implement QR generation via a lightweight Dart port as a last resort.