high priority high complexity testing pending integration specialist Tier 8

Acceptance Criteria

Integration test file is named recruitment_conversion_funnel_integration_test.dart and resides in integration_test/
Test uses a real in-process Supabase client pointed at a local Supabase instance (supabase start) — no mocks for repositories
Test is skipped automatically (skip: 'requires local Supabase') if the SUPABASE_TEST_URL environment variable is not set, to avoid CI failures on machines without local Supabase
Setup: create a test organisation, a test peer mentor user, and seed the referral_codes and recruitment_attribution tables via direct Supabase client calls
Step 1 — Code generation: ReferralCodeService.generateCode returns a non-null code_string and the code is persisted to Supabase (verified by direct DB query)
Step 2 — URL construction: constructShareableUrl returns a well-formed HTTPS URI containing the code_string
Step 3 — Click simulation: POST to the Edge Function /referral-click endpoint with the code and a visitor fingerprint returns HTTP 200 and a click record appears in the database
Step 4 — Registration trigger: insert a test new_member_registration record with a timestamp within the 72-hour attribution window
Step 5 — Attribution matching: ReferralAttributionService.matchRegistration returns an AttributionRecord with matched_referral_code_id equal to the generated code's ID
Step 6 — Confirmation: confirmAttribution transitions the record to 'confirmed' status — verified by DB re-fetch
Step 7 — Aggregate count: getAggregatedCount returns 1 for the test mentor after confirmation
Step 8 — Milestone event: BadgeCriteriaIntegration spy captures exactly one MilestoneEvent with threshold=1 and cumulative_count=1
Teardown: all test-created database rows are deleted in tearDown — no test pollution between runs
Full funnel test completes in < 30 seconds on a local machine with Supabase running

Technical Requirements

frameworks
Flutter
flutter_test
Riverpod
apis
Supabase PostgreSQL 15
Supabase Edge Functions (Deno)
Supabase Auth
data models
assignment
badge_definition
activity
contact
performance requirements
Each Supabase query in the test must complete in < 2 seconds on local hardware
Edge Function invocation must respond in < 5 seconds on local Supabase
Teardown must complete all deletes in < 3 seconds to allow rapid test reruns
security requirements
Integration test uses a dedicated test Supabase project or local supabase start — never production credentials
Test service role key stored in .env.test file which is gitignored — never committed
All test data uses synthetic UUIDs and fake names — no real personnummer or contact data
RLS policies are exercised during the test using the test mentor's JWT — not bypassed via service role except in setup/teardown

Execution Context

Execution Tier
Tier 8

Tier 8 - 48 tasks

Can start after Tier 7 completes

Implementation Notes

Structure the integration test as a single test() with sequential await calls that mirror the user journey, plus descriptive print() statements for each step to aid debugging when the test fails in CI. Do not split into multiple test() blocks — the funnel depends on shared state (the generated code ID) that would require complex setUp() sharing. Use a TestSupabaseClient helper class that wraps SupabaseClient with test-specific convenience methods (insertTestOrg, deleteAllTestRows). For the Edge Function call, use http.post directly rather than going through a service layer — this validates the function's HTTP contract independently of the Dart service logic.

Include a fallback: if the Edge Function is not running, the test should log a clear warning and skip (not fail with an obscure connection error).

Testing Requirements

Integration test using flutter_test integration_test package. Run with: flutter test integration_test/recruitment_conversion_funnel_integration_test.dart. Prerequisites documented in a README section: local Supabase running (supabase start), SUPABASE_TEST_URL and SUPABASE_TEST_ANON_KEY env vars set, Edge Functions deployed locally (supabase functions serve). Use setUpAll() to create shared test fixtures (org, mentor).

Use setUp()/tearDown() per test to reset attribution state. Assert each funnel step with expect() before proceeding to the next — fail fast with descriptive messages. The integration test is not run in standard CI (flutter test test/) — it is gated behind a separate CI job that spins up a Supabase Docker environment.

Component
Referral Attribution Service
service high
Epic Risks (3)
high impact medium prob integration

Confirmed registration events originate from the membership system (Dynamics portal for HLF), which may call back asynchronously with significant delay. If the attribution service only accepts synchronous confirmation at registration time, late callbacks will fail to match the originating referral code, resulting in under-counted conversions.

Mitigation & Contingency

Mitigation: Design the attribution confirmation path as a webhook endpoint (Supabase Edge Function) that accepts a referral_code + new_member_id pair at any time after click. The service matches by code string, not by session. Persist pending_signup events immediately at onboarding screen submission so there is always a record to upgrade to 'confirmed' when the webhook fires.

Contingency: If the membership system cannot reliably call the webhook, implement a polling reconciliation job (Supabase pg_cron, daily) that queries the membership system for recently registered members and back-fills any unmatched attribution records.

medium impact medium prob technical

If confirmRegistration() is called more than once for the same new member (e.g., idempotency retry from the webhook), duplicate milestone events could be emitted, causing the badge system to award badges multiple times.

Mitigation & Contingency

Mitigation: Use a UNIQUE constraint on (referral_code_id, new_member_id) in the referral_events table for confirmed events. The confirmRegistration() method uses upsert semantics; milestone evaluation reads the confirmed count from the aggregation query rather than counting individual calls.

Contingency: If duplicate awards occur in production, the badge system should support idempotent award checks (query existing badges before awarding). Add a deduplication guard in BadgeCriteriaIntegration as a secondary defence.

medium impact medium prob scope

Stakeholder review may expand attribution requirements mid-epic to include click-through tracking per channel (WhatsApp vs SMS vs email), which is not currently in scope but was mentioned in user story discussions. This would require schema changes in the foundation epic and delay delivery.

Mitigation & Contingency

Mitigation: Capture per-channel data in the device_metadata JSONB field from day one as an unstructured field (share_channel: 'whatsapp'). This preserves data without requiring a schema column, allowing structured querying to be added later without migrations.

Contingency: If channel-level analytics become a hard requirement during this epic, timebox the change to adding a nullable channel column to referral_events and a corresponding filter parameter on the aggregation query, deferring dashboard UI to a separate task.