Write unit tests for BenefitCalculationService
epic-benefit-calculator-core-logic-task-004 — Write exhaustive unit tests for BenefitCalculationService covering nominal inputs, zero values, fractional hours, large km values, and boundary multiplier configs. Verify all three output dimensions (hours_saved, travel_cost_avoided, public_health_offset) match expected values. Use flutter_test; no mocks needed as the service is a pure function.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Structure the test file with a single top-level group('BenefitCalculationService', ...) containing nested group()s per input family. Use setUp() to create a shared service instance and a default config to reduce boilerplate. For the floating-point comparisons, define a helper const delta = 0.001 at the top of the file. Prefer table-driven tests (a list of input/output tuples iterated with for loops) for the fractional and nominal cases to maximise coverage with minimal code.
Ensure the test file is listed in the CI test command if not already covered by glob patterns.
Testing Requirements
All tests are unit tests using the flutter_test package. No widget tests, no integration tests, no mocks. Use group() to organise tests into logical families: 'nominal inputs', 'zero/boundary values', 'fractional hours', 'large values', 'invalid inputs'. Use expect() with closeTo() for doubles and throwsArgumentError for error cases.
Each test should have a descriptive name explaining the scenario and the expected outcome.
The exact formulas for SROI social value (public health system cost offset) may not be agreed upon with the client organisations or Bufdir. If formulas are disputed post-implementation, the service and all downstream tests will need to be revised.
Mitigation & Contingency
Mitigation: Document the two formulas and their multiplier inputs explicitly in the BenefitCalculationService source file and obtain sign-off from the product owner before implementation begins. Store formula multipliers exclusively in the Supabase config table so adjustments require only a config update, not a code deployment.
Contingency: If formulas are revised after implementation, the pure-function architecture means changes are isolated to BenefitCalculationService. Update the service, adjust unit tests, and re-run the test suite. No UI components need modification.
The BLoC must handle the asynchronous config fetch from the multiplier repository during initialisation. Race conditions between the config loading state and the first InputChanged event could result in calculations running against null or stale multiplier values.
Mitigation & Contingency
Mitigation: Guard all InputChanged event handlers in the BLoC with a null check on the loaded config state. Emit BenefitCalculationLoading until config resolves. Write a BLoC test that fires InputChanged before config loads and asserts the state remains BenefitCalculationLoading.
Contingency: If race conditions surface in integration testing, add an explicit config-loaded flag and queue InputChanged events until the flag is set, draining the queue on config resolution.