feat: complete catalog fleet management workflow
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user