critical priority medium complexity testing pending backend specialist Tier 6

Acceptance Criteria

MentorLocationRepository.findMentorsInBounds() invokes the PostgisSpatialAdapter RPC and returns a List<MentorLocationModel> with no stub code remaining
Integration test seeds at least 3 mentor_location rows within the bounding box and asserts all 3 are returned
Integration test seeds at least 2 mentor_location rows outside the bounding box and asserts 0 are returned
Integration test seeds rows for two different organisations and asserts RLS filters out the cross-org rows (only rows for the authenticated org are returned)
Integration test runs against supabase start local instance and passes in CI without external network access
RPC call includes organisation_id context derived from the authenticated user JWT claim, not a hardcoded value
Error from Supabase RPC (e.g., invalid bounding box) is surfaced as a typed domain exception, not a raw PostgrestException
findMentorsInBounds returns an empty list (not null or exception) when no mentors exist within the bounds
All integration tests are tagged so they can be excluded from fast unit test runs (e.g., @Tags(['integration']))

Technical Requirements

frameworks
Flutter
Riverpod
flutter_test
apis
Supabase RPC (PostGIS bounding-box function)
Supabase Auth (JWT org claims)
data models
activity
assignment
performance requirements
RPC call with a bounding box covering 100 mentor rows must complete within 500ms on local Supabase instance
Repository must not make more than one RPC call per findMentorsInBounds invocation
security requirements
Row-Level Security on mentor_location table must be enforced — cross-org rows must never be returned
JWT org claim used for RLS must not be overridable by client-supplied parameters
Integration test must use a non-service-role key to validate RLS enforcement (anon or user-scoped key)

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Replace stub in MentorLocationRepository with: final response = await supabase.rpc('mentors_in_bounds', params: {'min_lng': bounds.west, 'min_lat': bounds.south, 'max_lng': bounds.east, 'max_lat': bounds.north}). Map the RPC response list to List using a fromJson factory. For integration test setup, use the Supabase management API or direct SQL via supabase db execute to seed data. Ensure the PostGIS RPC function uses ST_MakeEnvelope and filters by the organisation_id from auth.uid() → organisations lookup, not a passed parameter.

Wrap the RPC call in a try/catch mapping PostgrestException to a domain-level SpatialQueryException. In CI, add a docker-compose step that runs supabase start before the integration test step.

Testing Requirements

Integration tests using flutter_test with supabase_flutter and a locally running supabase start instance. Test setup: (1) create two organisations, two authenticated users (one per org); (2) seed mentor_location rows with known coordinates; (3) call findMentorsInBounds with a bounding box that includes some rows and excludes others. Assertions: correct rows returned, cross-org rows absent, empty bounding box returns empty list, malformed bounding box throws typed exception. All tests must clean up seeded data via afterAll.

Tag tests as 'integration' to allow exclusion from unit test CI step. No mocking of the Supabase client in integration tests — the real client must be used.

Component
PostGIS Spatial Query Adapter
infrastructure medium
Epic Risks (3)
high impact medium prob integration

Supabase's hosted PostGIS extension behaviour may differ from the local emulator for spatial RPC functions, causing bounding-box queries to return incorrect results or fail in production while passing locally.

Mitigation & Contingency

Mitigation: Write integration tests against the Supabase emulator from the start and run the same test suite against a staging Supabase project before merging. Use ST_DWithin and ST_MakeEnvelope in plain SQL first, validate with psql, then wrap as RPC.

Contingency: If PostGIS RPC proves unreliable, fall back to client-side bounding box filtering on a full fetch of consented mentor locations (acceptable for up to ~200 mentors per chapter) until the spatial query is stabilised.

medium impact low prob dependency

OpenStreetMap tile usage may require attribution handling and rate limiting. Switching to Google Maps Flutter plugin mid-implementation would require significant rework of the map-provider-integration abstraction.

Mitigation & Contingency

Mitigation: Define the map-provider-integration abstraction interface before selecting the SDK so that the concrete implementation is swappable. Implement OSM first with correct attribution. Document Google Maps as the alternate with its API key setup steps.

Contingency: If OSM tiles are rejected by stakeholders or tile server limits are hit, activate the Google Maps Flutter plugin implementation behind the same interface without touching any UI or service code.

high impact low prob security

Incorrect RLS configuration could allow a coordinator to query mentor locations from a different organisation, constituting a GDPR data breach.

Mitigation & Contingency

Mitigation: Write dedicated RLS integration tests with two isolated test organisations and assert that cross-organisation queries return zero rows. Include these tests in CI. Have a second developer review all RLS policy SQL before migration is applied.

Contingency: If a cross-organisation data leak is discovered post-deployment, immediately disable the map feature via the organisation feature flag, revoke the affected Supabase RLS policy, and notify the data protection officer per the organisation's GDPR incident response procedure.