high priority medium complexity testing pending testing specialist Tier 2

Acceptance Criteria

AnimationController instances are created and attached to the TickerProvider before the first frame is rendered (verified via WidgetTester.pump())
After dispose() is called, all AnimationController.isDisposed properties return true and no further ticker callbacks are received
play() transitions AnimationController.status to AnimationStatus.forward
pause() transitions AnimationController.status to AnimationStatus.completed or AnimationStatus.dismissed (no forward/reverse in progress) without throwing
triggerRiveInput(inputName, value) does not throw a StateError or NoSuchMethodError when the Rive StateMachineController is null (asset absent or not yet loaded)
triggerRiveInput correctly calls StateMachineController.findInput and sets the input value when Rive is available (mocked)
Tests use WidgetTester with `tester.pumpWidget()` and a real SingleTickerProviderStateMixin or a TestVSync to avoid 'Ticker created but not started' leaks
No leaked AnimationController warnings appear in the test output (verify via Flutter's test framework leak detection)
All rive package imports are mockable without requiring a real .riv asset file to be present in the test environment
Tests complete without flakiness across 3 consecutive runs in CI

Technical Requirements

frameworks
flutter_test
rive (^0.13.x or project-pinned version)
mockito for RiveFile / StateMachineController mocking
apis
AnimationController (play, pause, dispose, status, isDisposed)
WrappedAnimationController (public API under test)
StateMachineController.findInput<T>(name)
WidgetTester.pump() / pumpWidget()
SingleTickerProviderTestMixin or vsync: TestVSync()
data models
annual_summary
performance requirements
Widget tests complete within 2 seconds total with no real animation frames rendered
security requirements
No real .riv asset files should be loaded from the network during tests; use a mock or empty fallback RiveFile
ui components
WrappedAnimationController (the widget/mixin under test)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

WrappedAnimationController likely implements State with TickerProviderStateMixin or is a standalone class accepting a TickerProvider. In tests, use `tester.pumpWidget(MaterialApp(home: TestHarness()))` where TestHarness creates the controller. To test initialisation before first frame, use `tester.pump(Duration.zero)` and inspect controller state immediately after pumpWidget. For dispose testing, grab a reference to the controller, call `tester.pumpWidget(Container())` to remove the widget from tree (triggering dispose), then assert `controller.isDisposed`.

For Rive mocking, create a `MockStateMachineController` that implements the minimum interface needed; inject it via a setter or factory parameter. If WrappedAnimationController does not currently accept a TickerProvider via injection, refactor to add a `@visibleForTesting` constructor parameter before writing tests. Avoid using `await tester.pumpAndSettle()` for animation tests — use explicit `pump(duration)` calls to control time precisely.

Testing Requirements

Widget tests using flutter_test's WidgetTester. Create a minimal test harness widget that instantiates WrappedAnimationController with a TestVSync (or by mounting it inside a StatefulWidget with SingleTickerProviderStateMixin). Test groups: 'initialisation before first frame', 'dispose lifecycle', 'play and pause state transitions', 'triggerRiveInput graceful fallback', 'triggerRiveInput with mock Rive'. Use `addTearDown(controller.dispose)` in each test to prevent leaked tickers.

Use `expect(tester.takeException(), isNull)` after triggerRiveInput with null Rive to verify no exception propagates. Mock RiveFile loading using a stub that returns an empty RiveFile or null.

Component
Wrapped Animation Controller
infrastructure high
Epic Risks (3)
medium impact medium prob dependency

Rive animation files may not be available at implementation time, blocking the wrapped-animation-controller from being fully tested. If asset delivery is delayed, the controller cannot be validated for memory-leak-free disposal.

Mitigation & Contingency

Mitigation: Implement the animation controller with stub/placeholder AnimationController instances first so the lifecycle and disposal logic can be unit-tested independently of Rive assets. Define a named animation registry interface early so UI components can reference animations by name without coupling to specific Rive files.

Contingency: If Rive assets are not delivered before Epic 3 begins, replace Rive animations with Flutter implicit animations (AnimatedOpacity, ScaleTransition) as a drop-in and schedule Rive integration as a follow-on task once assets arrive.

high impact medium prob technical

The annual_summaries Supabase RPC aggregating 12 months of activity records per mentor may exceed acceptable query latency (>2s) for mentors with high activity volumes such as the HLF mentor with 380 registrations cited in workshop notes.

Mitigation & Contingency

Mitigation: Design the RPC to materialise summary results into the annual_summaries table via a scheduled edge function rather than computing on demand. The repository reads pre-computed rows, keeping query latency constant regardless of activity volume.

Contingency: If on-demand queries are required for real-time period switching, add a PostgreSQL partial index on (mentor_id, activity_date) and implement a client-side loading skeleton so slow queries degrade gracefully rather than blocking the UI.

medium impact low prob technical

iOS requires photo library permission before saving a screenshot to the gallery. If the permission prompt is triggered at an unexpected point in the share flow, the UX breaks and users may deny permission permanently, making gallery save unavailable.

Mitigation & Contingency

Mitigation: Trigger the permission request only when the user explicitly chooses 'Save to gallery' in the share overlay, not on screen load. Implement a pre-prompt explanation screen following Apple HIG so users understand why the permission is needed before the system dialog appears.

Contingency: If permission is denied, gracefully fall back to clipboard copy and system share sheet options which do not require photo library access, and surface a non-blocking snackbar explaining the limitation.