high priority medium complexity testing pending testing specialist Tier 11

Acceptance Criteria

All-clean path: when all participants pass conflict detection, batchInsert is called exactly once with a list whose length equals the full participant count
Mixed conflict path: when any participant has a conflict and coordinator has not approved, batchInsert is never called and a BulkConflictSummary is returned listing all conflicting mentor IDs
Coordinator-approved subset path: given a list where 3 of 5 participants are coordinator-approved, batchInsert is called with exactly 3 records and the committed count in BulkRegistrationSuccess equals 3
Atomic failure path: when batchInsert throws, BulkRegistrationError is returned; a follow-up call to a hypothetical getActivities mock returns 0 new rows (no partial state leaked)
Empty list edge case: registerBulk([]) returns BulkRegistrationSuccess(count: 0) and batchInsert is never called
Attribution verification: in the happy path, each record passed to batchInsert has both recorded_by_user_id and peer_mentor_id set, verified by capturing the argument with mocktail's `captureAny`
Tests are isolated — no shared mutable state between test cases
Minimum 5 tests matching the 5 documented paths plus the empty list edge case (6 total)
Test file compiles and all tests pass with `flutter test` with zero warnings

Technical Requirements

frameworks
Flutter
flutter_test
mocktail
apis
ProxyActivityRepository.batchInsert()
ActivityAttributionService.attributeRecord()
BulkConflictDetectionService.detect()
data models
BulkRegistrationSuccess
BulkRegistrationError
BulkConflictSummary
ApprovedParticipant
AttributedActivityRecord
performance requirements
Full test suite must complete under 3 seconds — no real async I/O
security requirements
Use fixture UUIDs for user IDs — never real identifiers

Execution Context

Execution Tier
Tier 11

Tier 11 - 5 tasks

Can start after Tier 10 completes

Dependencies (10)
epic-proxy-activity-registration-core-services-task-001 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-002 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-003 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-008 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-004 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-005 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-006 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-007 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-009 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-010 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
Dependents (18)
epic-proxy-activity-registration-ui-task-001 component Cross-Epic Component bulk-participant-list depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-002 component Cross-Epic Component bulk-participant-list depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-003 component Cross-Epic Component bulk-participant-list depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-004 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-005 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-006 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-007 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-008 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-009 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-010 component Cross-Epic Component proxy-activity-form depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-011 component Cross-Epic Component proxy-activity-form depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-012 component Cross-Epic Component proxy-registration-screen depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-013 component Cross-Epic Component proxy-registration-screen depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-014 component Cross-Epic Component proxy-registration-screen depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-015 component Cross-Epic Component bulk-registration-screen depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-016 component Cross-Epic Component bulk-registration-screen depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-017 component Cross-Epic Component bulk-registration-screen depends on bulk-registration-service
epic-proxy-activity-registration-ui-task-018 component Cross-Epic Component proxy-registration-bloc depends on bulk-registration-service

Implementation Notes

The key testing challenge is the approved-subset scenario: construct fixture data with a mix of approved and unapproved participants and capture what BulkRegistrationService actually passes to batchInsert. Use mocktail's `captureAny` matcher: `final captured = verify(() => mockRepo.batchInsert(captureAny())).captured.first as List; expect(captured.length, 3)`. For the atomic failure test, configure `when(() => mockRepo.batchInsert(any())).thenThrow(...)` and then verify the return type is BulkRegistrationError. To prove atomicity, assert that a separate read mock (or the same repo mock's hypothetical `findByCoordinatorId`) was never called with a non-empty result — this is conceptual proof since true atomicity lives in Supabase, but the Dart layer must not call batchInsert multiple times.

Document each test's intent clearly and link to the acceptance criterion number in the test description string.

Testing Requirements

Test file location: `test/orchestration/bulk_registration_service_test.dart`. Use mocktail for all mocks. Use `captureAny()` to capture the list argument passed to batchInsert in the happy path and approved-subset path — assert its length and content rather than relying on `verify().called(1)` alone. For the atomic failure path, consider using an in-memory list as a side-effect spy on batchInsert to confirm zero elements were written.

Group tests by scenario with `group()`. The approved-subset scenario requires constructing a list where some ParticipantRecord objects have `coordinatorApproved: true` and others `coordinatorApproved: false` — ensure BulkRegistrationService filters correctly before passing to batchInsert.

Component
Bulk Registration Service
service high
Epic Risks (3)
high impact low prob integration

If the Supabase batch RPC partial-inserts some records before encountering an error and does not roll back cleanly, the bulk service may report failure while orphaned records exist in the database, corrupting reporting data.

Mitigation & Contingency

Mitigation: Wrap the bulk insert in an explicit Supabase transaction via the RPC function. Write an integration test that simulates a mid-batch constraint violation and asserts zero records were written.

Contingency: If a partial-write incident occurs, the registered_by audit field allows identification and deletion of the orphaned records. Implement a coordinator-facing bulk submission status screen to surface any such anomalies.

medium impact medium prob scope

When a bulk submission of 15 participants has 4 duplicates, the aggregated conflict summary may be too complex for coordinators to process quickly, leading to blanket override decisions that defeat the purpose of duplicate detection.

Mitigation & Contingency

Mitigation: Design the conflict result type to support per-participant override flags, so the UI can present a clear list of conflicting participants with individual cancel/override toggles rather than a single global decision.

Contingency: If coordinator usability testing reveals the conflict review screen is too complex, simplify to a 'skip all conflicts and submit the rest' mode as an immediate fallback while a more granular UI is designed.

high impact low prob security

If the coordinator role check inside proxy-registration-service is inconsistent with the route-level guard, a regression in the guard could allow peer mentors to call the service directly via deep links, submitting records with incorrect attribution.

Mitigation & Contingency

Mitigation: Enforce role authorization at both the route guard level (coordinator-role-guard) and inside each service method independently. Write a security test that calls the service directly with a peer mentor session token and asserts rejection.

Contingency: If a bypass is discovered, immediately enable the server-side RLS policy as the final enforcement layer and audit any records written during the exposure window using the registered_by field.