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
@@ -160,7 +160,18 @@ test("suggests sensitive flags from structural metadata without persisting them"
});
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
const responseBody = response.json();
expect(responseBody).toMatchObject({
run: {
databaseId: database.id,
scope: "all",
modelId: configuredModel.id,
status: "completed",
total: 1,
suggestedSensitive: 1,
suggestedNonSensitive: 0,
errorSummary: null,
},
suggestions: [{
columnId: column.id,
tableId: table.id,
@@ -173,6 +184,43 @@ test("suggests sensitive flags from structural metadata without persisting them"
});
expect(await repository.getColumn(database.id, column.tableId, column.id))
.toMatchObject({ sensitive: false });
expect(responseBody.run).not.toHaveProperty("suggestions");
const history = await app.inject({
method: "GET",
url: "/catalog/sensitive-data-suggestion-runs?limit=1",
});
expect(history.statusCode).toBe(200);
expect(history.json()).toEqual([responseBody.run]);
expect(history.body).not.toContain(column.id);
const detail = await app.inject({
method: "GET",
url: `/catalog/sensitive-data-suggestion-runs/${responseBody.run.id}`,
});
expect(detail.statusCode).toBe(200);
expect(detail.json()).toEqual(responseBody.run);
const events = await app.inject({
method: "GET",
url: `/catalog/sensitive-data-suggestion-runs/${responseBody.run.id}/events-list`,
});
expect(events.statusCode).toBe(200);
expect(events.body).not.toContain(column.id);
expect(events.json()).toMatchObject([
{
runId: responseBody.run.id,
sequence: 1,
level: "info",
message: "Sensitive-field suggestion generation started.",
},
{
runId: responseBody.run.id,
sequence: 2,
level: "info",
message: "Sensitive-field suggestion generation completed for 1 column.",
},
]);
const request = modelCompleter.complete.mock.calls[0]![0] as ModelCompletionRequest;
const prompt = request.messages.map((message) => message.content).join("\n");
@@ -505,6 +553,42 @@ test("explains a sensitive-data suggestion provider failure without exposing pro
expect(response.body).not.toContain("model completion failed");
expect(await repository.getColumn(database.id, column.tableId, column.id))
.toMatchObject({ sensitive: false });
const history = await app.inject({
method: "GET",
url: "/catalog/sensitive-data-suggestion-runs",
});
expect(history.statusCode).toBe(200);
const [failedRun] = history.json();
expect(failedRun).toMatchObject({
databaseId: database.id,
status: "failed",
total: 1,
suggestedSensitive: 0,
suggestedNonSensitive: 0,
errorSummary: "Sensitive-field suggestion generation failed.",
});
const events = await app.inject({
method: "GET",
url: `/catalog/sensitive-data-suggestion-runs/${failedRun.id}/events-list`,
});
expect(events.statusCode).toBe(200);
expect(events.json()).toMatchObject([
{
runId: failedRun.id,
sequence: 1,
level: "info",
message: "Sensitive-field suggestion generation started.",
},
{
runId: failedRun.id,
sequence: 2,
level: "error",
message: "Sensitive-field suggestion generation failed.",
},
]);
expect(events.body).not.toContain("model completion failed");
} finally {
await app.close();
}
@@ -2676,10 +2760,25 @@ test("requires database.manage for every Description Generation route", async ()
url: "/catalog/description-generation-runs/99999999-9999-4999-8999-999999999999/events",
headers,
}),
app.inject({
method: "GET",
url: "/catalog/sensitive-data-suggestion-runs",
headers,
}),
app.inject({
method: "GET",
url: "/catalog/sensitive-data-suggestion-runs/99999999-9999-4999-8999-999999999999",
headers,
}),
app.inject({
method: "GET",
url: "/catalog/sensitive-data-suggestion-runs/99999999-9999-4999-8999-999999999999/events-list",
headers,
}),
]);
expect(responses.map((response) => response.statusCode)).toEqual([
403, 403, 403, 403, 403, 403, 403,
403, 403, 403, 403, 403, 403, 403, 403, 403, 403,
]);
expect(modelCompleter.complete).not.toHaveBeenCalled();
} finally {