feat: complete catalog fleet management workflow

This commit is contained in:
Codex
2026-08-31 15:58:43 +02:00
parent 866aee4249
commit 9c697dc062
56 changed files with 6692 additions and 611 deletions
@@ -11,6 +11,7 @@ import { up as upSchemaSync } from "../src/catalog/migrations/003_catalog_schema
import { up as upRuntimeSequencePrivileges } from "../src/catalog/migrations/004_catalog_runtime_sequence_privileges.js";
import { up as upDescriptionGeneration } from "../src/catalog/migrations/005_description_generation_runs.js";
import { up as upSensitiveDataFlag } from "../src/catalog/migrations/006_sensitive_data_flag.js";
import { up as upSensitiveSuggestionRuns } from "../src/catalog/migrations/007_sensitive_data_suggestion_runs.js";
const dockerAvailable = spawnSync("docker", ["info"], { stdio: "ignore" }).status === 0;
@@ -98,6 +99,33 @@ test.skipIf(!dockerAvailable)("PostgreSQL migration enforces one database per wo
patientName.generatedDescription,
true,
)).toMatchObject({ sensitive: true });
expect(await repository.getCatalogMetrics(created.id)).toEqual({
scope: "database",
databaseId: created.id,
tables: 2,
columns: 4,
sensitiveColumns: 1,
relationships: 0,
descriptionTargets: 6,
describedTargets: 1,
descriptionCoverage: 17,
updatedAt: expect.any(String),
});
expect(await repository.getCatalogMetrics()).toEqual({
scope: "global",
databaseId: null,
tables: 2,
columns: 4,
sensitiveColumns: 1,
relationships: 0,
descriptionTargets: 6,
describedTargets: 1,
descriptionCoverage: 17,
updatedAt: expect.any(String),
});
expect(await repository.getCatalogMetrics(
"99999999-9999-4999-8999-999999999999",
)).toBeUndefined();
const refreshedColumnsSnapshot: ObservedSchemaSnapshot = {
...fullColumnsSnapshot,
schemaVersion: 2,
@@ -340,7 +368,7 @@ test.skipIf(!dockerAvailable)("PostgreSQL repository atomically consolidates sel
}
}, 60_000);
test.skipIf(!dockerAvailable)("PostgreSQL repository persists globally exclusive Description Generation Runs and ordered events", async () => {
test.skipIf(!dockerAvailable)("PostgreSQL repository persists description and sensitive suggestion run histories", async () => {
const container = await new PostgreSqlContainer("postgres:17.6-bookworm").start();
const db = new Kysely<CatalogDatabase>({
dialect: new PostgresDialect({ pool: new Pool({ connectionString: container.getConnectionUri() }) }),
@@ -352,6 +380,7 @@ test.skipIf(!dockerAvailable)("PostgreSQL repository persists globally exclusive
await upSchemaSync(db);
await upSensitiveDataFlag(db);
await upDescriptionGeneration(db);
await upSensitiveSuggestionRuns(db);
const repository = new KyselyCatalogRepository(db);
const firstDatabase = await repository.create({
workspaceId: "generation-one",
@@ -519,6 +548,63 @@ test.skipIf(!dockerAvailable)("PostgreSQL repository persists globally exclusive
}),
]);
expect(await repository.getActiveDescriptionGenerationRun()).toBeUndefined();
const suggestionRun = await repository.createSensitiveDataSuggestionRun(
firstDatabase.id,
"selected_columns",
"openai-mini",
);
expect(suggestionRun).toMatchObject({
databaseId: firstDatabase.id,
status: "running",
total: 0,
suggestedSensitive: 0,
suggestedNonSensitive: 0,
startedAt: expect.any(String),
});
await repository.appendSensitiveDataSuggestionEvent(
suggestionRun.id,
"info",
"Sensitive-field suggestion generation started.",
);
await repository.appendSensitiveDataSuggestionEvent(
suggestionRun.id,
"info",
"Sensitive-field suggestion generation completed for 2 columns.",
);
expect(await repository.updateSensitiveDataSuggestionRun(suggestionRun.id, {
status: "completed",
total: 2,
suggestedSensitive: 1,
suggestedNonSensitive: 1,
finishedAt: new Date().toISOString(),
})).toMatchObject({
status: "completed",
total: 2,
suggestedSensitive: 1,
suggestedNonSensitive: 1,
});
expect(await repository.listSensitiveDataSuggestionEvents(suggestionRun.id, 1)).toEqual([
expect.objectContaining({ sequence: 2, level: "info" }),
]);
expect((await repository.listSensitiveDataSuggestionRuns(1))[0]).toMatchObject({
id: suggestionRun.id,
});
const interruptedSuggestionRun = await repository.createSensitiveDataSuggestionRun(
secondDatabase.id,
"all",
"openai-mini",
);
expect(await repository.interruptActiveSensitiveDataSuggestionRuns(
"Sensitive-field suggestion generation was interrupted by backend restart.",
)).toEqual([
expect.objectContaining({
id: interruptedSuggestionRun.id,
status: "interrupted",
finishedAt: expect.any(String),
}),
]);
} finally {
await db.destroy();
await container.stop();