paperclip/packages/shared/src/decision.test.ts

158 lines
4.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
decisionEffectSchema,
decisionInputsSchema,
decisionOptionSchema,
decisionOptionsSchema,
} from "./validators/decision.js";
const targetIssueId = "11111111-1111-4111-8111-111111111111";
const secondIssueId = "22222222-2222-4222-8222-222222222222";
describe("decision validators", () => {
it("accepts all six effect variants", () => {
const effects = [
{
type: "comment_on_issue",
targetIssueId,
staleness: "lenient",
bodyMarkdown: "Approved with {{input.note}}",
},
{
type: "create_issue",
targetIssueId,
staleness: "strict",
draft: { title: "Follow up", parentId: targetIssueId },
},
{
type: "update_issue_status",
targetIssueId,
staleness: "strict",
status: "done",
comment: "Decision approved",
},
{
type: "assign_issue",
targetIssueId,
staleness: "lenient",
assigneeAgentId: secondIssueId,
},
{
type: "cancel_issue_tree",
targetIssueId,
staleness: "strict",
reasonComment: "No longer needed",
},
{
type: "resolve_blocker",
targetIssueId,
staleness: "strict",
removeBlockedByIssueIds: [secondIssueId],
},
];
for (const effect of effects) {
expect(decisionEffectSchema.parse(effect)).toEqual(effect);
}
});
it("rejects malformed and unknown effects", () => {
expect(() => decisionEffectSchema.parse({
type: "comment_on_issue",
targetIssueId,
staleness: "strict",
})).toThrow();
expect(() => decisionEffectSchema.parse({
type: "delete_company",
targetIssueId,
staleness: "strict",
})).toThrow();
expect(() => decisionEffectSchema.parse({
type: "assign_issue",
targetIssueId,
staleness: "strict",
})).toThrow();
});
it("forces cancel-tree effects to be strict and destructive", () => {
expect(() => decisionEffectSchema.parse({
type: "cancel_issue_tree",
targetIssueId,
staleness: "lenient",
reasonComment: "No longer needed",
})).toThrow();
expect(() => decisionOptionSchema.parse({
id: "cancel",
label: "Cancel tree",
effects: [{
type: "cancel_issue_tree",
targetIssueId,
staleness: "strict",
reasonComment: "No longer needed",
}],
})).toThrow();
expect(decisionOptionSchema.parse({
id: "cancel",
label: "Cancel tree",
style: "destructive",
effects: [{
type: "cancel_issue_tree",
targetIssueId,
staleness: "strict",
reasonComment: "No longer needed",
}],
}).style).toBe("destructive");
});
it("enforces option, input, and effect limits", () => {
const dismissOption = { id: "dismiss", label: "Dismiss", effects: [] };
expect(decisionOptionsSchema.parse(Array.from({ length: 8 }, (_, index) => ({
...dismissOption,
id: `option-${index}`,
})))).toHaveLength(8);
expect(() => decisionOptionsSchema.parse(Array.from({ length: 9 }, (_, index) => ({
...dismissOption,
id: `option-${index}`,
})))).toThrow();
expect(decisionInputsSchema.parse(Array.from({ length: 4 }, (_, index) => ({
id: `input-${index}`,
label: `Input ${index}`,
})))).toHaveLength(4);
expect(() => decisionInputsSchema.parse(Array.from({ length: 5 }, (_, index) => ({
id: `input-${index}`,
label: `Input ${index}`,
})))).toThrow();
const commentEffect = {
type: "comment_on_issue",
targetIssueId,
staleness: "strict",
bodyMarkdown: "Comment",
};
expect(decisionOptionSchema.parse({
id: "approve",
label: "Approve",
effects: Array.from({ length: 10 }, () => commentEffect),
}).effects).toHaveLength(10);
expect(() => decisionOptionSchema.parse({
id: "approve",
label: "Approve",
effects: Array.from({ length: 11 }, () => commentEffect),
})).toThrow();
});
it("rejects duplicate option and input ids", () => {
expect(() => decisionOptionsSchema.parse([
{ id: "same", label: "One", effects: [] },
{ id: "same", label: "Two", effects: [] },
])).toThrow();
expect(() => decisionInputsSchema.parse([
{ id: "same", label: "One" },
{ id: "same", label: "Two" },
])).toThrow();
});
});