high priority low complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test file is located at test/benefit_calculator/benefit_calculation_service_test.dart
All tests pass with flutter test and zero failures
Minimum 15 distinct test cases covering the specified input families
Nominal input test: 2.0 h, 20.0 km, 5 activities with realistic multipliers produces expected non-zero result
All-zeros test: 0 h, 0 km, 0 activities with any positive config returns BenefitCalculationResult with all zeros
Fractional hours tests: 0.25, 0.5, 1.5 hours each verified to two decimal places
Large km test: 500 km does not throw and returns a finite double
Boundary multiplier tests: config with all rates = 1.0 returns output values equal to input values
Boundary multiplier tests: config with all rates = 0.0 returns all-zero result
Negative input tests: negative duration_hours, negative distance_km, and negative activities_count each throw ArgumentError
total_societal_value equality assertion is present in every test case
Tests use closeTo() matcher for floating-point comparisons with a delta of 0.001
No test imports Flutter widgets or platform channels — pure Dart test environment

Technical Requirements

frameworks
flutter_test
data models
BenefitCalculationResult
BenefitMultiplierConfig
ActivityInput
performance requirements
Full test suite must complete in under 5 seconds

Execution Context

Execution Tier
Tier 3

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.

Component
Benefit Calculation Service
service low
Epic Risks (2)
medium impact medium prob scope

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.

medium impact low prob technical

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.