high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test file contains at minimum 10 distinct test cases covering all four repository methods
setUp opens a fresh in-memory SQLite database and runs the migration; tearDown closes the database
insert() test: inserts one ClaimReceiptAttachment and asserts the returned id is a positive integer
insert() duplicate path test: inserting two records with the same receipt_storage_path succeeds (no UNIQUE constraint on path — multiple claims could reference the same storage object, though this should not occur in practice) OR fails with a clear exception if a UNIQUE constraint is intentionally added
getByClaimId() test: inserts 3 records for claim-A and 2 for claim-B; asserts getByClaimId('claim-A') returns exactly 3 records ordered by created_at DESC
getByClaimId() empty result test: querying a non-existent claim_id returns an empty list, not an exception
deleteById() test: inserts a record, deletes by id, asserts getByClaimId returns empty
deleteById() missing id test: deleting a non-existent id returns false without throwing
updateSyncStatus() test: inserts with status 'pending', updates to 'synced', asserts the record's sync_status is 'synced'
All tests pass consistently with no flakiness across 10 runs
Test file does not import any Supabase dependencies — this is pure local SQLite testing

Technical Requirements

frameworks
Flutter
flutter_test
sqflite_common_ffi (for in-memory SQLite in tests)
data models
ClaimReceiptAttachment
performance requirements
Entire test suite completes in under 5 seconds
security requirements
Test data must use placeholder UUIDs, not real user or org IDs

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use sqflite_common_ffi's inMemoryDatabaseFactory.openDatabase(':memory:') in setUp to get a real SQLite in-process. Call the same migration function used in production (e.g., DatabaseMigrations.applyV1(db)) so the schema is always in sync with real migrations. Build a small test factory function (makeTestAttachment({String claimId = 'test-claim-1', ...})) at the top of the test file to reduce boilerplate. For the ordering assertion in getByClaimId, insert records with explicit created_at values 1 second apart to make the DESC ordering deterministic.

Avoid using DateTime.now() directly in test data — use fixed ISO-8601 strings instead.

Testing Requirements

Unit tests only — no Flutter widget rendering, no Supabase calls. Use sqflite_common_ffi with inMemoryDatabaseFactory to obtain a real SQLite engine in-memory without file I/O. Run the project's database migration in setUp so the schema is identical to production. Use descriptive test names following the pattern 'methodName — scenario — expected outcome'.

Group tests by method using group() blocks. Avoid test interdependency: each test must be runnable in isolation.

Component
Claim Receipt Repository
data low
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.