high priority low complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test file exists at `test/repositories/notification_record_repository_test.dart`
Test: createNotificationRecord(mentorId, thresholdDays, notificationType) calls Supabase insert with the correct payload — verified via mock argument capture
Test: hasNotificationBeenSentForThreshold(mentorId, threshold: 30) returns true when a matching row exists in the mock response
Test: hasNotificationBeenSentForThreshold(mentorId, threshold: 30) returns false when the mock response is an empty list
Test: hasNotificationBeenSentForThreshold(mentorId, threshold: 60) returns false even when a 30-day record exists (thresholds are distinct)
Test: markAsRead(notificationId) calls Supabase update with `{is_read: true}` filtered to the correct notification_id
Test: markAllAsRead(userId) calls Supabase update with `{is_read: true}` filtered to the correct user_id
Test: getUnreadCount(userId) returns the correct integer count from a mock response containing multiple rows
Test: getUnreadCount(userId) returns 0 when mock response is empty
Test: Supabase PostgrestException during createNotificationRecord is re-thrown as NotificationRecordRepositoryException
Test: JSON round-trip — a NotificationRecord serialized to JSON and deserialized back produces an equal object (using `==` override or field-by-field comparison)
Test: concurrent write assumption — if two createNotificationRecord calls are made simultaneously for the same mentorId+threshold, the unique constraint is expected to reject the second (test documents this assumption; actual constraint enforcement is a DB concern)
Minimum 80% method coverage on NotificationRecordRepository

Technical Requirements

frameworks
flutter_test
mocktail for Supabase mocking
Riverpod (if repository is provided via Riverpod — override provider in tests)
apis
Supabase Flutter SDK (mocked)
NotificationRecordRepository public API
data models
NotificationRecord (notification_id, mentor_id, threshold_days, notification_type, is_read, created_at, sent_at)
NotificationReadStatus enum (if applicable)
performance requirements
Full test suite must complete within 10 seconds
No network I/O — all calls mocked
security requirements
JSON fixtures must use synthetic data only
No real Supabase keys in test code

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Structure the test file in `group()` blocks matching the repository methods: `group('createNotificationRecord', ...)`, `group('hasNotificationBeenSentForThreshold', ...)`, `group('markAsRead / markAllAsRead', ...)`, `group('getUnreadCount', ...)`, `group('error handling', ...)`, `group('JSON serialization', ...)`. For the threshold distinctness test (30-day record does not satisfy 60-day check), ensure the mock is set up to return a row with `threshold_days: 30` and verify that `hasNotificationBeenSentForThreshold(mentorId, threshold: 60)` queries for `threshold_days = 60` specifically — not just any record for that mentor. This test is the key deduplication correctness check. The JSON round-trip test is valuable for catching serialization bugs early — use `test/fixtures/notification_records/single_unread.json` as the canonical fixture and verify both `fromJson(toJson(record)) == record` directions.

If `NotificationRecord` uses `DateTime`, ensure the serialization handles UTC vs local time correctly (use `DateTime.utc()` in fixtures).

Testing Requirements

This task is entirely test authoring. Create JSON fixtures under `test/fixtures/notification_records/` covering: a single unread record, a single read record, a batch of 5 mixed read/unread records, and an empty array. For the JSON round-trip test, ensure NotificationRecord implements `==` and `hashCode` (or use field-by-field assertions) — if these are not implemented, file this as a gap and use field assertions as workaround. For the concurrent write test, document clearly that this is a contract test (verifying expected behavior on constraint violation) not a true concurrency test — simulate by calling the mock to throw a `PostgrestException` with code `23505` (unique violation) and verify the repository surfaces this as a typed exception.

Run coverage with `flutter test --coverage` and verify the 80% threshold on the repository class specifically.

Component
Notification Record Repository
data medium
Epic Risks (3)
high impact medium prob technical

The RLS policy predicate that checks certification_expiry_date and suppression_status on every coordinator list query could cause full table scans at scale, degrading response time for coordinator contact list screens across all chapters.

Mitigation & Contingency

Mitigation: Add a partial index on (certification_expiry_date, suppression_status) filtered to active mentors. Benchmark the policy predicate against a representative data set (500+ mentors) during development using EXPLAIN ANALYZE on Supabase staging.

Contingency: If the index does not resolve the performance issue, introduce a computed boolean column is_publicly_visible that is updated by the mentor_visibility_suppressor service and indexed separately, shifting the predicate cost to write time rather than read time.

medium impact medium prob integration

FCM device tokens become invalid when users reinstall the app or switch devices. If the token management strategy does not handle token refresh reliably, notification delivery will silently fail for a significant portion of the user base without surfacing errors.

Mitigation & Contingency

Mitigation: Implement the FCM token refresh callback in the Flutter client to upsert the latest token to Supabase on every app launch. Store token with a last_refreshed_at timestamp. The FCM sender should handle UNREGISTERED error codes by deleting stale tokens.

Contingency: If token staleness becomes widespread, add a token health check that forces re-registration during the expiry check edge function run by querying mentors whose token was last refreshed more than 30 days ago and triggering a silent push to prompt re-registration.

medium impact low prob integration

The certification expiry and notification record tables may have column naming or constraint conflicts with existing tables in the peer mentor status and certification management features, causing migration failures in shared Supabase environments.

Mitigation & Contingency

Mitigation: Audit existing table schemas for user_roles, certifications, and notification tables before writing migrations. Prefix new columns with expiry_ to avoid collisions. Run migrations against a clean Supabase branch environment before merging.

Contingency: If a conflict is found post-merge, apply ALTER TABLE migrations to rename conflicting columns and issue a hotfix migration. Communicate schema changes to all dependent feature teams via a shared migration changelog.