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; resolve: () => void; } function deferred(): Deferred { let resolve!: () => void; const promise = new Promise((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 { 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 }); } });