high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

All tests pass with `flutter test` using only mocked Supabase client — no live database required
Test: exact triple-key match (same peer_mentor_id + activity_type + date) → returns one MentorDuplicateWarning with all required fields populated
Test: partial match — same mentor ID and activity type but different date → returns empty warning list (not a duplicate)
Test: partial match — same mentor ID and date but different activity type → returns empty warning list
Test: bulk input with 3 mentors, 1 duplicate → returns exactly 1 MentorDuplicateWarning for the matching mentor, not the clean ones
Test: empty candidates list → returns empty list AND verifies that the Supabase client .from() was never called (verifyNever on mock)
Test: NHF multi-coordinator scenario → raw result has recorded_by_user_id = coordinatorB_id → warning.originalRecordedByCoordinatorId == coordinatorB_id
Test: raw result with missing mentorName falls back gracefully (no exception, warning still contains mentorId)
Test: Supabase client throws PostgrestException → checkDuplicates propagates the exception (does not swallow it)
Test file named proxy_duplicate_detector_test.dart following project conventions
Tests are grouped: 'triple-key matching logic', 'bulk input handling', 'empty and edge cases', 'NHF cross-coordinator scenario', 'error propagation'

Technical Requirements

frameworks
Flutter
Dart
flutter_test
mockito or mocktail
data models
MentorActivityCandidate
MentorDuplicateWarning
DuplicateQueryResult
performance requirements
All unit tests complete in under 3 seconds (no I/O)
security requirements
Test that empty candidates list never triggers a Supabase query — verifyNever(mockClient.from('activities'))

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

The main testing challenge is the Supabase fluent query builder — it is difficult to mock at the client level. Two approaches: (1) wrap the Supabase query in a thin ActivityQueryPort interface (abstract class) and mock that — this is the cleanest approach; (2) use a fake Supabase in-memory implementation if one exists in the supabase_flutter test utilities. Approach (1) is strongly recommended for unit tests, with the real Supabase client wired in production and the integration tests (task-014) covering the real query behaviour. For the 'bulk input with mixed matches' test, construct MentorActivityCandidate fixtures with distinct UUIDs for each mentor and return a DuplicateQueryResult only for one of them from the mock — then assert the result list length is 1 and contains the correct mentorId.

Testing Requirements

Use flutter_test with mockito @GenerateMocks for the Supabase client. Because the Supabase Flutter client uses a fluent builder pattern (.from().select().in_().eq()), mocking it requires stubbing the entire builder chain or using a repository abstraction layer (preferred). If ProxyDuplicateDetector wraps the Supabase call in a private repository-like method, mock that method instead of the raw client. Use setUp() to build fresh test data (MentorActivityCandidate fixtures, DuplicateQueryResult fixtures) for each test group.

For freezed value objects, use == equality in expect() assertions. For the NHF multi-coordinator test, construct a DuplicateQueryResult with a different recorded_by_user_id than the calling coordinator and verify the field propagates to the warning output.

Component
Proxy Duplicate Detector
service medium
Epic Risks (2)
high impact low prob security

The Proxy Registration Service must verify that the coordinator has a legitimate assignment relationship with the target peer mentor before creating a record. If this check is implemented only in application code and not enforced at the DB/RLS level, a compromised or buggy client could bypass it by calling the Supabase endpoint directly, creating fraudulent proxy records for arbitrary peer mentors.

Mitigation & Contingency

Mitigation: Implement permission validation at two levels: (1) application-layer check in Proxy Registration Service that queries the assignments table before constructing the payload, and (2) RLS policy on the activities table that restricts INSERT to rows where recorded_by_user_id matches the authenticated user AND peer_mentor_id is in the set of peer mentors assigned to that coordinator. The RLS policy is the authoritative guard; the service-layer check provides early user-facing feedback.

Contingency: If RLS policy implementation is blocked by Supabase plan constraints, implement a Supabase Edge Function as a proxy endpoint that enforces the permission check server-side before forwarding to the DB. Disable direct client inserts entirely for proxy activities.

medium impact medium prob technical

For a bulk session with 30 selected peer mentors, the Proxy Duplicate Detector must query existing activities for each mentor. If implemented as 30 sequential Supabase queries, round-trip latency could make the bulk confirmation screen feel slow (>3s), degrading coordinator experience and potentially causing timeouts.

Mitigation & Contingency

Mitigation: Implement the duplicate check as a single Supabase query using an IN clause on peer_mentor_id combined with the activity_type and date filters, returning all potential duplicates for the entire batch in one network round-trip. Group results client-side by mentor ID to produce the per-mentor warning structure.

Contingency: If the single-query approach returns too much data for very large chapters, add a database index on (peer_mentor_id, activity_type, date) and profile query time. If still insufficient, accept a short loading state on the confirmation screen with a progress indicator rather than pre-loading duplicates before navigation.