high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Successful Supabase fetch returns a correctly typed and mapped BenefitMultiplierConfig with all multiplier fields populated
On cache hit, the Supabase client mock is never called (verify zero interactions with the network layer)
On cache miss, Supabase client is called exactly once and the result is stored in the cache mock
When Supabase throws a network exception and a cached value exists, the repository returns the cached value without re-throwing
When Supabase throws and no cached value exists, the repository throws or returns a typed failure (consistent with repository contract)
force-refresh flag bypasses cache and always calls Supabase, then updates the cache with the fresh result
Repository correctly maps raw Supabase row (Map<String, dynamic>) to BenefitMultiplierConfig using the same field names as the database schema
All async test cases use proper async/await with no unawaited futures
Tests are isolated: each test resets mocks via setUp/tearDown
All tests pass in CI using flutter_test with mocktail or mockito

Technical Requirements

frameworks
flutter_test
mocktail
apis
Supabase PostgREST (mocked)
data models
BenefitMultiplierConfig
CacheAdapter
performance requirements
Each test completes in under 500ms (no real network calls)
Mock setup overhead is minimal — reuse mock instances across tests in the same group
security requirements
Mock Supabase credentials must never appear in test files — use placeholder strings only

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Define mock classes using mocktail's Mock base class for the Supabase query builder and the cache adapter interface. If the Supabase Flutter SDK's query builder is difficult to mock directly, introduce a thin SupabaseDataSource abstraction that the repository depends on — this makes the boundary clean and the mock trivial. For the cache adapter, define a simple abstract class (read/write/clear) and mock it. Use setUp() to reset mock state and registerFallbackValue() for any custom types used in when() matchers.

Place test file at test/repositories/benefit_multiplier_config_repository_test.dart.

Testing Requirements

Async unit tests using flutter_test and mocktail. Create mock classes for the Supabase client (or the table query interface) and the cache adapter. Structure tests in five groups: (1) successful fetch and mapping, (2) cache hit path, (3) cache miss path, (4) offline/error fallback, (5) force-refresh behavior. Use when/thenAnswer for async Supabase returns and verify() to assert interaction counts.

Aim for 100% branch coverage of the repository class. No real Supabase connection should be opened.

Epic Risks (2)
medium impact medium prob integration

Supabase organisation configuration table may not yet have the five multiplier columns, requiring a migration. If the migration is not coordinated with other teams touching the same table, schema conflicts could delay delivery.

Mitigation & Contingency

Mitigation: Add multiplier columns in a dedicated, non-destructive ALTER TABLE migration script. Review the organisation config table schema with the backend team before writing the migration. Use nullable columns with defaults so existing rows are unaffected.

Contingency: If the migration cannot be deployed in time, stub the repository to return hardcoded default multiplier values from a local config file, allowing parallel development. Swap in the real Supabase fetch once the migration is live.

low impact medium prob scope

The ActivitySummaryAggregator depends on activity records already persisted in the database. For newly onboarded peer mentors with no activity history, the aggregator will return zero counts, which could make the calculator appear broken on first use.

Mitigation & Contingency

Mitigation: Design the pre-fill value object to distinguish between 'no data yet' and 'zero activities'. The calculator input panel should display empty inputs (not zero) when no history exists, with placeholder text guiding the user to enter values manually.

Contingency: If the distinction cannot be surfaced cleanly in the UI timeline, fall back to always showing empty inputs and document the manual-entry path as the primary UX until activity data accumulates.