149 lines
4.8 KiB
TypeScript
149 lines
4.8 KiB
TypeScript
import { strict as assert } from "node:assert";
|
|
import test from "node:test";
|
|
import { buildAiAgentStepItems } from "../../apps/desktop/src/lib/aiAgentStepPresentation.ts";
|
|
import type { AiAgentPlan } from "../../apps/desktop/src/lib/aiAgentPlan.ts";
|
|
|
|
test("presents auto-execute agent plans as completed generation, safety, and execution steps", () => {
|
|
const plan: AiAgentPlan = {
|
|
executableSql: "SELECT count(*) FROM users",
|
|
handoffSql: "SELECT count(*) FROM users",
|
|
steps: [
|
|
{ kind: "generate_sql", status: "done", sql: "SELECT count(*) FROM users" },
|
|
{
|
|
kind: "risk_check",
|
|
status: "done",
|
|
action: "auto_execute",
|
|
environment: "non_production",
|
|
category: "read",
|
|
reasons: [],
|
|
},
|
|
{ kind: "execute_sql", status: "pending", sql: "SELECT count(*) FROM users" },
|
|
],
|
|
};
|
|
|
|
assert.deepEqual(buildAiAgentStepItems(plan), [
|
|
{ key: "generated", labelKey: "ai.agentSteps.generated", tone: "success" },
|
|
{
|
|
key: "safe",
|
|
labelKey: "ai.agentSteps.safe",
|
|
titleKey: "ai.agentStepTitles.riskCheck",
|
|
titleParams: { action: "auto_execute", category: "read", environment: "non_production", reasons: "-" },
|
|
tone: "success",
|
|
},
|
|
{ key: "autoExecute", labelKey: "ai.agentSteps.autoExecute", tone: "active" },
|
|
]);
|
|
});
|
|
|
|
test("presents blocked and confirmation plans with warning tones", () => {
|
|
const blocked: AiAgentPlan = {
|
|
handoffSql: "DROP TABLE users",
|
|
steps: [
|
|
{ kind: "generate_sql", status: "done", sql: "DROP TABLE users" },
|
|
{
|
|
kind: "risk_check",
|
|
status: "done",
|
|
action: "block",
|
|
environment: "non_production",
|
|
category: "dangerous",
|
|
reasons: [],
|
|
},
|
|
{ kind: "execute_sql", status: "skipped", reason: "blocked_by_policy" },
|
|
],
|
|
};
|
|
const confirm: AiAgentPlan = {
|
|
handoffSql: "INSERT INTO users(name) VALUES ('a')",
|
|
steps: [
|
|
{ kind: "generate_sql", status: "done", sql: "INSERT INTO users(name) VALUES ('a')" },
|
|
{
|
|
kind: "risk_check",
|
|
status: "done",
|
|
action: "confirm",
|
|
environment: "production",
|
|
category: "low_risk_write",
|
|
reasons: [],
|
|
},
|
|
{ kind: "execute_sql", status: "skipped", reason: "requires_confirmation" },
|
|
],
|
|
};
|
|
|
|
assert.deepEqual(
|
|
buildAiAgentStepItems(blocked).map((item) => [item.labelKey, item.tone, item.titleKey, item.titleParams]),
|
|
[
|
|
["ai.agentSteps.generated", "success", undefined, undefined],
|
|
[
|
|
"ai.agentSteps.blocked",
|
|
"danger",
|
|
"ai.agentStepTitles.riskCheck",
|
|
{ action: "block", category: "dangerous", environment: "non_production", reasons: "-" },
|
|
],
|
|
["ai.agentSteps.skipped", "muted", "ai.agentStepTitles.blocked", undefined],
|
|
],
|
|
);
|
|
assert.deepEqual(
|
|
buildAiAgentStepItems(confirm).map((item) => [item.labelKey, item.tone, item.titleKey, item.titleParams]),
|
|
[
|
|
["ai.agentSteps.generated", "success", undefined, undefined],
|
|
[
|
|
"ai.agentSteps.needsConfirm",
|
|
"warning",
|
|
"ai.agentStepTitles.riskCheck",
|
|
{ action: "confirm", category: "low_risk_write", environment: "production", reasons: "-" },
|
|
],
|
|
["ai.agentSteps.skipped", "muted", "ai.agentStepTitles.requiresConfirmation", undefined],
|
|
],
|
|
);
|
|
});
|
|
|
|
test("includes risk reasons in risk check titles", () => {
|
|
const plan: AiAgentPlan = {
|
|
handoffSql: "INSERT INTO users(name) VALUES ('a'); UPDATE users SET name='b' WHERE id=1",
|
|
steps: [
|
|
{
|
|
kind: "risk_check",
|
|
status: "done",
|
|
action: "confirm",
|
|
environment: "non_production",
|
|
category: "write",
|
|
reasons: ["multi_statement"],
|
|
},
|
|
],
|
|
};
|
|
|
|
assert.deepEqual(buildAiAgentStepItems(plan)[0], {
|
|
key: "needsConfirm",
|
|
labelKey: "ai.agentSteps.needsConfirm",
|
|
titleKey: "ai.agentStepTitles.riskCheck",
|
|
titleParams: {
|
|
action: "confirm",
|
|
category: "write",
|
|
environment: "non_production",
|
|
reasons: "multi_statement",
|
|
},
|
|
tone: "warning",
|
|
});
|
|
});
|
|
|
|
test("presents no-sql and no-intent plans as muted skipped states", () => {
|
|
const noSql: AiAgentPlan = {
|
|
steps: [
|
|
{ kind: "generate_sql", status: "skipped", reason: "no_sql" },
|
|
{ kind: "execute_sql", status: "skipped", reason: "no_sql" },
|
|
],
|
|
};
|
|
const noIntent: AiAgentPlan = {
|
|
steps: [
|
|
{ kind: "generate_sql", status: "done", sql: "SELECT count(*) FROM users" },
|
|
{ kind: "execute_sql", status: "skipped", reason: "no_execution_intent" },
|
|
],
|
|
};
|
|
|
|
assert.deepEqual(
|
|
buildAiAgentStepItems(noSql).map((item) => item.labelKey),
|
|
["ai.agentSteps.noSql", "ai.agentSteps.skipped"],
|
|
);
|
|
assert.deepEqual(
|
|
buildAiAgentStepItems(noIntent).map((item) => item.labelKey),
|
|
["ai.agentSteps.generated", "ai.agentSteps.notRequested"],
|
|
);
|
|
});
|