Integration test full recruitment conversion funnel
epic-membership-recruitment-core-services-task-014 — Write an end-to-end integration test that exercises the complete conversion funnel through both services: generate a referral code for a mentor, construct a shareable URL, simulate an inbound click event via the Edge Function, trigger a new member registration, match the registration to the code, confirm attribution after membership verification, verify the aggregated count increments, and assert that the milestone event is published and received by the badge integration layer.
Acceptance Criteria
Technical Requirements
Execution Context
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.
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.
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.
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.