high priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

Widget test suite contains at least one test group per layout variant: peer_mentor and coordinator
Peer mentor layout test verifies certificate name Text widget is present and matches the seeded certification entity name
Peer mentor layout test verifies expiry date is rendered in a human-readable format (dd MMM yyyy) matching the certification.expires_at value
Peer mentor layout test verifies a status indicator widget (e.g. badge or icon) is visible and reflects the correct certification status (expiring_soon or expired)
Peer mentor layout test verifies exactly one action button is rendered: view-certification-status
Coordinator layout test verifies all peer mentor layout elements are also present
Coordinator layout test verifies two additional action buttons are rendered: trigger-course-enrollment and acknowledge-lapse
Role-gating test: when peer mentor role is injected, coordinator-only action buttons are absent from the widget tree
Role-gating test: when coordinator role is injected, peer mentor variant renders without coordinator-only actions
Tapping view-certification-status button dispatches the correct BLoC event (e.g. ViewCertificationStatusRequested) verified via a MockBloc or FakeBloc
Tapping trigger-course-enrollment dispatches the correct BLoC event (e.g. TriggerCourseEnrollmentRequested)
Tapping acknowledge-lapse dispatches the correct BLoC event (e.g. AcknowledgeLapseRequested)
Every interactive control has a Semantics widget with a non-empty label matching the control's visible text or purpose
All Semantics nodes pass flutter_test SemanticsChecker with no missing labels on focusable elements
All tests pass with flutter test --reporter expanded and zero skipped assertions

Technical Requirements

frameworks
Flutter
flutter_test
BLoC (bloc_test package)
mocktail or mockito
data models
certification
performance requirements
Each widget test must complete in under 2 seconds on CI
No real network calls — all data injected via fake repositories or seeded BLoC states
security requirements
No real user credentials or certification PII used in test fixtures — use anonymised stub data only
ui components
NotificationDetailView
ExpiryStatusBadge
ActionButton (view-certification-status)
ActionButton (trigger-course-enrollment)
ActionButton (acknowledge-lapse)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Create a test file at test/features/certificate_expiry_notifications/notification_detail_view_test.dart. Use setUp to create a fresh MockBloc per test to avoid state leakage. Define const test keys for each conditional widget (e.g. const Key('action_view_certification_status')) in the widget implementation so tests can target them reliably without depending on text strings that may be localised.

Use a helper method buildWidget(role, certificationState) to reduce boilerplate across test cases. For BLoC event verification, prefer bloc_test's verify() helper over capturing events manually. Ensure Semantics wrapper is added to each interactive button in the widget under test before writing the semantics assertions — do not write tests that will fail against the current implementation unless a corresponding implementation fix is included in the same PR.

Testing Requirements

Use flutter_test with WidgetTester. Wrap the widget under test in a BlocProvider supplying a MockBloc or FakeBloc seeded with a CertificationExpiryState containing a stub certification entity. Use tester.pumpWidget with a MaterialApp wrapper. For role-gating, inject role via the appropriate BLoC or dependency-injected auth context.

Use expect(find.byKey(...), findsOneWidget) and expect(find.byKey(...), findsNothing) assertions for role-conditional widgets. Verify BLoC event dispatch with bloc_test verify helpers or by capturing emitted events on the FakeBloc. Run tester.ensureSemantics() and use SemanticsController to assert label presence. Aim for 100% branch coverage across role variants and action dispatch paths.

Epic Risks (2)
medium impact medium prob technical

The persistent banner must remain visible across app sessions and only disappear when a specific backend condition is met (renewal or coordinator acknowledgement). If the BLoC state is not properly sourced from the notification record repository on every app launch, the banner may disappear prematurely or fail to reappear after a session restart.

Mitigation & Contingency

Mitigation: Drive the banner's visibility exclusively from a Supabase real-time subscription on the notification records table filtered by mentor_id and acknowledged_at IS NULL. Never persist banner visibility state locally. Write an integration test that restarts the BLoC and verifies the banner reappears from the database source.

Contingency: If real-time subscriptions introduce latency or connection reliability issues in offline-first scenarios, add a local cache flag that is only cleared when the repository confirms the acknowledgement write succeeded, with a cache TTL of 24 hours as a fallback.

high impact low prob security

The notification detail view must conditionally render coordinator-specific actions based on the authenticated user's role. Incorrect role resolution could expose the 'Acknowledge Lapse' action to peer mentors or hide it from coordinators, breaking the workflow and potentially allowing unauthorised state changes.

Mitigation & Contingency

Mitigation: Source the role check from the existing role_state_manager BLoC that is already authenticated against Supabase role claims. Do not rely on a local flag. The coordinator acknowledgement service backend also validates role server-side, providing defence in depth. Add widget tests that render the detail view with mentor and coordinator role fixtures and assert the presence or absence of coordinator actions.

Contingency: If a role resolution bug is found in production, immediately disable the acknowledge action via a feature flag and patch the role check in a hotfix release. The server-side validation in the coordinator acknowledgement service ensures no actual state change can occur even if the button is incorrectly rendered.