All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
246 lines
6.8 KiB
TypeScript
246 lines
6.8 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
import { AgentTreeService } from "./agent-tree.service";
|
|
import { PrismaService } from "../../prisma/prisma.service";
|
|
|
|
describe("AgentTreeService", () => {
|
|
let service: AgentTreeService;
|
|
let prisma: {
|
|
agentSessionTree: {
|
|
findMany: ReturnType<typeof vi.fn>;
|
|
count: ReturnType<typeof vi.fn>;
|
|
findUnique: ReturnType<typeof vi.fn>;
|
|
};
|
|
};
|
|
|
|
beforeEach(() => {
|
|
prisma = {
|
|
agentSessionTree: {
|
|
findMany: vi.fn(),
|
|
count: vi.fn(),
|
|
findUnique: vi.fn(),
|
|
},
|
|
};
|
|
|
|
service = new AgentTreeService(prisma as unknown as PrismaService);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("listSessions", () => {
|
|
it("returns paginated sessions and cursor", async () => {
|
|
const sessions = [
|
|
{
|
|
id: "tree-2",
|
|
sessionId: "agent-2",
|
|
parentSessionId: null,
|
|
provider: "internal",
|
|
missionId: null,
|
|
taskId: "task-2",
|
|
taskSource: "queue",
|
|
agentType: "worker",
|
|
status: "running",
|
|
spawnedAt: new Date("2026-03-07T11:00:00.000Z"),
|
|
completedAt: null,
|
|
metadata: {},
|
|
},
|
|
{
|
|
id: "tree-1",
|
|
sessionId: "agent-1",
|
|
parentSessionId: null,
|
|
provider: "internal",
|
|
missionId: null,
|
|
taskId: "task-1",
|
|
taskSource: "queue",
|
|
agentType: "worker",
|
|
status: "running",
|
|
spawnedAt: new Date("2026-03-07T10:00:00.000Z"),
|
|
completedAt: null,
|
|
metadata: {},
|
|
},
|
|
];
|
|
|
|
prisma.agentSessionTree.findMany.mockResolvedValue(sessions);
|
|
prisma.agentSessionTree.count.mockResolvedValue(7);
|
|
|
|
const result = await service.listSessions(undefined, 2);
|
|
|
|
expect(prisma.agentSessionTree.findMany).toHaveBeenCalledWith({
|
|
where: undefined,
|
|
orderBy: [{ spawnedAt: "desc" }, { sessionId: "desc" }],
|
|
take: 2,
|
|
});
|
|
expect(prisma.agentSessionTree.count).toHaveBeenCalledWith();
|
|
expect(result.sessions).toEqual(sessions);
|
|
expect(result.total).toBe(7);
|
|
expect(result.cursor).toBeTypeOf("string");
|
|
});
|
|
|
|
it("applies cursor filter when provided", async () => {
|
|
prisma.agentSessionTree.findMany.mockResolvedValue([]);
|
|
prisma.agentSessionTree.count.mockResolvedValue(0);
|
|
|
|
const cursorDate = "2026-03-07T10:00:00.000Z";
|
|
const cursorSessionId = "agent-5";
|
|
const cursor = Buffer.from(
|
|
JSON.stringify({
|
|
spawnedAt: cursorDate,
|
|
sessionId: cursorSessionId,
|
|
}),
|
|
"utf8"
|
|
).toString("base64url");
|
|
|
|
await service.listSessions(cursor, 25);
|
|
|
|
expect(prisma.agentSessionTree.findMany).toHaveBeenCalledWith({
|
|
where: {
|
|
OR: [
|
|
{
|
|
spawnedAt: {
|
|
lt: new Date(cursorDate),
|
|
},
|
|
},
|
|
{
|
|
spawnedAt: new Date(cursorDate),
|
|
sessionId: {
|
|
lt: cursorSessionId,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
orderBy: [{ spawnedAt: "desc" }, { sessionId: "desc" }],
|
|
take: 25,
|
|
});
|
|
});
|
|
|
|
it("ignores invalid cursor values", async () => {
|
|
prisma.agentSessionTree.findMany.mockResolvedValue([]);
|
|
prisma.agentSessionTree.count.mockResolvedValue(0);
|
|
|
|
await service.listSessions("invalid-cursor", 10);
|
|
|
|
expect(prisma.agentSessionTree.findMany).toHaveBeenCalledWith({
|
|
where: undefined,
|
|
orderBy: [{ spawnedAt: "desc" }, { sessionId: "desc" }],
|
|
take: 10,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("getSession", () => {
|
|
it("returns matching session entry", async () => {
|
|
const session = {
|
|
id: "tree-1",
|
|
sessionId: "agent-123",
|
|
parentSessionId: null,
|
|
provider: "internal",
|
|
missionId: null,
|
|
taskId: "task-1",
|
|
taskSource: "queue",
|
|
agentType: "worker",
|
|
status: "running",
|
|
spawnedAt: new Date("2026-03-07T11:00:00.000Z"),
|
|
completedAt: null,
|
|
metadata: {},
|
|
};
|
|
prisma.agentSessionTree.findUnique.mockResolvedValue(session);
|
|
|
|
const result = await service.getSession("agent-123");
|
|
|
|
expect(prisma.agentSessionTree.findUnique).toHaveBeenCalledWith({
|
|
where: { sessionId: "agent-123" },
|
|
});
|
|
expect(result).toEqual(session);
|
|
});
|
|
|
|
it("returns null when session does not exist", async () => {
|
|
prisma.agentSessionTree.findUnique.mockResolvedValue(null);
|
|
|
|
const result = await service.getSession("agent-missing");
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("getTree", () => {
|
|
it("returns mapped entries from Prisma", async () => {
|
|
prisma.agentSessionTree.findMany.mockResolvedValue([
|
|
{
|
|
id: "tree-1",
|
|
sessionId: "agent-1",
|
|
parentSessionId: "agent-root",
|
|
provider: "internal",
|
|
missionId: "mission-1",
|
|
taskId: "task-1",
|
|
taskSource: "queue",
|
|
agentType: "worker",
|
|
status: "running",
|
|
spawnedAt: new Date("2026-03-07T10:00:00.000Z"),
|
|
completedAt: new Date("2026-03-07T11:00:00.000Z"),
|
|
metadata: {},
|
|
},
|
|
]);
|
|
|
|
const result = await service.getTree();
|
|
|
|
expect(prisma.agentSessionTree.findMany).toHaveBeenCalledWith({
|
|
orderBy: { spawnedAt: "desc" },
|
|
take: 200,
|
|
});
|
|
expect(result).toEqual([
|
|
{
|
|
sessionId: "agent-1",
|
|
parentSessionId: "agent-root",
|
|
status: "running",
|
|
agentType: "worker",
|
|
taskSource: "queue",
|
|
spawnedAt: "2026-03-07T10:00:00.000Z",
|
|
completedAt: "2026-03-07T11:00:00.000Z",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("returns empty array when no entries exist", async () => {
|
|
prisma.agentSessionTree.findMany.mockResolvedValue([]);
|
|
|
|
const result = await service.getTree();
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it("maps null parentSessionId and completedAt correctly", async () => {
|
|
prisma.agentSessionTree.findMany.mockResolvedValue([
|
|
{
|
|
id: "tree-2",
|
|
sessionId: "agent-root",
|
|
parentSessionId: null,
|
|
provider: "internal",
|
|
missionId: null,
|
|
taskId: null,
|
|
taskSource: null,
|
|
agentType: null,
|
|
status: "spawning",
|
|
spawnedAt: new Date("2026-03-07T09:00:00.000Z"),
|
|
completedAt: null,
|
|
metadata: {},
|
|
},
|
|
]);
|
|
|
|
const result = await service.getTree();
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
sessionId: "agent-root",
|
|
parentSessionId: null,
|
|
status: "spawning",
|
|
agentType: null,
|
|
taskSource: null,
|
|
spawnedAt: "2026-03-07T09:00:00.000Z",
|
|
completedAt: null,
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
});
|