Write unit tests for all ClaimStatus branches and partial failure paths
epic-mileage-reimbursement-entry-claim-orchestration-task-007 — Write unit tests for MileageClaimService covering: (1) silent auto-approval path for below-threshold claims with no additional expenses, (2) pending-review path for above-threshold or extra-expense claims, (3) persistence failure causes submitClaim() to return a failure outcome, (4) cache update failure does not affect submission success, (5) correct SubmissionOutcome type is returned for each branch. Use mock implementations for all injected dependencies.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 6 - 158 tasks
Can start after Tier 5 completes
Implementation Notes
Create mock classes for: (1) MileageClaimRepository with stubbed `persist()` returning success or throwing, (2) DistancePrefillService with stubbed `updatePrefill()` returning void or throwing, (3) ClaimStatusResolver (or equivalent) with stubbed `resolve()` returning the desired ClaimStatus. Use `setUp()` to reset mocks before each test. For the boundary test on auto-approval threshold, explicitly test the edge value to document the intended semantics (strict less-than vs. less-than-or-equal).
Group structure suggestion: `group('MileageClaimService', () { group('submitClaim status resolution', ...), group('persistence failure handling', ...), group('cache update graceful degradation', ...) })`. If using Mockito, run `dart run build_runner build` to generate mocks before running tests.
Testing Requirements
All tests are pure unit tests using flutter_test. Use Mockito's `@GenerateMocks` or hand-written fakes for all dependencies. Structure tests with `group()` blocks per logical scenario. Use `expect()` with typed matchers (e.g., `isA
For async methods, use `expectLater` with `completion()` or `await` inside `test()`. Aim for 100% branch coverage of MileageClaimService.submitClaim(). Run with `flutter test --coverage` and verify coverage report shows all branches hit.
The auto-approval rule requires checking whether any additional expense lines are attached to the claim. The interface between the mileage claim and any co-submitted expense items is not fully defined within this feature's component scope. If the domain model does not include an explicit additionalExpenses collection, the evaluator cannot make a correct determination, which could auto-approve claims that should require manual review.
Mitigation & Contingency
Mitigation: Define the MileageClaim domain object interface with an explicit additionalExpenses: List field (nullable/empty for mileage-only claims) before implementing the service. Coordinate with the Expense Type Selection feature team to agree on the shared domain contract.
Contingency: If the cross-feature contract cannot be finalised before implementation, implement the evaluator to treat any non-null additionalExpenses list as requiring manual review and document the assumption for review during integration testing.
A peer mentor who taps the submit button multiple times rapidly (e.g. due to slow network) could cause MileageClaimService to be invoked concurrently, resulting in duplicate claim records being persisted with the same trip data.
Mitigation & Contingency
Mitigation: Implement a submission-in-progress guard in MileageClaimService using a BLoC/Cubit state flag that prevents re-entrant calls. The UI layer (implemented in Epic 4) will also disable the submit button during processing.
Contingency: Add a Supabase-level unique constraint or idempotency key on (user_id, origin, distance, submitted_at truncated to minute) to prevent duplicate rows reaching the database even if the application guard fails.