high priority high complexity testing pending testing specialist Tier 7

Acceptance Criteria

All 6 integration test scenarios execute against the locally served Edge Function without manual intervention
Scenario 1: coordinator JWT + Xledger org → HTTP 200, response contains signedUrl matching Supabase Storage pattern, expiresAt is ~15 minutes from now
Scenario 2: coordinator JWT + Dynamics org → HTTP 200, response contains externalRef string and system='dynamics'
Scenario 3: no Authorization header → HTTP 401, body {error: 'Unauthenticated'}, no audit log row created
Scenario 4: peer-mentor JWT → HTTP 403, body {error: 'Insufficient permissions'}, no audit log row created
Scenario 5: duplicate export (same org + period already exported) → HTTP 409, audit log row created with status=REJECTED
Scenario 6: all successful invocations have corresponding audit log rows with correct triggered_by_user_id, organization_id, status, and timestamp
Credential check: assert that XLEDGER_API_KEY, DYNAMICS_CLIENT_SECRET, and SUPABASE_SERVICE_ROLE_KEY strings do not appear anywhere in any HTTP response body across all scenarios
Test suite runs to completion in CI without requiring manual Supabase project setup beyond `supabase start`
External Xledger and Dynamics endpoints are stubbed — tests do not make real external API calls

Technical Requirements

frameworks
Deno test runner
Supabase CLI (supabase functions serve + supabase start)
HTTP stub server (e.g., Deno's built-in server for mocking Xledger/Dynamics)
apis
Supabase Edge Functions (local test harness)
Supabase Auth (test JWT generation)
Supabase PostgreSQL 15 (local instance for audit log verification)
Supabase Storage (local instance for signed URL verification)
data models
bufdir_export_audit_log
annual_summary
assignment
performance requirements
Full test suite must complete in under 3 minutes on CI
Each individual test scenario must complete in under 30 seconds
Test setup (seed data + start harness) must be automated and repeatable
security requirements
Test JWTs must be signed with the local SUPABASE_JWT_SECRET — never use production secrets in test environments
Credential leak check must scan the raw HTTP response bytes, not just parsed JSON fields
Test database must be isolated — use a dedicated Supabase local instance or schema prefix to prevent cross-test contamination
Stub servers for Xledger and Dynamics must validate that no real credentials are received in their request handlers

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

Use `supabase functions serve --no-verify-jwt` is NOT appropriate here — keep JWT verification enabled, and generate test tokens using `supabase.auth.admin.generateLinkToken` or by signing tokens directly with the local JWT secret. For stub servers, use `Deno.serve()` on localhost ports (e.g., 18080 for Xledger stub, 18081 for Dynamics stub) and inject these URLs as env vars into the Edge Function via `.env.local` overrides. Isolate test data by org ID — use unique org UUIDs per test run (or per test file) to prevent interference. The duplicate scenario requires pre-inserting a row into the export runs table via the service-role client before invoking the function.

Document the exact `supabase start` + `supabase functions serve` command sequence needed to run the suite in the repository README.

Testing Requirements

This task IS the test implementation. Structure tests as Deno.test() blocks grouped by scenario. Use beforeAll/afterAll (or Deno's lifecycle hooks) to: start `supabase start`, seed test users (coordinator, peer-mentor, org-admin) with distinct JWTs, seed one org with Xledger config and one with Dynamics config, seed a pre-existing export run for the duplicate scenario, and start stub HTTP servers for Xledger and Dynamics endpoints. After each scenario, query the audit log table directly via the service-role client and assert row contents.

Use `assert`, `assertMatch`, and `assertNotMatch` from Deno's std/testing/asserts for all checks. Include a dedicated credential-leak scan helper that searches all response payloads for known secret substrings.

Component
Supabase Edge Function: Generate Export
infrastructure high
Epic Risks (3)
high impact medium prob technical

The Edge Function may exceed Supabase's execution time limit (default 150 seconds, but effectively constrained by the 10-second client SLA) when processing large batches of claims with complex chart-of-accounts mapping, causing the export to fail after partial processing.

Mitigation & Contingency

Mitigation: Implement the export pipeline with early termination on timeout and an in-progress export run status. Add a benchmark test in CI that runs the full pipeline against 500 claims and fails if it exceeds 8 seconds. Optimize the approved claims query with indexes on status, org_id, and date fields.

Contingency: If performance targets cannot be met synchronously, convert the Edge Function to an async job pattern: the function queues the export and returns a job ID immediately; the client polls a status endpoint and downloads the file when ready. This requires a job queue table and a polling UI state.

high impact medium prob security

Supabase Vault access from the Edge Function may require specific service role key configuration that differs between staging and production environments, causing credential retrieval to fail silently and producing export runs that appear successful but have no valid accounting system target.

Mitigation & Contingency

Mitigation: Test Vault read access in the Edge Function in staging before implementing any business logic. Add an explicit credential validation step at Edge Function startup that fails fast with a clear error if Vault is unreachable or the secret is missing.

Contingency: If Vault access fails in production, fall back to environment variable-based credentials temporarily (never returned to client) while the Vault configuration is corrected. Alert on-call via a monitoring rule that fires if credential retrieval fails.

medium impact low prob technical

AccountingExporter Service may become tightly coupled to specific exporter implementations if the factory pattern is not implemented cleanly, making it difficult to add a third exporter in the future without modifying the orchestrator.

Mitigation & Contingency

Mitigation: Define an AccountingExporter abstract class with a strict interface contract before implementing any concrete class. Use a registry pattern (Map<orgType, AccountingExporter>) in the factory rather than conditionals. Code review should verify no concrete class is imported directly in the orchestrator.

Contingency: If tight coupling is discovered after implementation, refactor the factory before the Edge Function epic ships so the interface is stable before any external callers are wired in.