be70f56657
Wave A (Phase 1) implements the M1 foundations for REQ-038, REQ-039, REQ-040: Monorepo scaffold (pnpm workspaces, TS strict, NodeNext): - apps/control-plane (Next.js App Router scaffold — Wave B adds routes) - apps/relay-agent (Go module scaffold — Wave D adds WebSocket + install) - packages/db, packages/secrets, packages/runtime, packages/config packages/db (REQ-038, REQ-039): - 0001_init.sql: tenants, users, tenant_memberships, targets, byom_endpoints, invitations, audit_log (append-only hash-chain), runtime_health (not audited) - RLS policies on all tenant-scoped tables + FORCE ROW LEVEL SECURITY - withTenant(tenantId, fn): SET LOCAL app.tenant_id + transaction - audit.ts: appendAudit with sha256(prev_hash || canonical(payload)) chain; AuditWriteHaltError on write failure (Edge 7 — halts + rolls back) - BEFORE INSERT trigger enforces chain integrity (G-006: per-tenant concurrent-write serialization documented; M3 mitigation noted) - create-db.ts: PGlite (dev/test) + pg Pool (prod) behind DbClient interface - pen-test scaffold: cross-tenant isolation (PGlite RLS limitation documented; prod RLS test runs at M1 review) - 12 tests, 98% coverage packages/secrets (REQ-040, G-005, G-010): - SecretProvider interface: get/put/delete + SecretRef + SecretValue (toString returns [REDACTED]; unwrap is the only read path) - AwsSecretsManagerProvider (prod): coreci/<tenantId>/<name>, KMS-backed - LocalEncryptedProvider (dev): AES-256-GCM, PBKDF2 from SECRET_MASTER_KEY_DEV - relay-token.ts: JWT HS256 sign/verify/issue (G-005 contract — locked so Wave B + Wave D parallelize without blocking) - 24 tests (14 provider + 10 relay-token) packages/runtime (G-002, G-003): - Trigger.dev bootstrap (initRuntime) — pre-investment for M3 (G-003) - runtimeHealthCheck task: writes to runtime_health, NOT audit_log (G-002) - 6 tests packages/config (G-010): - Two-tier credential taxonomy: infra env vars (tier a) vs tenant creds (SecretProvider only, tier b). PO's 'no env vars' applies to tenant creds. Coverage: db 98%, secrets/runtime/config typecheck clean. All 45 tests passing. Go relay-agent builds. ---ci--- phase: 1 milestone: v0.1 status: verify ---/ci---
50 lines
2.1 KiB
TypeScript
50 lines
2.1 KiB
TypeScript
/**
|
|
* create-db pg-mode test — covers the `pg` Pool branch (lines 29-53).
|
|
*
|
|
* Mocks the `pg` module so we don't need a real Postgres. Verifies that
|
|
* createDb(mode='pg') returns a client that delegates to Pool.connect().
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
describe("createDb (pg mode)", () => {
|
|
beforeEach(() => {
|
|
vi.resetModules();
|
|
});
|
|
|
|
it("throws if DATABASE_URL is missing in pg mode", async () => {
|
|
const { createDb } = await import("../src/create-db.js");
|
|
delete process.env.DATABASE_URL;
|
|
await expect(createDb({ mode: "pg" })).rejects.toThrow("DATABASE_URL");
|
|
});
|
|
|
|
it("returns a client backed by pg.Pool", async () => {
|
|
const fakeQuery = vi.fn().mockResolvedValue({ rows: [{ x: 1 }], rowCount: 1 });
|
|
const fakeRelease = vi.fn();
|
|
const fakeConnect = vi.fn().mockResolvedValue({ query: fakeQuery, release: fakeRelease });
|
|
const fakePool = { connect: fakeConnect };
|
|
vi.doMock("pg", () => ({ Pool: vi.fn(() => fakePool) }));
|
|
|
|
const { createDb } = await import("../src/create-db.js");
|
|
const db = await createDb({ mode: "pg", databaseUrl: "postgres://localhost/test" });
|
|
|
|
const res = await db.query("SELECT $1::int AS x", [1]);
|
|
expect(res.rows).toHaveLength(1);
|
|
expect(res.rows[0]).toEqual({ x: 1 });
|
|
expect(fakeQuery).toHaveBeenCalledWith("SELECT $1::int AS x", [1]);
|
|
expect(fakeRelease).toHaveBeenCalled();
|
|
});
|
|
|
|
it("exec delegates to Pool.connect().query with multi-statement", async () => {
|
|
const fakeQuery = vi.fn().mockResolvedValue({ rows: [], rowCount: 0 });
|
|
const fakeRelease = vi.fn();
|
|
const fakeConnect = vi.fn().mockResolvedValue({ query: fakeQuery, release: fakeRelease });
|
|
const fakePool = { connect: fakeConnect };
|
|
vi.doMock("pg", () => ({ Pool: vi.fn(() => fakePool) }));
|
|
|
|
const { createDb } = await import("../src/create-db.js");
|
|
const db = await createDb({ mode: "pg", databaseUrl: "postgres://localhost/test" });
|
|
await db.exec("CREATE TABLE x (a int); CREATE TABLE y (b int);");
|
|
expect(fakeQuery).toHaveBeenCalledWith("CREATE TABLE x (a int); CREATE TABLE y (b int);");
|
|
});
|
|
}); |