high priority low complexity testing pending testing specialist Tier 4

Acceptance Criteria

All five specified BLoC test scenarios are implemented: config load success, InputChanged recalculation, ResetRequested clear, ShareRequested preservation, and repository failure.
Each test uses bloc_test's blocTest() helper with correct act/expect/build structure.
BenefitMultiplierConfigRepository and BenefitCalculationService are fully mocked using mocktail with no real network or database calls.
Test for InputChanged verifies that the emitted BenefitCalculationResult matches the expected computed values based on mocked config and input.
Test for ResetRequested verifies state returns to the defined initial/empty state with null or zeroed fields.
Test for ShareRequested verifies the BLoC does not mutate the result state — the calculated values remain unchanged after the share event.
Test for repository fetch failure verifies an error state is emitted with a non-null error message and that no result fields are populated.
All tests pass with flutter_test and bloc_test runners without warnings.
Test file is placed under test/bloc/ with a naming convention matching the BLoC file (benefit_calculator_bloc_test.dart).
Code coverage for BenefitCalculatorBloc reaches at least 90% line coverage as verified by lcov or flutter test --coverage.

Technical Requirements

frameworks
Flutter
BLoC
bloc_test
mocktail
flutter_test
data models
BenefitCalculationResult
BenefitMultiplierConfig
ActivitySummary
performance requirements
All unit tests complete within 5 seconds total.
No async timeouts — use fake async or proper stream closures in bloc_test.
security requirements
No real Supabase credentials or network calls in test setup.
Mocked repository must not log sensitive multiplier values to console.

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use mocktail's when().thenAnswer() pattern to stub async repository methods returning Future. For the InputChanged scenario, create multiple blocTest() calls with varying input values to confirm recalculation correctness against the mocked BenefitCalculationService. For the ShareRequested test, emit a prior InputChanged to populate state, then fire ShareRequested and verify the result is unchanged. For the error test, use throwsA(isA()) in the stub and confirm the BLoC maps this to an error state rather than crashing.

Avoid using real timers — if the BLoC uses debounce on inputs, use FakeAsync from quiver or clock package. Ensure the BLoC is closed after each test to prevent stream leaks.

Testing Requirements

Unit tests only using bloc_test and mocktail. No integration or widget tests required for this task. Each BLoC event maps to at least one dedicated blocTest() call. Use setUp() to instantiate a fresh BLoC and close it in tearDown().

Verify both emitted states (intermediate loading state if applicable, final success/error state). Aim for 90%+ branch coverage of BenefitCalculatorBloc. Run with: flutter test test/bloc/benefit_calculator_bloc_test.dart.

Component
Benefit Calculator BLoC
infrastructure 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.