high priority low complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test file exists at the correct test mirror path (e.g., test/contact/contact_rls_query_builder_test.dart)
Tests cover all three UserRole values (coordinator, org_admin, peer_mentor) and assert the specific Supabase column and operator used for each
Test confirms organization_id filter is present in every built query, including queries with no role filter and no search filter
ILIKE escaping tests: '%' in input → '\%' in pattern; '_' in input → '\_' in pattern; backslash in input → escaped backslash in pattern
Empty string search → no ILIKE predicate appended to query
Whitespace-only search string → no ILIKE predicate appended
Single character search → no ILIKE predicate appended
Two character search → ILIKE predicate appended
Combined filter test: forOrganisation('org-1').withRole(UserRole.coordinator).withSearch('test') → query contains organisation_id eq, coordinator role filter, and ILIKE predicate in that order
Multi-org test: forOrganisation(['org-1', 'org-2']) → query uses .in_() not .eq()
Empty org list → ArgumentError thrown before any Supabase call
All tests pass with zero live network calls — verified by asserting mock client was used
flutter test runs the file in isolation with exit code 0

Technical Requirements

frameworks
Flutter
flutter_test
Dart
apis
Supabase (mocked)
data models
UserRole
Contact
performance requirements
Full test file completes in under 5 seconds
No async operations that require real timers — use fake async if needed
security requirements
Test fixtures must not contain real contact names, real organisation IDs, or real user data — use clearly fake identifiers (e.g., 'test-org-id', 'Test User')

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

The most important design decision for this test file is the mock Supabase query builder. Supabase's Dart client uses a chainable builder where each method returns the same object (or a new modified one). Create a simple stub class that implements the same interface (or enough of it to satisfy the type checker) and stores method calls in a List where MethodCall is a simple record of {name: String, args: Map}. This makes assertions readable: expect(mock.calls, [ MethodCall('eq', {'column': 'organization_id', 'value': 'org-1'}), MethodCall('eq', {'column': 'role', 'value': 'coordinator'}) ]).

Avoid using Mockito's generated mocks for fluent builders — hand-written stubs are more maintainable here. Place the stub in test/helpers/ so it can be reused by task-010's integration tests.

Testing Requirements

All tests use flutter_test (not integration_test). Create a MockSupabaseQueryBuilder stub that records each filter method call (eq, in_, ilike, or) in an ordered list. After calling build(), assert the recorded call list matches the expected sequence. Group tests using group() blocks: 'role filter mapping', 'organisation scoping', 'search predicate construction', 'combined filter assembly'.

Use setUp() to create a fresh mock before each test. Aim for 100% statement coverage of ContactRLSQueryBuilder. Run flutter analyze on the test file itself to ensure no linting issues.

Component
Contact RLS Query Builder
infrastructure low
Epic Risks (2)
high impact medium prob security

Existing Supabase RLS policies for the contacts and peer_mentors tables may not align with the application-level UserRole model, causing ContactRLSQueryBuilder to construct filter expressions that are redundant, conflicting, or that allow over-fetching. In a multi-chapter context (NHF), this could expose contacts belonging to other chapters.

Mitigation & Contingency

Mitigation: Audit and document the existing RLS policies against the UserRole enum before writing a single line of query builder code. Write integration tests asserting cross-organization data isolation using separate test user tokens for each role.

Contingency: If RLS policies are misaligned at runtime, add an explicit application-level organization_id equality check in ContactRepository as a secondary guard while the database policies are corrected in a coordinated migration.

medium impact low prob integration

The Supabase schema for contacts and peer_mentors tables may differ from the expected typed models — missing columns, renamed fields, or type mismatches — causing deserialization failures that surface only at runtime during integration testing.

Mitigation & Contingency

Mitigation: Document expected schema fields upfront and validate against the live Supabase schema at sprint start. Use freezed and json_serializable for compile-time-safe deserialization with explicit required/optional field declarations.

Contingency: Introduce nullable fields with safe defaults for any schema mismatches discovered in testing; log deserialization errors to the monitoring service so schema drift is caught before production deployment.