AEGIS
July 11, 2026 · View on GitHub
Four harnesses, layered from cheap-and-fast to slow-and-thorough. Every push should run all four green.
| Harness | Command | Wall-clock | Catches |
|---|---|---|---|
| Jest unit / integration | npm test | ~8 s | Service + API logic in isolation. |
| E2E smoke | npm run test:e2e | ~7 s | Every endpoint responds correctly against a real gateway boot. |
| Tenant isolation | npm run test:isolation | ~5 s | Cross-tenant leaks — org A can't see org B's data. |
| SDK reliability chaos | npm run test:sdk-chaos | ~15 s | SDK survives gateway blip + restart, replays disk queue, doesn't panic on 4xx. |
Add :nobuild to any of the last three (test:e2e:nobuild,
test:isolation:nobuild, test:sdk-chaos:nobuild) to reuse the
existing gateway dist/ — useful in a fast local edit-loop.
Jest — npm test
Located under packages/gateway-mcp/src/__tests__/. 97 suites at
last count. Table-stakes: service logic in isolation with in-memory
SQLite, no HTTP, no gateway boot. Fast enough to run on save.
Notable suites:
trace-query-dsl.test.ts— parser + compiler + injection resistancerollback-chaos.test.ts— concurrent rollback race, DLQ, causal cyclesaga.test.ts— state machine transitionstrace-search.test.ts— integration test hitting FTS + generated columns
E2E smoke — npm run test:e2e
Source: tools/e2e/smoke.mjs. Boots the
gateway on a temp DB, seeds a dashboard key via node:sqlite, runs
12 golden path scenarios sequentially:
gateway_health· startup crash catchermetrics_prometheus· scrape format regressiontrace_ingest_and_list· insert + FTS trigger + listtrace_search_dsl· DSL compiler +$.arguments.labelregressiondelegation_endpoint· GET /:id/delegation for the cockpit waterfallsaved_queries_crud· lifecycle + DSL validation on savecheck_endpoint_reachable· policy engine responds structurallyauth_bootstrap· dashboard API key auto-issuerollback_saga_lifecycle· sagas / metrics / DLQ list endpointskill_switch_reachable· emergency stop routepolicies_reachable· policies endpoint on empty setcross_tenant_isolation· saved-queries CRUD with x-test-org header
Each scenario documents prevents: — the class of bug it catches.
Failed scenarios print the assertion + the docstring so the fix
context is one line away.
Tenant isolation — npm run test:isolation
Source: tools/e2e/tenant-isolation.mjs.
Seeds TWO org-scoped aegis_... keys (org-A, org-B) into a fresh
gateway, then runs 6 scenarios that verify org B cannot see, modify,
or delete org A's resources:
saved_queries_isolationpolicies_shape_consistencyagents_isolationrollback_sagas_isolationdlq_isolationtraces_search_isolation
Round D turned this harness up and found four real bugs in one sitting
(traces table had no org_id column, saved-queries wasn't behind
auth, datetime("now") with double quotes 500'd every request, and
the trace-search fixture had drifted). All fixed. This harness now
gates every merge.
SDK reliability chaos — npm run test:sdk-chaos
Source: tools/e2e/sdk_chaos.py. Boots
a gateway, uses the real Python SDK to send traces through it, kills
the gateway, sends more, restarts, spins up a fresh SDK instance,
verifies startup replay drained the disk queue. 5 scenarios:
happy_path— 5 traces sent, all deliveredgateway_kill_persist— outage → retries exhaust → circuit trips → traces on diskrestart_replay— new SDK instance drains the disk queue via startup replaymalformed_payload_does_not_trip— 4xx doesn't persist to disk, doesn't trip the breakermetrics_snapshot_shape— every documented counter is present
Load test — npm run loadtest
Source: tools/loadtest/. Not run on every push
(it's a real load test with per-scenario duration). Writes
PERFORMANCE.md with the latest numbers.
Running the full matrix locally
npm test # ~8 s
npm run test:e2e # ~7 s
npm run test:isolation # ~5 s
npm run test:sdk-chaos # ~15 s
Total: ~35 s for a full-matrix green. If one harness fails, the others still run — parallel-safe (each boots on a different port).
Wiring into CI
Sample GitHub Actions job:
- run: npm ci
- run: npm test
- run: npm run test:e2e
- run: npm run test:isolation
- run: npm run test:sdk-chaos
The last three use --no-build flavours if you build once earlier
in the job:
- run: npm run build
- run: npm test
- run: npm run test:e2e:nobuild
- run: npm run test:isolation:nobuild
- run: npm run test:sdk-chaos:nobuild
Writing a new scenario
Each harness is a single .mjs / .py file with a SCENARIOS
array. Copy the shape of any existing entry:
{
name: 'my_scenario',
prevents: 'What class of bug does this catch — one sentence.',
run: async () => {
// hit the gateway via `http()`, assert on responses
const r = await http('GET', '/api/v1/something');
assertEq(r.status, 200, 'something status');
}
}
The prevents: docstring is required — if the test fires on a
future regression, the docstring tells the maintainer WHY it exists
without them having to git blame.