high priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

Test file is located under test/ mirroring the source path (e.g., test/mileage/mileage_claim_service_test.dart)
Test group 'auto-approval path' contains at least 2 tests: (a) distance below threshold with no extra expenses → ClaimStatus.autoApproved, (b) distance exactly at threshold boundary → correct status based on threshold comparison semantics (strictly below or below-or-equal)
Test group 'pending-review path' contains at least 3 tests: (a) distance above threshold → ClaimStatus.pendingReview, (b) distance below threshold but with at least one extra expense → ClaimStatus.pendingReview, (c) distance above threshold AND extra expenses → ClaimStatus.pendingReview
Test 'persistence failure' verifies that when the persistence layer throws, submitClaim() returns a SubmissionOutcome of failure type — not a success type and not an unhandled exception
Test 'cache update failure isolation' verifies that when DistancePrefillService.updatePrefill() throws, submitClaim() still returns a SubmissionOutcome of success type
All injected dependencies (persistence repository, DistancePrefillService, status resolver) are replaced with Mockito or manual fake implementations — no real Supabase calls
All tests pass with flutter_test runner: `flutter test test/mileage/mileage_claim_service_test.dart`
Test descriptions are in English and clearly state the scenario and expected outcome
No test depends on execution order — each test sets up its own mock state independently

Technical Requirements

frameworks
Flutter
Dart
flutter_test
mockito
apis
MileageClaimService.submitClaim()
DistancePrefillService.updatePrefill()
MileageClaimRepository (persistence layer mock)
data models
MileageClaim
SubmissionOutcome
ClaimStatus
OrgRateConfig
performance requirements
All unit tests must complete within 5 seconds total
No async timeouts or real delays — use synchronous fake completions in mocks
security requirements
Test fixtures must not include real BankID tokens, real user IDs, or production Supabase credentials

Execution Context

Execution Tier
Tier 6

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()`, `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.

Component
Mileage Claim Service
service medium
Epic Risks (2)
high impact medium prob integration

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.

medium impact medium prob technical

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.