First Release of Claw3D (#11)

Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Luke The Dev
2026-03-19 23:14:04 -05:00
committed by GitHub
parent 5ea96b2650
commit 4fa4f13558
431 changed files with 105438 additions and 14 deletions
+173
View File
@@ -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();
});
});