Write unit tests for BadgeRepository
epic-achievement-badges-foundation-task-010 — Write unit and integration tests for BadgeRepository covering: fetchEarnedBadgesForUser returns only records scoped to the correct organisation_id, awardBadge inserts a record and the uniqueness constraint prevents duplicate awards, revokeBadge soft-deletes or removes the record, and RLS policy blocks cross-organisation access. Use a Supabase test database or mock the Supabase client with mocktail. Achieve at least 80% line coverage.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
When mocking Supabase with mocktail, mock at the SupabaseClient level rather than the http layer — this keeps tests closer to the repository's actual API surface. Set up a MockSupabaseClient that returns controlled responses for the specific table queries BadgeRepository issues. For the RLS test, simulate the policy by configuring the mock to return an empty list when the queried user_id belongs to a different organisation — the actual RLS enforcement is a Supabase concern, but the repository must handle the empty/error response gracefully. For the uniqueness constraint test, configure the mock to throw a PostgrestException with code '23505' (unique_violation) on the duplicate insert, then assert BadgeRepository wraps this into a domain-level DuplicateBadgeException.
Follow the existing test file structure already established in the project (check test/ directory for conventions).
Testing Requirements
Use flutter_test as the test runner and mocktail for mocking the Supabase client. Organise tests in group() blocks by method name (fetchEarnedBadgesForUser, awardBadge, revokeBadge). For each group write: one happy-path test, at least one error/edge-case test, and one organisation-scoping/RLS test. If integration tests against a real Supabase instance are included, place them in a separate test/integration/ directory and gate them behind an environment variable (RUN_INTEGRATION_TESTS=true) so they do not block CI by default.
Generate and inspect the LCOV report to confirm the 80% line coverage threshold is met before marking the task complete.
Badge criteria are stored as structured JSON in badge_definitions. If the JSON schema for criteria (threshold counts, streak lengths, training completion flags) is not well-defined upfront, the evaluation service will be built against a moving target, requiring costly migrations and refactors.
Mitigation & Contingency
Mitigation: Define and document the criteria JSON schema in a shared type file before any repository code is written. Review the schema with all three organisations' badge requirements — especially Blindeforbundet's honorar thresholds — and version the JSON schema using a 'criteria_version' field from day one.
Contingency: If the criteria schema must change after services are built, write a Supabase migration to backfill existing rows and add a migration version column. Keep the evaluation service criteria parser isolated behind an interface so only one function needs updating.
Badge icon assets may not yet exist or may fail WCAG 2.2 AA contrast validation (minimum 3:1 for graphical objects) when rendered over design-token backgrounds. Missing or non-compliant icons could block UI epic delivery for Blindeforbundet, for whom screen reader and visual accessibility is non-negotiable.
Mitigation & Contingency
Mitigation: During this epic, implement the contrast-ratio validator in badge-icon-asset-manager and run it as a Flutter test against all candidate icon assets early. Coordinate with the design team to provide WCAG-compliant SVG icons in both locked and unlocked variants before the UI epic begins.
Contingency: If assets are late or fail contrast checks, ship placeholder icons that are guaranteed compliant (solid design-token colour fills with text labels) and swap in final assets post-QA without requiring a code change.