high priority high complexity testing pending testing specialist Tier 6

Acceptance Criteria

Integration test suite passes end-to-end against a Supabase test project/bucket using real Storage and database calls
Happy path test: open bottom sheet → pick image → compress → upload → link to claim record → assert signed URL is accessible via HTTP GET returning 200
Cancellation test: simulate user dismissing the sheet mid-upload; assert no orphaned file exists in Storage and no dangling record in claim_receipt_attachments
Network failure retry test: mock one transient network error during upload; assert automatic retry succeeds and exactly one file exists in Storage
File size threshold test: supply a file exceeding the configured max bytes limit; assert upload is rejected before any network call with a user-visible error message
Accessibility test: every interactive widget in the receipt capture sheet has a Semantics label verified via flutter_test Semantics matchers compatible with VoiceOver and TalkBack
All tests are isolated — each test creates and cleans up its own Supabase Storage objects and database rows
Test suite completes in under 60 seconds on CI
Zero flaky failures across 5 consecutive runs on a clean environment

Technical Requirements

frameworks
Flutter
flutter_test
Riverpod
apis
Supabase Storage API
Supabase Database API (PostgREST)
data models
ClaimReceiptAttachment
Claim
performance requirements
Full suite completes within 60 seconds
Each individual test completes within 15 seconds including real network calls to Supabase test environment
security requirements
Test suite uses a dedicated Supabase test project with RLS enabled — never the production project
Supabase service role key used only for test setup/teardown helpers, never in tested code paths
Signed URL validity window in tests must be ≥ 10 seconds to avoid false negatives
ui components
Receipt capture bottom sheet
Upload progress indicator
Error message widget

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Create a dedicated test helper file (e.g., supabase_test_helpers.dart) that provides: createTestClaim(), deleteStorageObject(path), and assertStorageObjectExists(path) using the Supabase service role client. Keep this helper out of the main lib/ tree. For cancellation tests, cancel the Riverpod provider's upload notifier and then immediately query Storage to confirm no file was committed — use a small delay (500ms) to allow any in-flight requests to settle before asserting. For network failure simulation, wrap the ReceiptStorageRepository upload method with a configurable failure-injection flag rather than patching http — this keeps the test realistic.

For Semantics tests, use tester.getSemantics(find.byType(ElevatedButton)) and assert label contains the expected string; do not hardcode pixel coordinates. Ensure the test project's RLS policies match production exactly so tests validate real security boundaries.

Testing Requirements

Integration tests using flutter_test against a real Supabase test environment (no mocks for Storage or DB calls). Cover: (1) full happy-path flow, (2) mid-upload cancellation with Storage cleanup verification, (3) transient network error retry, (4) file size threshold rejection, (5) Semantics label validation for all interactive elements. Use setUp/tearDown hooks to insert prerequisite claim records and delete all created Storage objects after each test. Do not mock the Supabase client in these tests — use the actual SDK pointed at a test project.

Add a CI environment variable guard so tests are skipped if SUPABASE_TEST_URL is not set, preventing accidental runs without credentials.

Component
Receipt Attachment Service
service high
Epic Risks (3)
high impact medium prob technical

Non-blocking upload creates a race condition: if the claim record is submitted and saved before the upload completes, the storage path may never be written to the claim_receipts table, leaving the claim with a missing receipt that was nonetheless required.

Mitigation & Contingency

Mitigation: Design the attachment service to queue a completion callback that writes the storage path to the claim record upon upload completion, even after the claim form has submitted. Use a local task queue with persistence to survive app backgrounding. Test the race condition explicitly with simulated slow uploads.

Contingency: If the async path association proves unreliable, fall back to blocking upload before claim submission with a clear progress indicator, accepting the UX trade-off in exchange for data integrity.

high impact medium prob scope

The offline capture requirement (cache locally, sync when connected) significantly increases state management complexity. If the offline queue is not durable, receipts captured without connectivity may be lost when the app is killed, causing claim submission failures users are not aware of.

Mitigation & Contingency

Mitigation: Persist the offline upload queue to local storage (e.g., Hive or SQLite) on every state transition. Implement background sync using WorkManager (Android) and BGTaskScheduler (iOS). Scope the initial delivery to online-only flow if offline sync cannot be adequately tested before release.

Contingency: Ship without offline support in the first release, displaying a clear 'Upload requires connection' message. Add offline sync as a follow-on task once the core online flow is validated in production.

medium impact low prob integration

The inline bottom sheet presentation within a multi-step wizard can conflict with existing modal navigation and back-button handling, particularly if the expense wizard itself uses nested navigation or custom route management.

Mitigation & Contingency

Mitigation: Review the expense wizard navigation architecture before implementation. Use showModalBottomSheet with barrier dismissal disabled to prevent accidental dismissal. Coordinate with the expense wizard team on modal stacking behavior and ensure the camera sheet does not interfere with wizard step transitions.

Contingency: If modal stacking causes navigation issues, present the camera sheet as a full-screen dialog using PageRouteBuilder with a transparent barrier, preserving wizard state via the existing Bloc while still appearing inline.