Files
ThothII/backend/test/catalog-service.test.ts
Codex f586152636
Publish documentation / publish (push) Successful in 38s
fix: close catalog review gaps
2026-08-31 16:28:42 +02:00

112 lines
3.5 KiB
TypeScript

import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { MemoryCatalogRepository } from "../src/catalog/memory-repository.js";
import { CatalogOperationCoordinator } from "../src/catalog/operation-coordinator.js";
import type { CatalogPostgresAccess } from "../src/catalog/postgres-access.js";
import { CatalogService } from "../src/catalog/service.js";
import type { DatabaseTestResult, WorkspaceDatabase } from "../src/catalog/types.js";
import type { WorkspaceRegistry } from "../src/workspaces/registry.js";
import { WorkspaceSecretStore } from "../src/workspaces/secret-store.js";
interface Deferred {
promise: Promise<void>;
resolve: () => void;
}
function deferred(): Deferred {
let resolve!: () => void;
const promise = new Promise<void>((done) => { resolve = done; });
return { promise, resolve };
}
class PausingRecordRepository extends MemoryCatalogRepository {
constructor(
private readonly recordStarted: Deferred,
private readonly continueRecord: Deferred,
) {
super();
}
override async recordTest(
id: string,
expectedVersion: number,
result: DatabaseTestResult,
): Promise<WorkspaceDatabase | undefined> {
this.recordStarted.resolve();
await this.continueRecord.promise;
return await super.recordTest(id, expectedVersion, result);
}
}
test("holds the database reservation until the connection result is recorded", async () => {
const secretRoot = mkdtempSync(join(tmpdir(), "catalog-service-secret-"));
const runtimeRoot = mkdtempSync(join(tmpdir(), "catalog-service-runtime-"));
const recordStarted = deferred();
const continueRecord = deferred();
const repository = new PausingRecordRepository(recordStarted, continueRecord);
const coordinator = new CatalogOperationCoordinator();
const secretStore = new WorkspaceSecretStore({
root: secretRoot,
runtimeRoot,
installationId: "test",
});
const postgres: CatalogPostgresAccess = {
connect: async () => ({
query: async () => ({ rows: [{ database: "warehouse", schema: "datawarehouse" }] }),
end: async () => undefined,
}),
};
try {
const database = await repository.create({
workspaceId: "psd-clinical",
engine: "postgres",
databaseName: "warehouse",
schema: "datawarehouse",
binding: {
transport: "postgres_direct",
host: "db.internal",
port: 5432,
username: "reader",
},
});
const service = new CatalogService(
repository,
{} as WorkspaceRegistry,
secretStore,
[],
1_000,
postgres,
coordinator,
);
const connectionTest = service.test(database);
const firstCompletedPhase = await Promise.race([
recordStarted.promise.then(() => "recording" as const),
connectionTest.then(() => "returned" as const),
]);
expect(firstCompletedPhase).toBe("recording");
try {
await expect(coordinator.run(database.id, async () => "overlapped"))
.rejects.toThrow("A database operation is already in progress");
} finally {
continueRecord.resolve();
}
expect(await connectionTest).toMatchObject({
id: database.id,
version: 1,
connectionStatus: "reachable",
testedVersion: 1,
});
expect(await coordinator.run(database.id, async () => "released")).toBe("released");
} finally {
continueRecord.resolve();
rmSync(secretRoot, { recursive: true, force: true });
rmSync(runtimeRoot, { recursive: true, force: true });
}
});