Files
2026-07-13 13:39:12 +08:00

87 lines
2.5 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { A2ATaskManager } from "../../src/lib/a2a/taskManager.ts";
import { executeA2ATaskWithState } from "../../src/lib/a2a/taskExecution.ts";
const managers = [];
function createManager(ttlMinutes = 5) {
const manager = new A2ATaskManager(ttlMinutes);
managers.push(manager);
return manager;
}
test.afterEach(() => {
while (managers.length > 0) {
managers.pop()?.destroy();
}
});
test("completed task remains completed when queried after expiration", () => {
const tm = createManager();
const task = tm.createTask({
skill: "smart-routing",
messages: [{ role: "user", content: "hello" }],
});
tm.updateTask(task.id, "working");
tm.updateTask(task.id, "completed", [{ type: "text", content: "done" }]);
task.expiresAt = new Date(Date.now() - 1_000).toISOString();
assert.doesNotThrow(() => tm.getTask(task.id));
const loaded = tm.getTask(task.id);
assert.equal(loaded?.state, "completed");
});
test("stream execution marks task as failed when handler throws", async () => {
const tm = createManager();
const task = tm.createTask({
skill: "smart-routing",
messages: [{ role: "user", content: "trigger error" }],
});
tm.updateTask(task.id, "working");
await assert.rejects(
() =>
executeA2ATaskWithState(tm, task, async () => {
throw new Error("upstream failure");
}),
/upstream failure/
);
const loaded = tm.getTask(task.id);
assert.equal(loaded?.state, "failed");
assert.deepEqual(loaded?.artifacts.at(-1), { type: "error", content: "upstream failure" });
});
test("expired submitted task transitions to failed without throwing", () => {
const tm = createManager();
const task = tm.createTask({
skill: "smart-routing",
messages: [{ role: "user", content: "hello" }],
});
task.expiresAt = new Date(Date.now() - 1_000).toISOString();
assert.doesNotThrow(() => tm.getTask(task.id));
const loaded = tm.getTask(task.id);
assert.equal(loaded?.state, "failed");
});
test("cleanup keeps cancelled tasks as cancelled", () => {
const tm = createManager();
const task = tm.createTask({
skill: "smart-routing",
messages: [{ role: "user", content: "cancel me" }],
});
tm.updateTask(task.id, "cancelled");
task.expiresAt = new Date(Date.now() - 1_000).toISOString();
// private in TS only; callable at runtime for regression test
tm.cleanupExpired();
const loaded = tm.getTask(task.id);
assert.equal(loaded?.state, "cancelled");
});