high priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

StatsBloc: blocTest verifies StatsLoading emitted immediately on StatsLoadRequested event
StatsBloc: blocTest verifies StatsLoaded emitted with correct view model when service returns data
StatsBloc: blocTest verifies StatsError emitted with typed error when service throws
StatsBloc: blocTest verifies FilterChanged event with debounce — rapid successive FilterChanged events result in a single service call (use FakeAsync to advance debounce duration)
StatsBloc: blocTest verifies activity stream invalidation event triggers a reload without explicit user action
StatsBloc: blocTest verifies auto-refresh timer emits StatsLoadRequested at configured interval
PersonalStatsBloC: blocTest covers same loading/loaded/error transitions for personal stats
FlChartAdapter (bar): widget test verifies BarChartGroupData colors match AppColors design token values (not hardcoded hex)
FlChartAdapter (bar): widget test renders without exception for empty list input
FlChartAdapter (bar): widget test renders without exception for single-item and multi-item inputs
FlChartAdapter (pie): widget test verifies PieChartSectionData colors match design tokens
FlChartAdapter (pie): widget test renders without exception for empty and populated CoordinatorStatsViewModel
All bloc tests use bloc_test package actExpect or build/act/expect pattern
Widget tests use pumpWidget with MaterialApp wrapper and design token theme injected

Technical Requirements

frameworks
Flutter
Dart
flutter_test
bloc_test
mockito
fl_chart
data models
CoordinatorStatsViewModel
PersonalStatsViewModel
ActivityTypeStats
performance requirements
Debounce test must use FakeAsync — no real timers in test suite
Widget tests must pump and settle within 500ms per test
ui components
FlChartAdapter
BarChartGroupData
PieChartSectionData

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

The most error-prone part is the debounce test: use FakeAsync.withZone, add multiple FilterChanged events, advance time by less than the debounce duration, verify service not called, then advance past debounce and verify exactly one call. For the activity stream invalidation test, expose a StreamController in the mock service and add an event to it mid-test to trigger the BLoC reaction. For FlChartAdapter widget tests, wrap in a ProviderScope or BlocProvider as appropriate and inject a ThemeData carrying design tokens — this ensures colour assertions test the real token resolution path. Avoid testing fl_chart internal rendering; only assert on the data objects the adapter constructs.

Testing Requirements

BLoC tests with bloc_test package using blocTest() helper. Widget tests with flutter_test pumpWidget. Use mockito mocks for CoordinatorStatsService and PersonalStatsService. For debounce tests, use FakeAsync from fake_async package to control the event loop.

Design token colour assertions should compare Color values from the app's AppColors constants, not hardcoded hex strings, so that the test breaks if tokens change unexpectedly. Each BLoC gets its own test file. Adapter widget tests live in a separate adapters_test.dart file.

Component
Coordinator Statistics BLoC
infrastructure medium
Epic Risks (3)
medium impact medium prob technical

fl_chart's default colour palette may not meet WCAG 2.2 AA contrast requirements when rendered on the app's dark or light backgrounds. If segment colours are insufficient, the donut chart will fail accessibility audits, which is a compliance blocker for all three organisations.

Mitigation & Contingency

Mitigation: Define all chart colours in the design token system with pre-validated contrast ratios. Run the contrast-ratio-validator against every chart colour during the adapter's unit tests. Use the contrast-safe-color-palette as the source palette.

Contingency: If a colour fails validation, replace with the nearest compliant token. If activity types exceed the available token set, implement a deterministic hashing algorithm that maps activity type IDs to compliant colours.

medium impact medium prob technical

StatsBloc subscribing to the activity registration stream creates a long-lived subscription. If the subscription is not disposed correctly when the dashboard is closed, it will cause a stream leak and potentially trigger re-fetches on a disposed BLoC, resulting in uncaught errors in production.

Mitigation & Contingency

Mitigation: Implement subscription disposal in the BLoC's close() override. Write a widget test that navigates away from the dashboard and asserts no BLoC events are emitted after disposal.

Contingency: If leaks are detected in QA, add a mounted check guard before emitting states from async callbacks, and audit all other BLoC stream subscriptions in the codebase for the same pattern.

low impact low prob scope

PersonalStatsService's Phase 4 gamification data structure is designed against an assumed future schema. If the Phase 4 Spotify Wrapped feature defines a different data contract when it is developed, the structure built now will require a breaking change and migration.

Mitigation & Contingency

Mitigation: Document the contribution data structure with explicit field semantics and versioning comments. Keep the Phase 4 fields as optional/nullable so they do not break existing consumers if the schema evolves.

Contingency: If the Phase 4 schema diverges significantly, the personal stats data can be re-mapped in a thin adapter layer without changing PersonalStatsService's core implementation.