Skip to content

Commit 8d3fe21

Browse files
authored
test(tasks): cover task domain view mappers (#86755)
Adds focused coverage for task-domain view mapper DTO contracts, including summary cloning, task run/detail mapping, flow view/detail mapping, and implicit summary computation. Test-only PR. Verified with git diff --check and PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false pnpm test src/tasks/task-domain-views.test.ts on the current-main merge result. Thanks @leno23. Co-authored-by: wuyangfan <yangfan.wu@succaiss.com>
1 parent becd453 commit 8d3fe21

1 file changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import type { TaskFlowRecord } from "./task-flow-registry.types.js";
4+
import type { TaskRecord, TaskRegistrySummary } from "./task-registry.types.js";
5+
6+
import {
7+
mapTaskFlowDetail,
8+
mapTaskFlowView,
9+
mapTaskRunAggregateSummary,
10+
mapTaskRunDetail,
11+
mapTaskRunView,
12+
} from "./task-domain-views.js";
13+
14+
function makeTask(overrides: Partial<TaskRecord> = {}): TaskRecord {
15+
return {
16+
taskId: "task-1",
17+
runtime: "subagent",
18+
requesterSessionKey: "agent:main:main",
19+
ownerKey: "agent:main:main",
20+
scopeKind: "session",
21+
task: "Investigate flaky delivery",
22+
status: "running",
23+
deliveryStatus: "pending",
24+
notifyPolicy: "done_only",
25+
createdAt: 100,
26+
...overrides,
27+
};
28+
}
29+
30+
function makeFlow(overrides: Partial<TaskFlowRecord> = {}): TaskFlowRecord {
31+
return {
32+
flowId: "flow-1",
33+
syncMode: "managed",
34+
ownerKey: "agent:main:main",
35+
revision: 3,
36+
status: "waiting",
37+
notifyPolicy: "state_changes",
38+
goal: "Ship a safe fix",
39+
createdAt: 10,
40+
updatedAt: 20,
41+
...overrides,
42+
};
43+
}
44+
45+
function makeSummary(overrides: Partial<TaskRegistrySummary> = {}): TaskRegistrySummary {
46+
return {
47+
total: 2,
48+
active: 1,
49+
terminal: 1,
50+
failures: 1,
51+
byStatus: {
52+
queued: 0,
53+
running: 1,
54+
succeeded: 0,
55+
failed: 1,
56+
timed_out: 0,
57+
cancelled: 0,
58+
lost: 0,
59+
},
60+
byRuntime: {
61+
subagent: 1,
62+
acp: 0,
63+
cli: 1,
64+
cron: 0,
65+
},
66+
...overrides,
67+
};
68+
}
69+
70+
describe("task domain view mappers", () => {
71+
it("maps task registry summaries without sharing mutable count objects", () => {
72+
const summary = makeSummary();
73+
74+
const view = mapTaskRunAggregateSummary(summary);
75+
76+
expect(view).toEqual(summary);
77+
expect(view.byStatus).not.toBe(summary.byStatus);
78+
expect(view.byRuntime).not.toBe(summary.byRuntime);
79+
});
80+
81+
it("maps task run records to the public task run view contract", () => {
82+
const task = makeTask({
83+
taskId: "task-full",
84+
runtime: "cli",
85+
sourceId: "source-1",
86+
requesterSessionKey: "agent:main:telegram:chat-1",
87+
ownerKey: "agent:main:main",
88+
scopeKind: "system",
89+
childSessionKey: "agent:main:subagent:child-1",
90+
parentFlowId: "flow-1",
91+
parentTaskId: "task-parent",
92+
agentId: "main",
93+
runId: "run-1",
94+
label: "diagnostics",
95+
task: "Run diagnostics",
96+
status: "failed",
97+
deliveryStatus: "failed",
98+
notifyPolicy: "state_changes",
99+
createdAt: 100,
100+
startedAt: 110,
101+
endedAt: 200,
102+
lastEventAt: 190,
103+
cleanupAfter: 1_000,
104+
error: "Command failed",
105+
progressSummary: "Checking logs",
106+
terminalSummary: "Diagnostics failed",
107+
terminalOutcome: "blocked",
108+
});
109+
110+
expect(mapTaskRunView(task)).toEqual({
111+
id: "task-full",
112+
runtime: "cli",
113+
sourceId: "source-1",
114+
sessionKey: "agent:main:telegram:chat-1",
115+
ownerKey: "agent:main:main",
116+
scope: "system",
117+
childSessionKey: "agent:main:subagent:child-1",
118+
flowId: "flow-1",
119+
parentTaskId: "task-parent",
120+
agentId: "main",
121+
runId: "run-1",
122+
label: "diagnostics",
123+
title: "Run diagnostics",
124+
status: "failed",
125+
deliveryStatus: "failed",
126+
notifyPolicy: "state_changes",
127+
createdAt: 100,
128+
startedAt: 110,
129+
endedAt: 200,
130+
lastEventAt: 190,
131+
cleanupAfter: 1_000,
132+
error: "Command failed",
133+
progressSummary: "Checking logs",
134+
terminalSummary: "Diagnostics failed",
135+
terminalOutcome: "blocked",
136+
});
137+
});
138+
139+
it("keeps task run detail aligned with the task run view shape", () => {
140+
const task = makeTask({ taskId: "task-detail", runId: "run-detail" });
141+
142+
expect(mapTaskRunDetail(task)).toEqual(mapTaskRunView(task));
143+
});
144+
145+
it("maps task flow records to public flow views without sharing requester origins", () => {
146+
const requesterOrigin = {
147+
channel: "telegram",
148+
to: "chat-1",
149+
threadId: 123,
150+
deliveryIntent: { id: "intent-1", kind: "outbound_queue" as const },
151+
};
152+
const flow = makeFlow({
153+
requesterOrigin,
154+
currentStep: "wait_for_task",
155+
cancelRequestedAt: 18,
156+
endedAt: 30,
157+
});
158+
159+
const view = mapTaskFlowView(flow);
160+
161+
expect(view).toEqual({
162+
id: "flow-1",
163+
ownerKey: "agent:main:main",
164+
requesterOrigin,
165+
status: "waiting",
166+
notifyPolicy: "state_changes",
167+
goal: "Ship a safe fix",
168+
currentStep: "wait_for_task",
169+
cancelRequestedAt: 18,
170+
createdAt: 10,
171+
updatedAt: 20,
172+
endedAt: 30,
173+
});
174+
expect(view.requesterOrigin).not.toBe(requesterOrigin);
175+
});
176+
177+
it("maps flow details with supplied task summary and nested task views", () => {
178+
const task = makeTask({ taskId: "task-child", parentFlowId: "flow-1" });
179+
const summary = makeSummary();
180+
const flow = makeFlow({
181+
stateJson: { phase: "waiting" },
182+
waitJson: { kind: "task", taskId: "task-child" },
183+
blockedTaskId: "task-child",
184+
blockedSummary: "Waiting for child task",
185+
});
186+
187+
const detail = mapTaskFlowDetail({ flow, tasks: [task], summary });
188+
189+
expect(detail).toEqual({
190+
...mapTaskFlowView(flow),
191+
state: { phase: "waiting" },
192+
wait: { kind: "task", taskId: "task-child" },
193+
blocked: {
194+
taskId: "task-child",
195+
summary: "Waiting for child task",
196+
},
197+
tasks: [mapTaskRunView(task)],
198+
taskSummary: mapTaskRunAggregateSummary(summary),
199+
});
200+
expect(detail.taskSummary.byStatus).not.toBe(summary.byStatus);
201+
expect(detail.taskSummary.byRuntime).not.toBe(summary.byRuntime);
202+
});
203+
204+
it("summarizes nested tasks when flow detail callers do not provide a summary", () => {
205+
const running = makeTask({ taskId: "task-running", status: "running", runtime: "subagent" });
206+
const failed = makeTask({ taskId: "task-failed", status: "failed", runtime: "cli" });
207+
208+
const detail = mapTaskFlowDetail({ flow: makeFlow(), tasks: [running, failed] });
209+
210+
expect(detail.taskSummary).toEqual(makeSummary());
211+
});
212+
});

0 commit comments

Comments
 (0)