Integration test ThresholdValidationEdgeFunction against real Supabase
epic-expense-approval-workflow-core-logic-task-012 — Write integration tests for the Edge Function that execute against a local Supabase instance. Test scenarios: valid submission with matching client result, tampered submission where client claims auto-approval but server computes manual, edge cases at exact threshold boundary, missing or malformed request bodies, and unauthorized callers without valid JWT. Verify HTTP status codes and response payloads.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Use `fetch()` directly against the local Edge Function URL β do not use the Supabase JS client to invoke functions, as it abstracts HTTP details needed for status code assertions. Structure each test as: (1) seed minimal required data, (2) build request payload, (3) call fetch(), (4) assert status + body, (5) optionally assert side effects (audit log). For JWT generation in tests, use `supabase.auth.signInWithPassword()` with a seeded test user rather than crafting raw JWTs β this tests the real auth flow. Keep threshold value in a shared constant imported by both the Edge Function and the test file to avoid magic numbers.
Document the `supabase start` prerequisite prominently at the top of the test file. Add the test command to package.json or Makefile so CI can run it with a single command.
Testing Requirements
These ARE the tests for this task. The test file is the deliverable. Structure tests using Deno's `Deno.test()` with a `beforeAll` hook that calls `supabase start` and a `afterAll` that tears down seeded data. Use a `createTestUser()` helper that creates a Supabase auth user and returns a valid JWT for authenticated scenarios.
Use a `createExpiredJwt()` helper for the 401 expiry scenario. After each tamper scenario, use the service role client to query the audit log and assert the row β this verifies both the 422 response AND the audit trail in a single test. All helpers must be in a `test_helpers.ts` file, not inlined.
The ThresholdEvaluationService is described as shared Dart logic used both client-side and in the Edge Function. Supabase Edge Functions run Deno/TypeScript, not Dart, meaning the threshold logic must be maintained in two languages and can diverge, causing the server to reject legitimate client submissions.
Mitigation & Contingency
Mitigation: Implement the threshold logic as a single TypeScript module in the Edge Function and call it via a thin Dart HTTP client wrapper for client-side preview feedback only. The server is always authoritative; the client version is purely for UX (showing the user whether their claim will auto-approve before they submit).
Contingency: If dual-language maintenance is unavoidable, create a shared golden test file (JSON fixtures with inputs and expected outputs) that is run against both implementations in CI to detect divergence immediately.
A peer mentor could double-tap the submit button or a network retry could trigger a duplicate submission, causing the ApprovalWorkflowService to attempt two concurrent state transitions from draftβsubmitted for the same claim, potentially resulting in two audit events or conflicting statuses.
Mitigation & Contingency
Mitigation: Implement idempotency in the ApprovalWorkflowService using a database-level unique constraint on (claim_id, from_status, to_status) per transition, combined with a UI-level submission lock (disable button after first tap until response returns).
Contingency: Add a deduplication check at the start of every state transition method that returns the existing state if an identical transition is already in progress or completed within the last 10 seconds.
Claims with multiple expense lines (e.g., mileage + parking) must have their combined total evaluated against the threshold. If individual lines are added asynchronously or the evaluation runs before all lines are persisted, the auto-approval decision may be computed on an incomplete set of expense lines.
Mitigation & Contingency
Mitigation: The Edge Function always fetches all expense lines from the database (not from the client payload) before computing the threshold decision. Define a clear claim submission contract that requires all expense lines to be persisted before the submit action is called.
Contingency: Add a validation step in ApprovalWorkflowService that counts expected vs. persisted expense lines before allowing the transition, returning a validation error if lines are missing.