high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test: insertNotification(notification) asserts that the Supabase insert call receives a map with all required fields (user_id, org_id, title, body, assignment_id, created_at) and returns the inserted record's id
Test: markAsRead(notificationId, userId) asserts that the update call targets only the record matching both notificationId AND userId (dual-filter prevents one user marking another's notification read)
Test: fetchUnread(userId, orgId) asserts that the query filters by user_id = userId AND org_id = orgId AND is_read = false, and returns a list of typed InAppNotification domain models
Test: fetchByAssignment(assignmentId, orgId) asserts correct filter parameters and returns notifications scoped to that assignment
Test: deleteOldRecords(olderThan: DateTime) asserts that the delete call uses a lt filter on created_at with the correct timestamp value
Test: fetchUnread called with a different org_id (simulating RLS cross-org attempt) — mock returns empty list — asserts repository returns empty list without error (RLS silently scopes, not throws)
All mock stubs verify the exact filter parameters passed (use argument captors or verify() calls) — not just that a method was called
Test file passes flutter test with zero warnings

Technical Requirements

frameworks
Flutter
flutter_test
mockito or mocktail
apis
Supabase Flutter SDK (mocked)
data models
InAppNotification
NotificationStatus
performance requirements
Test suite completes in under 3 seconds
security requirements
Test for markAsRead must assert dual-filter (notificationId + userId) to prevent IDOR — this is a security regression test
Cross-org fetch test must confirm org_id is always included in the query filter

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

The dual-filter requirement on markAsRead (notificationId AND userId) is a deliberate security control — ensure the repository implementation uses .eq('id', notificationId).eq('user_id', userId) in the update chain, and the test verifies both filters are present. The deleteOldRecords method likely uses .lt('created_at', olderThan.toIso8601String()) — the test should pass a known DateTime and assert the exact ISO string. For the cross-org RLS test: Supabase RLS silently returns empty sets (no 403), so the expected behavior is an empty list, not an exception. This is different from the ReminderConfigRepository where an empty result for fetchConfig is unexpected and should throw.

Testing Requirements

Unit tests only. Pay particular attention to the markAsRead dual-filter test — this is a security-relevant assertion (IDOR prevention). Use argument captors (mocktail's any() + verify()) to assert the exact Map passed to the Supabase update/insert calls, not just that the method was called. For the deleteOldRecords test, freeze time using a fixed DateTime in the test to make the lt filter assertion deterministic.

Group tests logically using group() blocks: 'insert operations', 'read operations', 'update operations', 'delete operations', 'RLS scoping'.

Component
In-App Notification Repository
data low
Epic Risks (3)
high impact medium prob integration

Adding last_contact_date to the assignments table may conflict with existing RLS policies or trigger-based logic that monitors the assignments table. If the migration is not carefully reviewed, existing assignment management features could break in production.

Mitigation & Contingency

Mitigation: Review all existing triggers, policies, and foreign key constraints on the assignments table before writing the migration. Run the migration against a staging Supabase instance with production-like data and execute the full existing test suite before merging.

Contingency: Roll back the migration using Supabase's versioned migration history. Apply the schema change as an additive-only migration (nullable column with default) to ensure zero downtime and reversibility.

medium impact medium prob dependency

The PushNotificationService wraps an existing FCM integration whose internal API contract may have changed or may not expose the payload formatting required for deep-link CTAs. Misalignment discovered late delays the dispatch service epic.

Mitigation & Contingency

Mitigation: Before implementing the wrapper, read the existing push notification integration code and confirm the method signatures, payload structure, and token management model. Agree on a stable interface contract in a shared Dart abstract class.

Contingency: If the existing service is incompatible, implement a thin adapter layer that translates reminder payloads to the existing service's format, isolating the reminder feature from upstream changes.

high impact low prob security

Incorrect RLS policies on notification_log could allow coordinators to read reminder records belonging to peer mentors in other chapters, exposing sensitive assignment information across organisational boundaries.

Mitigation & Contingency

Mitigation: Write explicit RLS policies with integration tests that assert cross-chapter queries return zero rows. Use Supabase's built-in auth.uid() and join through the org membership tables to scope all queries.

Contingency: If a policy gap is discovered post-merge, immediately disable the affected table's SELECT policy, deploy a corrected policy, and audit recent queries in Supabase logs for any cross-boundary reads.