First Release of Claw3D (#11)
Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { performCronCreateFlow } from "@/features/agents/operations/cronCreateOperation";
|
||||
import type { CronCreateDraft } from "@/lib/cron/createPayloadBuilder";
|
||||
import type { CronJobCreateInput, CronJobSummary } from "@/lib/cron/types";
|
||||
|
||||
const createDraft = (): CronCreateDraft => ({
|
||||
templateId: "custom",
|
||||
name: "Nightly sync",
|
||||
taskText: "Sync project status and report blockers.",
|
||||
scheduleKind: "every",
|
||||
everyAmount: 30,
|
||||
everyUnit: "minutes",
|
||||
deliveryMode: "announce",
|
||||
deliveryChannel: "last",
|
||||
});
|
||||
|
||||
const createJob = (id: string, agentId: string, updatedAtMs: number): CronJobSummary => ({
|
||||
id,
|
||||
name: id,
|
||||
agentId,
|
||||
enabled: true,
|
||||
updatedAtMs,
|
||||
schedule: { kind: "every", everyMs: 60_000 },
|
||||
sessionTarget: "isolated",
|
||||
wakeMode: "now",
|
||||
payload: { kind: "agentTurn", message: "Run task" },
|
||||
state: {},
|
||||
});
|
||||
|
||||
const createInput = (): CronJobCreateInput => ({
|
||||
name: "Nightly sync",
|
||||
agentId: "agent-1",
|
||||
enabled: true,
|
||||
schedule: { kind: "every", everyMs: 1_800_000 },
|
||||
sessionTarget: "isolated",
|
||||
wakeMode: "now",
|
||||
payload: { kind: "agentTurn", message: "Sync project status and report blockers." },
|
||||
delivery: { mode: "announce", channel: "last" },
|
||||
});
|
||||
|
||||
describe("cron create flow state", () => {
|
||||
it("successful_create_refreshes_list_for_selected_agent", async () => {
|
||||
const client = {} as never;
|
||||
const onBusyChange = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const onJobs = vi.fn();
|
||||
|
||||
const buildInput = vi.fn(() => createInput());
|
||||
const createCronJob = vi.fn(async () => createJob("created", "agent-1", 15));
|
||||
const listCronJobs = vi.fn(async () => ({
|
||||
jobs: [
|
||||
createJob("older", "agent-1", 10),
|
||||
createJob("newer", "agent-1", 20),
|
||||
createJob("other-agent", "agent-2", 30),
|
||||
],
|
||||
}));
|
||||
|
||||
await expect(
|
||||
performCronCreateFlow({
|
||||
client,
|
||||
agentId: "agent-1",
|
||||
draft: createDraft(),
|
||||
busy: { createBusy: false, runBusyJobId: null, deleteBusyJobId: null },
|
||||
onBusyChange,
|
||||
onError,
|
||||
onJobs,
|
||||
deps: { buildInput, createCronJob, listCronJobs },
|
||||
})
|
||||
).resolves.toBe("created");
|
||||
|
||||
expect(buildInput).toHaveBeenCalledWith("agent-1", expect.any(Object));
|
||||
expect(createCronJob).toHaveBeenCalledWith(client, createInput());
|
||||
expect(listCronJobs).toHaveBeenCalledWith(client, { includeDisabled: true });
|
||||
expect(onJobs).toHaveBeenCalledWith([
|
||||
createJob("newer", "agent-1", 20),
|
||||
createJob("older", "agent-1", 10),
|
||||
]);
|
||||
expect(onError).toHaveBeenCalledWith(null);
|
||||
expect(onBusyChange).toHaveBeenNthCalledWith(1, true);
|
||||
expect(onBusyChange).toHaveBeenNthCalledWith(2, false);
|
||||
});
|
||||
|
||||
it("create_failure_surfaces_cron_error_message", async () => {
|
||||
const onBusyChange = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const onJobs = vi.fn();
|
||||
const expectedError = new Error("Gateway exploded");
|
||||
|
||||
await expect(
|
||||
performCronCreateFlow({
|
||||
client: {} as never,
|
||||
agentId: "agent-1",
|
||||
draft: createDraft(),
|
||||
busy: { createBusy: false, runBusyJobId: null, deleteBusyJobId: null },
|
||||
onBusyChange,
|
||||
onError,
|
||||
onJobs,
|
||||
deps: {
|
||||
buildInput: vi.fn(() => createInput()),
|
||||
createCronJob: vi.fn(async () => {
|
||||
throw expectedError;
|
||||
}),
|
||||
listCronJobs: vi.fn(async () => ({ jobs: [] })),
|
||||
},
|
||||
})
|
||||
).rejects.toThrow("Gateway exploded");
|
||||
|
||||
expect(onError).toHaveBeenNthCalledWith(1, null);
|
||||
expect(onError).toHaveBeenNthCalledWith(2, "Gateway exploded");
|
||||
expect(onBusyChange).toHaveBeenNthCalledWith(1, true);
|
||||
expect(onBusyChange).toHaveBeenNthCalledWith(2, false);
|
||||
expect(onJobs).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("create_is_blocked_while_run_or_delete_busy", async () => {
|
||||
const onBusyChange = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const onJobs = vi.fn();
|
||||
const buildInput = vi.fn(() => createInput());
|
||||
const createCronJob = vi.fn(async () => createJob("created", "agent-1", 15));
|
||||
const listCronJobs = vi.fn(async () => ({ jobs: [] }));
|
||||
|
||||
await expect(
|
||||
performCronCreateFlow({
|
||||
client: {} as never,
|
||||
agentId: "agent-1",
|
||||
draft: createDraft(),
|
||||
busy: { createBusy: false, runBusyJobId: "job-1", deleteBusyJobId: null },
|
||||
onBusyChange,
|
||||
onError,
|
||||
onJobs,
|
||||
deps: { buildInput, createCronJob, listCronJobs },
|
||||
})
|
||||
).rejects.toThrow("Please wait for the current cron action to finish.");
|
||||
|
||||
expect(onError).toHaveBeenCalledWith("Please wait for the current cron action to finish.");
|
||||
expect(onBusyChange).not.toHaveBeenCalled();
|
||||
expect(onJobs).not.toHaveBeenCalled();
|
||||
expect(buildInput).not.toHaveBeenCalled();
|
||||
expect(createCronJob).not.toHaveBeenCalled();
|
||||
expect(listCronJobs).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("fails_fast_when_agent_id_missing", async () => {
|
||||
const onBusyChange = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const onJobs = vi.fn();
|
||||
const buildInput = vi.fn(() => createInput());
|
||||
const createCronJob = vi.fn(async () => createJob("created", "agent-1", 15));
|
||||
const listCronJobs = vi.fn(async () => ({ jobs: [] }));
|
||||
|
||||
await expect(
|
||||
performCronCreateFlow({
|
||||
client: {} as never,
|
||||
agentId: " ",
|
||||
draft: createDraft(),
|
||||
busy: { createBusy: false, runBusyJobId: null, deleteBusyJobId: null },
|
||||
onBusyChange,
|
||||
onError,
|
||||
onJobs,
|
||||
deps: { buildInput, createCronJob, listCronJobs },
|
||||
})
|
||||
).rejects.toThrow("Failed to create cron job: missing agent id.");
|
||||
|
||||
expect(onError).toHaveBeenCalledWith("Failed to create cron job: missing agent id.");
|
||||
expect(onBusyChange).not.toHaveBeenCalled();
|
||||
expect(onJobs).not.toHaveBeenCalled();
|
||||
expect(buildInput).not.toHaveBeenCalled();
|
||||
expect(createCronJob).not.toHaveBeenCalled();
|
||||
expect(listCronJobs).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user