End-to-end receipt upload flow integration test
epic-receipt-capture-and-attachment-foundation-task-017 — Write an integration test that exercises the full receipt foundation stack: pick raw bytes via mock ReceiptImagePickerIntegration, compress via ReceiptImageCompressor, upload to Supabase Storage via ReceiptStorageRepository, persist the association record via ClaimReceiptRepository, and validate threshold via ReceiptThresholdValidator. Assert that the SQLite record references the correct storage path and that the stored file is accessible via signed URL.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Structure the test with a setUp() that builds the ProviderContainer with all mocked dependencies and seeds the SQLite in-memory DB with a test claim record. Use mocktail for all mocked services. The critical assertion is that the storage path in SQLite matches the path returned by the mock ReceiptStorageRepository — this validates the handoff between the upload and persistence layers. For the rollback test, configure the mock ReceiptStorageRepository to throw a StorageException on the first call, then assert that ClaimReceiptRepository.getReceiptsForClaim() returns an empty list.
Document each test with a comment explaining what integration contract it validates. Keep the test self-contained — no shared state between test cases.
Testing Requirements
Use flutter_test with a ProviderContainer and full dependency override for all external services. Mock ReceiptImagePickerIntegration to return a known fixture JPEG byte array. Mock ReceiptStorageRepository to simulate a successful upload returning a deterministic storage path, and a second variant that simulates upload failure. Mock Supabase signed URL generation to return a valid-format URL string.
Use an in-memory SQLite database (or a temp file) for ClaimReceiptRepository to avoid polluting real data. Assert the full data flow by querying the in-memory SQLite after each step. Cover both the success and upload-failure scenarios in separate test cases. This test serves as a contract test for the integration between all five components.
Supabase Storage RLS policies using org/user/claim path scoping may not enforce correctly if claim ownership is not present in the JWT or if path segments are constructed differently at upload vs. read time, leading to data leakage or access denial for legitimate users.
Mitigation & Contingency
Mitigation: Define and test RLS policies in isolation before wiring to app code. Write integration tests that assert cross-org and cross-user access is denied. Use service-role key only in edge functions, never in client code.
Contingency: If client-side RLS proves insufficient, route all storage reads through a Supabase Edge Function that validates ownership before generating signed URLs, adding a controlled server-side enforcement layer.
Aggressive image compression may reduce receipt legibility below the threshold required for financial auditing, causing claim rejections or compliance failures despite technically successful uploads.
Mitigation & Contingency
Mitigation: Define minimum legibility requirements with HLF finance team before implementation. Set compression targets conservatively (e.g., max 1MB, min 80% JPEG quality) and validate with sample receipt images. Provide compression statistics in verbose/debug mode.
Contingency: If post-compression quality is disputed by auditors, increase the quality floor at the cost of larger file sizes, and add a manual override allowing users to skip compression for PDFs and high-quality scans.
The Flutter image_picker package behaves differently on iOS 17+ (PHPicker) vs older Android (Intent-based), particularly for file types, permission flows, and PDF selection, which may cause platform-specific failures not caught in development.
Mitigation & Contingency
Mitigation: Test image picker integration on physical devices for both platforms early in the sprint. Pin the image_picker package version and review changelogs before updates. Write widget tests using mock file results for each platform branch.
Contingency: If PHPicker or Android Intent differences cause blocking issues, implement separate platform-specific picker delegates behind the unified interface, allowing platform-specific fixes without breaking the shared API.