high priority medium complexity testing pending testing specialist Tier 3

Acceptance Criteria

All integration tests are placed in test/integration/bufdir_reporting/ and run via flutter test --tags integration
Test fixture setup creates two distinct test organisations (org_a_id, org_b_id) with separate JWT tokens minted for each
Test fixture creates three test users: coordinator_org_a, coordinator_org_b, and super_admin_user with appropriate JWT claims
Append-only audit log test: insertAuditEntry succeeds; a direct .update() via org_a coordinator client throws PostgrestException (RLS blocks UPDATE); a direct .delete() throws PostgrestException (RLS blocks DELETE)
Org isolation test: a record inserted by coordinator_org_a is not returned by getAuditLogForOrg called with org_b credentials
Org isolation test: coordinator_org_b attempting to read org_a audit log receives an empty list (not an exception, per RLS SELECT policy behaviour)
Schema version uniqueness: upsertSchemaVersion with version=1 for org_a succeeds; a second call with version=1 and different columns throws SchemaVersionConflictException
Signed URL expiry: uploadExportFile succeeds; generateSignedUrl with ttl=Duration(seconds:2) produces a URL that is accessible immediately and returns 400 after 3 seconds
File path construction: uploaded file is retrievable at exactly '{org_id}/{export_id}.{extension}' — no extra prefixes or suffixes
Cross-org storage access: coordinator_org_b attempting to download a file at org_a's path via signed URL receives an error
All test fixtures clean up after themselves — tearDown deletes inserted rows and uploaded files
Tests are tagged with @Tags(['integration']) and excluded from the default unit test run

Technical Requirements

frameworks
flutter_test (test runner)
Dart (latest)
supabase_flutter (real SupabaseClient for integration tests)
apis
Supabase Admin API (service_role key) for fixture setup/teardown
Supabase Auth Admin: createUser, generateLinkType for minting test tokens
Supabase PostgREST and Storage APIs (under test)
data models
ExportAuditEntry
BufdirColumnSchema
ExportFormat
Test fixture models: TestOrg, TestUser
performance requirements
Full integration test suite must complete within 120 seconds
Each individual test must complete within 15 seconds — flag tests exceeding this with a timeout annotation
Signed URL expiry test must use the shortest possible TTL (1-2 seconds) to keep test duration minimal
security requirements
Test credentials (Supabase URL, anon key, service_role key) must be loaded from environment variables — never hardcoded
service_role key is used only in fixture setup/teardown helpers, never in the code under test
Test org IDs must be UUIDs generated fresh per test run (Uuid().v4()) to prevent cross-run data collisions
All test data must be deleted in tearDown even if assertions fail — use try/finally in tearDown

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Create a test/integration/ directory separate from test/unit/. Add a custom test tag by placing a dart_test.yaml at the project root: tags: { integration: { timeout: '2m' } }. Run with: flutter test --tags integration --dart-define=SUPABASE_URL=... --dart-define=SUPABASE_SERVICE_ROLE_KEY=...

The IntegrationTestFixtures helper should use the service_role SupabaseClient to call supabase.auth.admin.createUser() for each test persona with custom app_metadata: { 'org_id': orgAId, 'role': 'coordinator' }. After creation, sign in with email/password to get a real user JWT. For the signed URL expiry test, use http package's http.get() on the URL immediately (expect 200), then await Future.delayed(Duration(seconds: 3)) and call again (expect 400 or non-200). Ensure CI pipeline sets the required environment variables as secrets — document this in the test file header comment.

Avoid test interdependencies: each test must be independently runnable.

Testing Requirements

These tests ARE the deliverable. Structure each test file around one repository/adapter class: export_audit_repository_integration_test.dart, bufdir_schema_config_repository_integration_test.dart, bufdir_export_file_storage_integration_test.dart. Use a shared IntegrationTestFixtures class in test/integration/helpers/integration_fixtures.dart for org/user setup and teardown. Each test file must have a setUpAll that creates test orgs and users via service_role client, and a tearDownAll that removes them.

Individual tests use setUp/tearDown for per-test data. Use expect(..., throwsA(isA())) for RLS violation assertions. The signed URL expiry test should use a real HTTP GET via http.get(Uri.parse(signedUrl)) to verify actual access, not just URL generation. Minimum 5 test cases per class, 15 total.

Component
Export Audit Log Repository
data low
Epic Risks (2)
high impact medium prob security

RLS policies for the audit log and schema config tables must correctly handle multi-chapter membership hierarchies (up to 1,400 local chapters for NHF). Incorrect policies could either over-expose data across organisations or prevent legitimate coordinator access, both of which are serious compliance failures.

Mitigation & Contingency

Mitigation: Design RLS policies using the existing org hierarchy resolver pattern. Write integration tests that verify cross-organisation isolation with representative fixture data covering NHF's multi-level hierarchy before merging.

Contingency: If RLS policies prove too complex to express safely in Postgres, implement a Supabase Edge Function as a data access proxy that enforces isolation in application code, with RLS serving as a secondary defence layer.

medium impact medium prob scope

Bufdir's column schema is expected to evolve as Norse Digital Products negotiates a simplified digital reporting format. If the schema config versioning model is too rigid, applying Bufdir schema updates without a code deployment could fail, forcing emergency releases.

Mitigation & Contingency

Mitigation: Design the schema config table to store the full JSON column mapping as a JSONB field with a version number. Provide an admin API to upsert new versions without any schema migration required.

Contingency: If the versioning model is insufficient for a Bufdir schema change, fall back to a code deployment with the updated default schema, using the database config only for org-specific overrides.