test(cli): cover heartbeat scope guards

This commit is contained in:
Luke 2026-09-11 12:51:12 +02:00
parent eb63af2348
commit 6aa7cee733
2 changed files with 47 additions and 0 deletions

View File

@ -26,6 +26,16 @@ async function run(args: string[]): Promise<void> {
], { from: "user" });
}
async function expectCommandFailure(args: string[], message: string): Promise<void> {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(process, "exit").mockImplementation((code) => {
throw new Error(`process.exit:${code}`);
});
await expect(run(args)).rejects.toThrow("process.exit:1");
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining(message));
}
describe("agent lifecycle commands", () => {
beforeEach(() => {
vi.restoreAllMocks();
@ -95,6 +105,42 @@ describe("agent lifecycle commands", () => {
});
});
it.each([
{
args: ["agent", "heartbeat:invoke", AGENT_ID],
message: "Specify --issue-id for task work or --allow-unscoped for a generic heartbeat",
},
{
args: ["agent", "heartbeat:invoke", AGENT_ID, "--issue-id", "PC-42", "--allow-unscoped"],
message: "Use either --issue-id or --allow-unscoped, not both",
},
])("rejects an invalid heartbeat scope choice", async ({ args, message }) => {
const fetchMock = vi.fn();
vi.stubGlobal("fetch", fetchMock);
await expectCommandFailure(args, message);
expect(fetchMock).not.toHaveBeenCalled();
});
it("rejects an issue assigned to another agent without posting a heartbeat", async () => {
const fetchMock = vi.fn().mockImplementation(() => Promise.resolve(jsonResponse({
id: ISSUE_ID,
identifier: "PC-42",
assigneeAgentId: "55555555-5555-4555-8555-555555555555",
})));
vi.stubGlobal("fetch", fetchMock);
await expectCommandFailure(
["agent", "heartbeat:invoke", AGENT_ID, "--issue-id", "PC-42"],
`Issue PC-42 is not assigned to agent ${AGENT_ID}`,
);
expect(fetchMock.mock.calls.map((call) => [call[1]?.method ?? "GET", call[0]])).toEqual([
["GET", "http://localhost:3100/api/issues/PC-42"],
]);
});
it("wraps configuration, runtime, skills, and instructions endpoints", async () => {
const fetchMock = vi.fn().mockImplementation(() => Promise.resolve(jsonResponse()));
vi.stubGlobal("fetch", fetchMock);

View File

@ -6655,6 +6655,7 @@ registry.registerPath({
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
409: r.conflict,
},
});