high priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

Integration test covers the full happy path from activity template + mentor list input to BulkResultsScreen rendering with per-mentor outcomes
Test verifies that the BulkRegistrationService.submitBatch() call receives only the mentors that were not removed in the confirmation screen (filtered list)
Widget tests for mentor removal: tap remove on mentor A, assert mentor A moves to removed section, assert confirm button label decrements count, tap undo, assert mentor A returns to active list
Integration test verifies duplicate warnings are rendered for mentors flagged by the pre-check mock response
Unit tests for BulkRegistrationService aggregation: given a batch of 5 mentor submissions where 2 fail, the aggregated BulkRegistrationResult has successCount=3, failureCount=2, and perMentorOutcomes with correct per-entry success/error values
Test covers the retry-failed path: from BulkResultsScreen, tap 'Retry failed', assert BulkConfirmationScreen is pushed with only the 2 failed mentors pre-populated
Test covers the all-success outcome: BulkResultsScreen shows success illustration and no 'Retry failed' button
All tests use mocked Supabase/service dependencies with synthetic data
Tests run via flutter test and complete within 90 seconds on standard CI hardware
Test files are located in test/integration/ and test/unit/ following project conventions

Technical Requirements

frameworks
Flutter
flutter_test
BLoC/Cubit
mocktail
apis
BulkRegistrationService.preCheck(List<BulkMentorEntry>) -> Future<List<BulkMentorPreCheckResult>>
BulkRegistrationService.submitBatch(List<BulkMentorEntry>) -> Future<BulkRegistrationResult>
data models
BulkMentorEntry
BulkMentorPreCheckResult (mentorId, duplicates: List<DuplicateActivityDetail>)
BulkRegistrationResult
MentorRegistrationOutcome
performance requirements
Full integration test must complete within 90 seconds
Unit test suite for BulkRegistrationService must complete within 10 seconds
security requirements
All test fixtures use synthetic UUIDs — no production mentor or coordinator IDs
Mocked service responses must not include real activity or user data

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Create test helper factories for all bulk domain objects (BulkMentorEntry.fixture(), BulkRegistrationResult.mixedFixture(successCount: 3, failureCount: 2)) to keep test bodies readable. For the integration test, inject mocked BulkRegistrationService via the dependency injection layer (Provider override or GetIt test setup) rather than patching at the HTTP level — this isolates the test from networking and is more reliable. Use mocktail's when().thenAnswer() to simulate async service delays (e.g., Future.delayed(Duration(milliseconds: 100))) to ensure loading state is observable in widget tests. For the retry-failed path test, use go_router's RouterProvider.override with a test observer that captures navigation events to assert the correct screen is pushed with the correct mentor list extra.

Parameterise the BulkRegistrationService aggregation unit tests with test cases (all-success, all-failure, mixed, single-mentor batch) to maximise coverage with minimal boilerplate.

Testing Requirements

Four test layers required: (1) BulkRegistrationService unit tests — test aggregation logic with mixed success/failure batch responses; test preCheck correctly maps server response to per-mentor duplicate lists; test empty batch input returns empty result without calling Supabase. (2) BulkConfirmationCubit unit tests — test removeMentor/undoRemoval state transitions; test submitBatch is called with activeMentors only; test loading/success/failure state transitions. (3) Widget tests — mount BulkConfirmationScreen with cubit overrides; drive removal interaction; assert confirmation button label updates; assert duplicate warning badge visibility per flagged mentor. (4) Integration tests — drive full flow from BulkMentorSelectionScreen through BulkConfirmationScreen to BulkResultsScreen using stubbed services; capture submitBatch argument and assert filtered list; assert BulkResultsScreen per-mentor rows; drive retry-failed navigation.

Component
Bulk Registration Service
service high
Epic Risks (3)
medium impact medium prob scope

If the batch insert RPC returns a mix of successes and failures (e.g., 3 of 10 mentors fail due to constraint violations that slipped through application-level duplicate detection), the confirmation screen result state becomes ambiguous. A coordinator who sees '7 of 10 succeeded' may not know whether to manually register the 3 failures, retry, or escalate — leading to either duplicate registrations or silent underreporting.

Mitigation & Contingency

Mitigation: Design the Bulk Registration Service to return a strongly typed BulkRegistrationResult with per-mentor RegistrationOutcome (success | duplicate_detected | constraint_violation | permission_denied). Design the result screen to list each failed mentor with a specific, plain-language reason and a one-tap 'Retry for this mentor' action that pre-fills the activity wizard with the batch template for that individual.

Contingency: If per-mentor retry UI is too complex to deliver within the epic scope, fall back to displaying failed mentors with their error codes and instructing coordinators to use single-proxy mode for the failures. Document this as a known limitation in release notes and create a follow-up ticket for per-mentor retry in the next sprint.

medium impact medium prob dependency

The Proxy Activity Wizard must reuse the existing activity wizard step widgets (type, date, duration, notes) while injecting a proxy attribution banner and a different submission payload builder. If the existing wizard is not designed for composability, the proxy variant may require forking the widget tree, creating two maintenance-diverging codebases that will drift out of sync when the base wizard is updated (e.g., new activity types added, new mandatory fields).

Mitigation & Contingency

Mitigation: Before implementing the Proxy Activity Wizard, audit the existing activity wizard's architecture. If steps are already extracted as independent StatelessWidget/ConsumerWidget classes, compose them directly with a wrapping Column that injects the attribution banner. If they are tightly coupled inside a parent widget, refactor the existing wizard to accept a nullable ProxyContext parameter before starting the proxy variant — this refactor should be a prerequisite task in this epic.

Contingency: If refactoring the base wizard is blocked by unrelated in-flight work on that component, implement the proxy wizard as a full fork but create a shared StepWidgets library file that both the base wizard and proxy wizard import. Schedule a deduplication refactor as a tech-debt ticket in the next planning cycle.

medium impact medium prob technical

The bulk registration flow spans three sequential screens (multi-select → activity form → confirmation → result) with shared mutable state: the selected mentor list, the activity template, the per-mentor duplicate warnings, and the final submission result. Managing this state across screens without a well-designed Bloc risks state leaks, stale duplicate warning data after mentor removal, and confirmation screen inconsistencies if the user navigates back and changes the mentor selection.

Mitigation & Contingency

Mitigation: Define a single BulkRegistrationBloc (or Cubit) with explicit state transitions covering: MentorsSelected → ActivityTemplateCompleted → DuplicatesChecked → ConfirmationReady → Submitting → SubmissionResult. Each backward navigation event (e.g., 'Back' from confirmation to mentor selection) dispatches a ResetToMentorSelection event that clears downstream state. Unit test every state transition with edge cases including empty mentor list, all mentors having duplicates, and network failure during submission.

Contingency: If state management complexity causes persistent bugs in testing, simplify by passing state explicitly through Navigator arguments (immutable snapshots per screen) rather than a shared Bloc. This reduces flexibility but eliminates cross-screen state mutation bugs.