9482a97795
CI (SIAX Cloud) / security (push) Successful in 13s
CI (SIAX Cloud) / contracts (pull_request) Successful in 17s
CI (SIAX Cloud) / security (pull_request) Successful in 13s
CI (SIAX Cloud) / contracts (push) Successful in 16s
CI (SIAX Cloud) / quality (push) Successful in 52s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m13s
- schema migration 001 (registries, scans; owner_sub = introspected subject) - pg pool + services; scan ownership enforced in SQL (INSERT ... SELECT WHERE owner_sub) - real handlers: 201/400/404/500, owner-scoped GET/POST, listScans registryId filter - graceful shutdown closes pool; 7 new persistence tests (21/21)
88 lines
3.2 KiB
TypeScript
88 lines
3.2 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { listRegistries, createRegistry, getRegistry } from "../src/services/registries.js";
|
|
import { createScan, listScans, getScan } from "../src/services/scans.js";
|
|
import type { PoolLike } from "../src/services/registries.js";
|
|
|
|
function fakePool(handlers: {
|
|
query: (text: string, values?: unknown[]) => { rows: Record<string, unknown>[] };
|
|
}): PoolLike & { queries: { text: string; values?: unknown[] }[] } {
|
|
const queries: { text: string; values?: unknown[] }[] = [];
|
|
return {
|
|
queries,
|
|
query(text, values) {
|
|
queries.push({ text, values });
|
|
return handlers.query(text, values);
|
|
},
|
|
};
|
|
}
|
|
|
|
const ROW = {
|
|
id: "11111111-1111-1111-1111-111111111111",
|
|
owner_sub: "svc-user",
|
|
name: "example",
|
|
url: "https://example.com",
|
|
config: {},
|
|
created_at: new Date("2026-09-16T10:00:00Z"),
|
|
};
|
|
|
|
describe("registries service", () => {
|
|
it("listRegistries filters by owner", async () => {
|
|
const db = fakePool({ query: () => ({ rows: [ROW] }) });
|
|
const out = await listRegistries(db, "svc-user");
|
|
expect(out).toHaveLength(1);
|
|
expect(out[0]).toMatchObject({ id: ROW.id, name: "example", createdAt: "2026-09-16T10:00:00.000Z" });
|
|
expect(db.queries[0].values).toEqual(["svc-user"]);
|
|
expect(db.queries[0].text).toContain("owner_sub = $1");
|
|
});
|
|
|
|
it("createRegistry inserts with owner and config jsonb", async () => {
|
|
const db = fakePool({ query: () => ({ rows: [ROW] }) });
|
|
const out = await createRegistry(db, "svc-user", { name: "example", url: "https://example.com" });
|
|
expect(out.id).toBe(ROW.id);
|
|
expect(db.queries[0].values?.[0]).toBe("svc-user");
|
|
expect(db.queries[0].text).toContain("RETURNING");
|
|
});
|
|
|
|
it("getRegistry returns null when not owner (fail-closed)", async () => {
|
|
const db = fakePool({ query: () => ({ rows: [] }) });
|
|
const out = await getRegistry(db, "other-user", ROW.id);
|
|
expect(out).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("scans service", () => {
|
|
const SCAN_ROW = {
|
|
...ROW,
|
|
registry_id: ROW.id,
|
|
status: "pending",
|
|
result: {},
|
|
updated_at: ROW.created_at,
|
|
};
|
|
|
|
it("createScan enforces registry ownership in SQL and returns null otherwise", async () => {
|
|
const db = fakePool({ query: () => ({ rows: [] }) });
|
|
const out = await createScan(db, "svc-user", ROW.id);
|
|
expect(out).toBeNull();
|
|
expect(db.queries[0].text).toContain("owner_sub = $1");
|
|
});
|
|
|
|
it("createScan returns scan for owned registry", async () => {
|
|
const db = fakePool({ query: () => ({ rows: [SCAN_ROW] }) });
|
|
const out = await createScan(db, "svc-user", ROW.id);
|
|
expect(out).toMatchObject({ id: SCAN_ROW.id, registryId: SCAN_ROW.id, status: "pending" });
|
|
});
|
|
|
|
it("listScans supports optional registryId filter", async () => {
|
|
const db = fakePool({ query: () => ({ rows: [SCAN_ROW] }) });
|
|
await listScans(db, "svc-user");
|
|
expect(db.queries[0].text).not.toContain("registry_id = $2");
|
|
await listScans(db, "svc-user", SCAN_ROW.id);
|
|
expect(db.queries[1].values).toEqual(["svc-user", SCAN_ROW.id]);
|
|
});
|
|
|
|
it("getScan returns null for foreign scan", async () => {
|
|
const db = fakePool({ query: () => ({ rows: [] }) });
|
|
expect(await getScan(db, "other-user", SCAN_ROW.id)).toBeNull();
|
|
});
|
|
});
|