high priority medium complexity testing pending testing specialist Tier 2

Acceptance Criteria

Tests run against a dedicated Supabase test project with isolated storage bucket, not production
Upload test: uploading a 5KB synthetic JPEG byte array succeeds and returns a non-null file path
Existence check: after upload, the file exists at the expected org/user/claim scoped path in the test bucket
Signed URL test: generateSignedUrl() returns a non-empty HTTPS URL with an expiry parameter
Delete test: after deletion, generateSignedUrl() throws a StorageException or returns a 404-equivalent error
Path scoping test: files uploaded for org A are not accessible via a Supabase client authenticated as org B (RLS enforcement)
Teardown: all test files are deleted after each test regardless of pass/fail using addTearDown
Tests are tagged @Tags(['integration']) and excluded from default flutter test runs via test configuration
CI environment variables (SUPABASE_TEST_URL, SUPABASE_TEST_ANON_KEY) documented in .env.test.example

Technical Requirements

frameworks
Flutter
flutter_test
dart
apis
Supabase Storage
data models
assignment
performance requirements
Each individual test must complete within 10 seconds to keep CI pipeline fast
Test JPEG fixture must be ≤10KB to minimise upload latency
security requirements
Test Supabase project must have RLS enabled on the receipts storage bucket
Test credentials (URL, anon key) stored in CI secrets — never committed to repository
Service role key not used in tests — authenticate as a real test user to validate RLS
All test files cleaned up to prevent accumulation of PII-adjacent test data

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Create test file at test/integration/receipt_storage_repository_integration_test.dart. Use a package:test group() to wrap all tests so setUp/tearDown applies consistently. Generate a synthetic 5KB JPEG using a minimal valid JPEG byte sequence (FFD8FF + minimal headers + FFD9) rather than bundling a real image asset. Authenticate via Supabase Auth signInWithPassword using a dedicated test user seeded in the test project.

The hardest part is reliably asserting the 404 state after deletion — check the Supabase Storage SDK's exception type hierarchy and match on the specific error code rather than message string. Document the test Supabase project setup steps in a TESTING.md or inline comment block at the top of the test file.

Testing Requirements

Integration tests using flutter_test against a live Supabase test environment. Test scenarios: (1) upload JPEG bytes → assert path returned matches ReceiptStoragePathBuilder output, (2) sign URL → assert URL is valid HTTPS string, (3) delete → assert subsequent sign URL call fails with expected error type, (4) cross-org access attempt → assert StorageException thrown. Use setUp to create a test user session via Supabase Auth and addTearDown for cleanup. Tag tests with 'integration' so they are skipped in unit test CI steps but run in a dedicated integration test step.

Component
Receipt Storage Repository
data medium
Epic Risks (3)
high impact medium prob security

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.

high impact medium prob technical

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.

medium impact medium prob dependency

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.