high priority medium complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test suite contains at minimum 25 integration test cases covering all five data layer areas listed in the description
Expense type catalogue: test fetches from Supabase and caches locally; test serves cached data when network is unavailable; test cache invalidation when TTL expires
Expense claim CRUD: test create returns a claim with a server-assigned ID; test read returns the correct claim by ID; test update (status change, line item edit) is reflected immediately in the next read
Offline queue sync: test that a claim created while offline is stored locally, surfaced as 'pending sync', and submitted once connectivity is restored; test that no duplicate claim is created if sync is triggered twice
Receipt upload: test that a file is stored in Supabase Storage and a signed URL is returned; test that an oversized file returns a typed error (not an unhandled exception)
Threshold config: test that config is loaded and the correct auto-approval decision is returned; test that a stale cache is invalidated after TTL and the fresh value is used
Auto-approval edge function: test successful invocation returns a structured Decision object; test network timeout returns a typed NetworkError; test 403 response returns an AuthorizationError
Every repository method under test has a corresponding 'network failure' test case that asserts a typed error is returned (no unhandled exceptions bubble up)
Tests are runnable with `flutter test integration_test/` against a local Supabase emulator started via `supabase start`
CI configuration (GitHub Actions or equivalent) includes a step to start the local emulator before running integration tests
Test run completes in under 3 minutes on a standard CI runner
All tests pass on the main branch before this task is marked complete

Technical Requirements

frameworks
Flutter
flutter_test
integration_test package
Supabase local emulator (supabase CLI)
apis
Supabase REST API (via repository under test)
Supabase Storage API (via repository under test)
Auto-approval Edge Function (via repository under test)
data models
ExpenseClaim
ExpenseLineItem
ExpenseType
OrgThresholdConfig
Receipt
performance requirements
Full integration test suite completes in under 3 minutes
Each individual test case completes in under 10 seconds
Emulator seed data is idempotent — tests can be run in any order
security requirements
Test credentials (Supabase anon key for emulator) stored in `.env.test` not committed to version control
Test suite must not run against the production Supabase project — validate by checking the Supabase URL in a test setup hook
Seed data must not include real personal data — use synthetic names and IDs

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Do not use `mockito` or similar mocking libraries for these tests — the intent is true integration against a real (emulated) database. Use the actual repository classes (ExpenseRepository, ReceiptRepository, etc.) instantiated with the emulator Supabase client. Seed data should be declared in `test/fixtures/expense_seed.sql` and loaded via `supabase db reset --local` or the emulator's REST seed endpoint in `setUpAll`. For offline simulation, wrap the Supabase client in a `NetworkAwareSupabaseClient` that checks a `NetworkInfo` abstraction — this is testable via injection.

The CI step order must be: (1) `supabase start`, (2) `supabase db reset --local` to apply seed, (3) `flutter test integration_test/`, (4) `supabase stop`. Watch for Supabase emulator Storage — it requires the `storage` service to be running; confirm `config.toml` includes it.

Testing Requirements

These tests ARE the deliverable. Structure: `integration_test/data_layer/` with one file per domain area (expense_types_test.dart, expense_claims_test.dart, receipt_test.dart, threshold_config_test.dart, auto_approval_test.dart). Each file uses `setUpAll` to seed the emulator with fixture data and `tearDownAll` to clean up. Use `flutter_test`'s `TestWidgetsFlutterBinding` for async support.

For offline tests, inject a `NetworkInfoMock` that simulates no connectivity. For edge function tests, use the emulator's local function URL. Document how to run the suite in the project README (or in task-012 documentation).

Component
Expense Repository
data medium
Dependencies (4)
Build the AutoApprovalEdgeFunctionClient Dart class that invokes the Supabase Edge Function responsible for evaluating whether a submitted expense claim meets the organisation's auto-approval criteria. Handle authentication headers, parse the approval/rejection response envelope, and expose a typed result object. Implement retry logic with exponential back-off for transient network failures. epic-travel-expense-registration-foundation-task-008 Write and deploy the Supabase Edge Function (Deno/TypeScript) that receives a claim payload, loads the organisation's threshold configuration from the database, evaluates distance and receipt rules, records the auto-approval decision with a timestamp and threshold snapshot in expense_claims, and returns a structured response. Include unit tests for threshold boundary conditions. epic-travel-expense-registration-foundation-task-009 Build the ExpenseRepository Dart class wrapping Supabase queries for CRUD operations on expense claims and line items. Implement offline write queue using a local SQLite or Hive store so claims created in low-connectivity areas are synced automatically when connectivity is restored. Expose typed streams for claim status changes via Supabase Realtime. epic-travel-expense-registration-foundation-task-004 Build the ReceiptStorageAdapter Dart class that handles image selection via image_picker, compresses images to ≤500 KB using flutter_image_compress before upload, uploads to the Supabase Storage bucket, and returns short-lived signed URLs for display. Implement download caching for previously fetched receipt thumbnails to avoid redundant network calls. epic-travel-expense-registration-foundation-task-006
Epic Risks (3)
high impact medium prob security

Row-level security policies for expense claims must correctly scope data to organisation, role (peer mentor sees own claims only, coordinator sees org-wide queue), and claim status. Incorrect RLS can expose claims cross-organisation or prevent coordinators from accessing the attestation queue.

Mitigation & Contingency

Mitigation: Define RLS policies in code-reviewed migration files. Write integration tests that attempt cross-org reads with different JWT roles and assert access denial. Review with a second engineer before merging migrations.

Contingency: If RLS is misconfigured post-deployment, disable the affected policy temporarily and apply a hotfix migration within the same release window. No claim data is exposed publicly due to Supabase project-level auth requirement.

medium impact medium prob technical

The auto-approval Edge Function is triggered server-side on expense insert. Cold-start latency or Edge Function failures can block the submission response and degrade UX, especially on mobile networks.

Mitigation & Contingency

Mitigation: Implement the auto-approval Edge Function client with a timeout and graceful fallback: if no result is received within 5 seconds, treat the claim as 'pending' and poll for the status update via Supabase Realtime. Keep the Edge Function warm with a periodic ping.

Contingency: If Edge Function reliability is unacceptable, move auto-approval evaluation to a database trigger or Postgres function as an interim measure, accepting that threshold configuration changes require a migration rather than a settings update.

medium impact low prob scope

The expense type catalogue and threshold configuration are cached locally for offline use. If an organisation updates their catalogue exclusion rules or thresholds while a peer mentor is offline, the local cache may allow submissions that violate the new policy.

Mitigation & Contingency

Mitigation: Cache entries include a TTL (24 hours). On connectivity restore, refresh cache before allowing new submissions. Server-side validation in the Edge Function and save functions provides a second enforcement layer.

Contingency: If a stale-cache submission passes client validation but fails server validation, surface a clear error message explaining that the expense type rules have been updated and prompt the user to review their selection with the refreshed catalogue.