- Add `app` export and wrap startup logic behind `if (import.meta.main)` - Extract `hono` middleware into `sdk/hono.ts` for clean separation - Refactor module imports slightly to support in-memory native mocking (`db`, `valkey`, `spire_ffi`, `ratelimit`, `audit`) - Implement comprehensive native Deno mock tests in `server/main.test.ts` - Fix type checking across project files Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
6.8 KiB
6.8 KiB
TASK METADATA
- Target Files:
server/main.tsserver/main.test.ts(new)server/db.tsserver/db.test.ts(new)server/spire_ffi.test.ts(new)server/valkey.tsserver/valkey.test.ts(new)sdk/mod.tssdk/hono.ts(new)sdk/mod.test.tssdk/hono.test.ts(new)
- Core Objective: Design a comprehensive multi-tier automated test harness
and verification suite for Auth-Yes, validating Tier 1 & 2 (Traefik
ForwardAuth), Tier 3 (ConnectRPC with SPIFFE/mTLS workload attestation),
decoupled Default-Deny RBAC authorization, and modular
@auth-yes/sdkclient middleware using native Deno mocking. - Dependencies: Deno standard library
@std/testing/mock,@std/testing/bdd, andhono. - Additional Important Notes: Do NOT use Testcontainers or Docker
dependencies. Mock external services (Postgres, Valkey, SPIFFE API) using
Deno's native mocking capabilities to ensure hermetic, fast, and deterministic
test execution. Refactor
server/main.tstop-level side effects (server listener and network probes) behindif (import.meta.main)and exportappfor in-memoryapp.request()testing.
Architectural Considerations & Risks
Risks
- Mocking Accuracy: Using mocks for PostgreSQL (
db.ts) and Valkey (valkey.ts) risks diverging from actual production behavior if the database schema or RESP3 client tracking intricacies change. We must ensure the mocked responses accurately reflect both success cases and edge cases (e.g., specific Valkey error codes, Postgres constraint violations). - Top-Level Side Effects during Test Imports: Currently, importing
server/main.ts,server/db.ts, orserver/valkey.tsexecutes top-level code (e.g.,Deno.serve(...),MetadataService.initialize(),pingValkey(), and strict env validation). These must be cleanly decoupled/guarded so test runners can import modules in isolation without triggering network listeners or unhandled runtime rejections. - SPIFFE Workload Mismatches: Relying on explicit Deno stubs for
extractSpiffeIdFromCertandfetchSpiffeIdentitymust be robust enough to simulate corrupted X.509 certificates or unrecognized SPIFFE IDs deterministically. If stubs aren't well isolated between tests, state bleed could cause flaky test runs. - SDK Modularity: Integrating Hono middleware directly into
sdk/mod.tsrisks coupling the core SDK to a specific web framework. To preserve Rule 1 ofAGENTS.md(zero-dependency SDK), Hono helpers must be housed in a dedicated modular submodule (sdk/hono.ts) while keepingsdk/mod.tscompletely framework-agnostic.
Alternatives
- Alternative 1: E2E Containers (Rejected): Spinning up live Postgres, Valkey, and SPIRE agent containers using Docker/Testcontainers would provide higher fidelity but drastically slow down the test suite and introduce environment dependency friction. As requested, we are prioritizing hermetic Deno native mocking.
- Alternative 2: In-Memory Hono App & Direct RPC Testing (Selected):
Leverage Hono's
.request()helper on the exportedappinstance to test API endpoints (Tier 1 & 2) in memory while stubbing the underlyingsqlandvalkeyexports. For Tier 3 (ConnectRPC), test both direct service handler invocations and RPC routes in memory.
Proposed Implementation
Phase 1: Test Environment & Server Module Decoupling
- Entrypoint Refactoring (
server/main.ts):- Export
appfromserver/main.ts. - Wrap
Deno.serve(...),pingValkey(), andMetadataService.initialize()insideif (import.meta.main) { ... }so importingmain.tsin tests does not spin up a live server or execute remote network calls. - Provide safe fallback defaults for
RP_IDandORIGINin test mode or allow environment overrides before import.
- Export
- Database & Valkey Isolation:
- Create reusable mock helpers using
@std/testing/mock(stubandspy). - Stub the
sqlexport inserver/db.tsto return controlled records representing mock users, apps, and grants. - Stub
valkey.getandvalkey.setexinserver/valkey.tsto simulate session cache hits, misses, and invalidations.
- Create reusable mock helpers using
- SPIFFE/mTLS Mocking (Tier 3):
- Stub
extractSpiffeIdFromCertinserver/spire_ffi.ts. - Provide test cases for valid SPIFFE URIs (e.g.,
spiffe://system.local/ed-droid-backend), invalid URIs, missing extensions, and unparseable certs.
- Stub
Phase 2: Tier 1 & 2 (Edge ForwardAuth) Verification
- Target:
GET /api/forward-authviaapp.request()inserver/main.test.ts. - Test Cases:
- Valid session cookie: Assert HTTP
200 OKand presence ofX-Forwarded-UserandX-Forwarded-User-Idheaders. - Missing session cookie: Assert HTTP
401 Unauthorized. - Invalid/Expired session (simulated via Valkey cache miss): Assert HTTP
401 Unauthorized. - Suspended account (simulated via mocked
sqlreturningaccount_status: 'suspended'): Assert HTTP403 Forbidden.
- Valid session cookie: Assert HTTP
Phase 3: Tier 3 (Zero-Trust Mesh & RBAC) Verification
- Target:
AuthService.validateSessionConnectRPC endpoint & handler. - Test Cases:
- Default-Deny: Unassigned user attempting to access a valid SPIFFE ID.
Assert
valid: falseand authorization rejection. - Valid RBAC Grant: Authenticated user with explicit grant for the
calling app's SPIFFE ID. Assert
valid: true, correct UUID, and correct scope (e.g.,viewer,admin). - SPIFFE Attestation Failure: Missing or invalid
x-peer-certheader. Assert validation failure.
- Default-Deny: Unassigned user attempting to access a valid SPIFFE ID.
Assert
Phase 4: Audit Ledger Verification
- Target:
auditLogfunction calls inserver/audit.ts. - Test Cases:
- Spy on
auditLoginserver/audit.ts. - Verify that successful logins (
login_success), login failures (login_failed), and explicit access denials (session_validation_failed) generate the correct event payload structures and are dispatched to the mocked DB.
- Spy on
Phase 5: Modular SDK Ergonomics & Hono Middleware
- Target:
sdk/hono.ts(new) andsdk/hono.test.ts(new). - Implementation Steps:
- Create
sdk/hono.tsexportingcreateAuthMiddleware(sdk: AuthSdk)so the coresdk/mod.tsremains framework-agnostic. - The middleware extracts the
session_idcookie orAuthorizationheader, callssdk.requireAuth(), and injects the resultinguserIdinto the Hono context (c.set('userId', userId)). - If
requireAuththrows, the middleware catches it and returns HTTP401 Unauthorized.
- Create
- Verification:
- In
sdk/hono.test.ts, create a test Hono app, attach the middleware, and verify that authorized requests pass and unauthorized requests return401 Unauthorized.
- In