123 lines
5.9 KiB
TypeScript
123 lines
5.9 KiB
TypeScript
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, expect, test, vi } from "vitest";
|
|
import { buildApp } from "../src/app.js";
|
|
import { loadConfig } from "../src/config.js";
|
|
import { MemoryCatalogRepository } from "../src/catalog/memory-repository.js";
|
|
import { WorkspaceSecretStore } from "../src/workspaces/secret-store.js";
|
|
import type { WorkspaceRegistry, WorkspaceRevision } from "../src/workspaces/registry.js";
|
|
import type { WorkspaceDescriptor } from "../src/workspaces/schema.js";
|
|
|
|
const roots: string[] = [];
|
|
afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); });
|
|
|
|
const workspace: WorkspaceDescriptor = {
|
|
workspace: { schema_version: 3, id: "psd-clinical", name: "Policlinico San Donato", language: "it" },
|
|
dwh: {
|
|
engine: "postgres", database: "warehouse", schema: "datawarehouse", port: 5432,
|
|
supported_transports: ["postgres_direct", "rest_api"],
|
|
},
|
|
semantic_index: {
|
|
vector_store: { engine: "qdrant", collection: "psd", dimensions: 1024, distance: "cosine" },
|
|
embedding: { provider: "ollama_internal", model: "qwen3-embedding:0.6b", dimensions: 1024 },
|
|
},
|
|
llm_policy: { allowed: ["zai/glm-5.2"] },
|
|
diagnostics: { dwh_rest: { method: "GET", path: "/health", auth: "bearer", response: { database: "database", schema: "schema" } } },
|
|
};
|
|
const revision: WorkspaceRevision = { id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/tmp/psd.yaml" };
|
|
|
|
function setup() {
|
|
const secretRoot = mkdtempSync(join(tmpdir(), "catalog-secret-"));
|
|
const runtimeRoot = mkdtempSync(join(tmpdir(), "catalog-secret-runtime-"));
|
|
roots.push(secretRoot, runtimeRoot);
|
|
const secretStore = new WorkspaceSecretStore({ root: secretRoot, runtimeRoot, installationId: "test" });
|
|
const repository = new MemoryCatalogRepository();
|
|
const registry = {
|
|
list: vi.fn(async () => [revision]),
|
|
listCatalog: vi.fn(async () => [{ id: "psd-clinical", name: "Policlinico San Donato", configurationState: "ready", revision }]),
|
|
read: vi.fn(async () => ({ workspace, revision })),
|
|
} as unknown as WorkspaceRegistry;
|
|
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "/missing", NODE_ENV: "test" }), {
|
|
thtRunner: {} as never,
|
|
workspaceRegistry: registry,
|
|
workspaceSecretStore: secretStore,
|
|
catalogRepository: repository,
|
|
workspaceDiagnoser: vi.fn(),
|
|
});
|
|
return { app, secretStore, repository };
|
|
}
|
|
|
|
const direct = {
|
|
workspaceId: "psd-clinical",
|
|
engine: "postgres",
|
|
databaseName: "warehouse",
|
|
schema: "datawarehouse",
|
|
binding: { transport: "postgres_direct", host: "db.internal", port: 5432, username: "reader" },
|
|
};
|
|
|
|
test("lists every YAML workspace and creates its one database configuration", async () => {
|
|
const { app } = setup();
|
|
const initial = await app.inject({ method: "GET", url: "/catalog/databases" });
|
|
expect(initial.statusCode).toBe(200);
|
|
expect(initial.json()).toMatchObject([{ workspaceId: "psd-clinical", configured: false, databaseName: "warehouse" }]);
|
|
|
|
const created = await app.inject({ method: "POST", url: "/catalog/databases", payload: direct });
|
|
expect(created.statusCode).toBe(201);
|
|
expect(created.json()).toMatchObject({ configured: true, workspaceId: "psd-clinical", version: 1 });
|
|
expect((await app.inject({ method: "POST", url: "/catalog/databases", payload: direct })).statusCode).toBe(409);
|
|
|
|
const listed = await app.inject({ method: "GET", url: "/catalog/databases" });
|
|
expect(listed.json()).toMatchObject([{ configured: true, binding: { transport: "postgres_direct", host: "db.internal" } }]);
|
|
});
|
|
|
|
test("lists orphaned records and takes the REST diagnostic path from workspace YAML", async () => {
|
|
const { app, repository } = setup();
|
|
await repository.create({
|
|
workspaceId: "removed-workspace",
|
|
engine: "postgres",
|
|
databaseName: "legacy",
|
|
schema: "public",
|
|
binding: { transport: "postgres_direct", host: "legacy.internal", port: 5432, username: "reader" },
|
|
});
|
|
const created = await app.inject({
|
|
method: "POST",
|
|
url: "/catalog/databases",
|
|
payload: {
|
|
...direct,
|
|
binding: {
|
|
transport: "rest_api", baseUrl: "https://psd.example/api", restPath: "/client-controlled", restAuth: "bearer",
|
|
},
|
|
},
|
|
});
|
|
expect(created.statusCode).toBe(201);
|
|
expect(created.json()).toMatchObject({ binding: { restPath: "/health" } });
|
|
|
|
const rows = (await app.inject({ method: "GET", url: "/catalog/databases" })).json();
|
|
expect(rows).toEqual(expect.arrayContaining([
|
|
expect.objectContaining({ workspaceId: "removed-workspace", configured: true, workspaceAvailable: false }),
|
|
expect.objectContaining({ workspaceId: "psd-clinical", configured: true, workspaceAvailable: true }),
|
|
]));
|
|
});
|
|
|
|
test("uses optimistic versions, keeps secrets write-only, and hard-deletes only local configuration", async () => {
|
|
const { app, secretStore } = setup();
|
|
const created = (await app.inject({ method: "POST", url: "/catalog/databases", payload: direct })).json();
|
|
const stale = await app.inject({ method: "PATCH", url: `/catalog/databases/${created.id}`, payload: { ...direct, version: 99 } });
|
|
expect(stale.statusCode).toBe(409);
|
|
|
|
const secret = await app.inject({
|
|
method: "PUT", url: `/catalog/databases/${created.id}/secrets`,
|
|
payload: { version: 1, values: { password: "do-not-return-this" } },
|
|
});
|
|
expect(secret.statusCode).toBe(200);
|
|
expect(secret.body).not.toContain("do-not-return-this");
|
|
expect(secret.json()).toMatchObject({ version: 2, secrets: { password: true } });
|
|
expect(secretStore.has("psd-clinical", "catalog.dwh.password")).toBe(true);
|
|
|
|
const removed = await app.inject({ method: "DELETE", url: `/catalog/databases/${created.id}?version=2` });
|
|
expect(removed.statusCode).toBe(204);
|
|
expect(secretStore.has("psd-clinical", "catalog.dwh.password")).toBe(false);
|
|
expect((await app.inject({ method: "GET", url: "/catalog/databases" })).json()).toMatchObject([{ configured: false }]);
|
|
});
|