Write unit tests for BadgeIconAssetManager
epic-achievement-badges-foundation-task-012 — Write unit tests for BadgeIconAssetManager covering: resolveIconPath returns correct asset path for locked and unlocked variants, fallback to placeholder icon when asset does not exist, validateContrastRatio correctly computes pass/fail for known colour pairs (e.g., white on dark blue passes, light grey on white fails WCAG AA), and the service initialises without errors when the asset manifest is loaded.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
validateContrastRatio must implement the full WCAG relative luminance formula. For sRGB channels, linearise using: if (c <= 0.04045) c/12.92 else ((c+0.055)/1.055)^2.4. Then L = 0.2126*R + 0.7152*G + 0.0722*B. Contrast = (lighter + 0.05) / (darker + 0.05).
Flutter's Color class stores ARGB as integers — extract channels via color.red / 255.0, color.green / 255.0, color.blue / 255.0. For the asset existence check in resolveIconPath, accept a Set
Testing Requirements
Use flutter_test. BadgeIconAssetManager should be designed for testability — inject an asset-existence checker (or an abstract AssetManifest interface) so tests can simulate missing assets without touching the real asset bundle. Test the contrast ratio computation with at least five known colour pairs: white/dark-blue (pass), grey/white (fail), black/white (pass, maximum), black/yellow (pass, ~19:1), and medium-grey/medium-grey (fail). For each pair, compare the computed ratio against a pre-calculated reference value accurate to two decimal places.
Do not use flutter_test's TestWidgetsFlutterBinding unless necessary — plain unit tests are sufficient for pure logic like contrast computation.
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.