Skip to content

Commit 48683a7

Browse files
committed
ci: split auto-reply reply routing shard
1 parent af548bb commit 48683a7

4 files changed

Lines changed: 81 additions & 45 deletions

File tree

scripts/lib/ci-node-test-plan.mjs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,7 @@ function createAutoReplyReplySplitShards() {
6767
}
6868
}
6969

70-
const mergedGroups = {
71-
"auto-reply-reply-agent-runner": groups["auto-reply-reply-agent-runner"],
72-
"auto-reply-reply-dispatch": groups["auto-reply-reply-dispatch"],
73-
"auto-reply-reply-commands-state-routing": [
74-
...groups["auto-reply-reply-commands"],
75-
...groups["auto-reply-reply-state-routing"],
76-
],
77-
};
78-
79-
return Object.entries(mergedGroups)
70+
return Object.entries(groups)
8071
.map(([groupName, includePatterns]) => ({
8172
configs: ["test/vitest/vitest.auto-reply-reply.config.ts"],
8273
includePatterns,

src/gateway/server.agent.gateway-server-agent-a.test.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from "node:fs/promises";
22
import os from "node:os";
33
import path from "node:path";
4-
import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
4+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
55
import type { ChannelPlugin } from "../channels/plugins/types.js";
66
import { createChannelTestPluginBase } from "../test-utils/channel-plugins.js";
77
import { setRegistry } from "./server.agent.gateway-server-agent.mocks.js";
@@ -61,9 +61,18 @@ async function setTestSessionStore(params: {
6161
});
6262
}
6363

64-
function latestAgentCall(): AgentCommandCall {
64+
async function waitForAgentCall(runId: string): Promise<AgentCommandCall> {
65+
await vi.waitFor(() =>
66+
expect(
67+
(vi.mocked(agentCommand).mock.calls as unknown as Array<[AgentCommandCall]>).some(
68+
([call]) => call.runId === runId,
69+
),
70+
).toBe(true),
71+
);
6572
const calls = vi.mocked(agentCommand).mock.calls as unknown as Array<[unknown]>;
66-
return calls.at(-1)?.[0] as AgentCommandCall;
73+
return calls.find(
74+
([call]) => (call as AgentCommandCall).runId === runId,
75+
)?.[0] as AgentCommandCall;
6776
}
6877

6978
async function runMainAgentDeliveryWithSession(params: {
@@ -89,7 +98,7 @@ async function runMainAgentDeliveryWithSession(params: {
8998
...params.request,
9099
});
91100
expect(res.ok).toBe(true);
92-
return latestAgentCall();
101+
return await waitForAgentCall(String(params.request.idempotencyKey));
93102
} finally {
94103
testState.allowFrom = undefined;
95104
}
@@ -160,8 +169,19 @@ const defaultRegistry = createRegistry([
160169
]);
161170

162171
describe("gateway server agent", () => {
163-
test("agent marks implicit delivery when lastTo is stale", async () => {
172+
beforeEach(() => {
173+
vi.mocked(agentCommand).mockClear();
174+
testState.agentsConfig = undefined;
175+
testState.allowFrom = undefined;
164176
setRegistry(defaultRegistry);
177+
});
178+
179+
afterEach(() => {
180+
testState.agentsConfig = undefined;
181+
testState.allowFrom = undefined;
182+
});
183+
184+
test("agent marks implicit delivery when lastTo is stale", async () => {
165185
testState.allowFrom = ["+436769770569"];
166186
await setTestSessionStore({
167187
entries: {
@@ -182,7 +202,7 @@ describe("gateway server agent", () => {
182202
});
183203
expect(res.ok).toBe(true);
184204

185-
const call = latestAgentCall();
205+
const call = await waitForAgentCall("idem-agent-last-stale");
186206
expectChannels(call, "whatsapp");
187207
expect(call.to).toBe("+1555");
188208
expect(call.deliveryTargetMode).toBe("implicit");
@@ -191,7 +211,6 @@ describe("gateway server agent", () => {
191211
});
192212

193213
test("agent forwards sessionKey to agentCommand", async () => {
194-
setRegistry(defaultRegistry);
195214
await setTestSessionStore({
196215
entries: {
197216
"agent:main:subagent:abc": {
@@ -207,7 +226,7 @@ describe("gateway server agent", () => {
207226
});
208227
expect(res.ok).toBe(true);
209228

210-
const call = latestAgentCall();
229+
const call = await waitForAgentCall("idem-agent-subkey");
211230
expect(call.sessionKey).toBe("agent:main:subagent:abc");
212231
expect(call.sessionId).toBe("sess-sub");
213232
expectChannels(call, "webchat");
@@ -216,7 +235,6 @@ describe("gateway server agent", () => {
216235
});
217236

218237
test("agent preserves spawnDepth on subagent sessions", async () => {
219-
setRegistry(defaultRegistry);
220238
await setTestSessionStore({
221239
entries: {
222240
"agent:main:subagent:depth": {
@@ -245,7 +263,6 @@ describe("gateway server agent", () => {
245263
});
246264

247265
test("agent derives sessionKey from agentId", async () => {
248-
setRegistry(defaultRegistry);
249266
await setTestSessionStore({
250267
agentId: "ops",
251268
entries: {
@@ -263,13 +280,12 @@ describe("gateway server agent", () => {
263280
});
264281
expect(res.ok).toBe(true);
265282

266-
const call = latestAgentCall();
283+
const call = await waitForAgentCall("idem-agent-id");
267284
expect(call.sessionKey).toBe("agent:ops:main");
268285
expect(call.sessionId).toBe("sess-ops");
269286
});
270287

271288
test("agent rejects unknown reply channel", async () => {
272-
setRegistry(defaultRegistry);
273289
const res = await rpcReq(ws, "agent", {
274290
message: "hi",
275291
replyChannel: "unknown-channel",
@@ -283,7 +299,6 @@ describe("gateway server agent", () => {
283299
});
284300

285301
test("agent rejects mismatched agentId and sessionKey", async () => {
286-
setRegistry(defaultRegistry);
287302
testState.agentsConfig = { list: [{ id: "ops" }] };
288303
const res = await rpcReq(ws, "agent", {
289304
message: "hi",
@@ -299,7 +314,6 @@ describe("gateway server agent", () => {
299314
});
300315

301316
test("agent rejects malformed agent-prefixed session keys", async () => {
302-
setRegistry(defaultRegistry);
303317
const res = await rpcReq(ws, "agent", {
304318
message: "hi",
305319
sessionKey: "agent:main",
@@ -391,7 +405,6 @@ describe("gateway server agent", () => {
391405
});
392406

393407
test("agent forwards image attachments as images[]", async () => {
394-
setRegistry(defaultRegistry);
395408
await setTestSessionStore({
396409
entries: {
397410
main: {
@@ -414,7 +427,7 @@ describe("gateway server agent", () => {
414427
});
415428
expect(res.ok).toBe(true);
416429

417-
const call = latestAgentCall();
430+
const call = await waitForAgentCall("idem-agent-attachments");
418431
expect(call.sessionKey).toBe("agent:main:main");
419432
expectChannels(call, "webchat");
420433
expect(typeof call.message).toBe("string");
@@ -429,7 +442,6 @@ describe("gateway server agent", () => {
429442
});
430443

431444
test("agent errors when delivery requested and no last channel exists", async () => {
432-
setRegistry(defaultRegistry);
433445
testState.allowFrom = ["+1555"];
434446
try {
435447
await setTestSessionStore({
@@ -493,7 +505,6 @@ describe("gateway server agent", () => {
493505
idempotencyKey: "idem-agent-last-signal",
494506
},
495507
])("agent routes main last-channel $name", async (tc) => {
496-
setRegistry(defaultRegistry);
497508
await setTestSessionStore({
498509
entries: {
499510
main: {
@@ -513,7 +524,7 @@ describe("gateway server agent", () => {
513524
});
514525
expect(res.ok).toBe(true);
515526

516-
const call = latestAgentCall();
527+
const call = await waitForAgentCall(tc.idempotencyKey);
517528
expectChannels(call, tc.lastChannel);
518529
expect(call.to).toBe(tc.lastTo);
519530
expect(call.deliver).toBe(true);

src/gateway/server.agent.gateway-server-agent-b.test.ts

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,30 @@ function expectChannels(call: Record<string, unknown>, channel: string) {
111111
expect(call.messageChannel).toBe(channel);
112112
}
113113

114-
function readAgentCommandCall(fromEnd = 1) {
114+
async function readAgentCommandCall(params: { runId?: string; fromEnd?: number } = {}) {
115+
if (params.runId) {
116+
await vi.waitFor(() =>
117+
expect(
118+
(vi.mocked(agentCommand).mock.calls as unknown as Array<[Record<string, unknown>]>).some(
119+
([call]) => call.runId === params.runId,
120+
),
121+
).toBe(true),
122+
);
123+
const calls = vi.mocked(agentCommand).mock.calls as unknown as Array<[Record<string, unknown>]>;
124+
return calls.find(([call]) => call.runId === params.runId)?.[0] ?? {};
125+
}
115126
const calls = vi.mocked(agentCommand).mock.calls;
116-
return (calls.at(-fromEnd)?.[0] ?? {}) as Record<string, unknown>;
127+
return (calls.at(-(params.fromEnd ?? 1))?.[0] ?? {}) as Record<string, unknown>;
117128
}
118129

119-
function expectAgentRoutingCall(params: {
130+
async function expectAgentRoutingCall(params: {
120131
channel: string;
121132
deliver: boolean;
122133
to?: string;
123134
fromEnd?: number;
135+
runId?: string;
124136
}) {
125-
const call = readAgentCommandCall(params.fromEnd);
137+
const call = await readAgentCommandCall({ runId: params.runId, fromEnd: params.fromEnd });
126138
expectChannels(call, params.channel);
127139
if ("to" in params) {
128140
expect(call.to).toBe(params.to);
@@ -186,10 +198,13 @@ async function useTempSessionStorePath() {
186198

187199
describe("gateway server agent", () => {
188200
beforeEach(() => {
201+
vi.mocked(agentCommand).mockClear();
202+
testState.allowFrom = undefined;
189203
setRegistry(defaultRegistry);
190204
});
191205

192206
afterEach(() => {
207+
testState.allowFrom = undefined;
193208
setRegistry(emptyRegistry);
194209
});
195210

@@ -215,11 +230,11 @@ describe("gateway server agent", () => {
215230
idempotencyKey: "idem-agent-last-msteams",
216231
});
217232
expect(res.ok).toBe(true);
218-
expectAgentRoutingCall({
233+
await expectAgentRoutingCall({
219234
channel: "msteams",
220235
deliver: true,
221236
to: "conversation:teams-123",
222-
fromEnd: 1,
237+
runId: "idem-agent-last-msteams",
223238
});
224239
});
225240

@@ -308,10 +323,11 @@ describe("gateway server agent", () => {
308323
idempotencyKey: "idem-agent-imsg",
309324
});
310325
expect(resIMessage.ok).toBe(true);
311-
await vi.waitFor(() => {
312-
expect(vi.mocked(agentCommand)).toHaveBeenCalled();
326+
await expectAgentRoutingCall({
327+
channel: "imessage",
328+
deliver: true,
329+
runId: "idem-agent-imsg",
313330
});
314-
expectAgentRoutingCall({ channel: "imessage", deliver: true, fromEnd: 1 });
315331
});
316332

317333
test("agent accepts plugin channel alias (teams)", async () => {
@@ -333,11 +349,11 @@ describe("gateway server agent", () => {
333349
idempotencyKey: "idem-agent-teams",
334350
});
335351
expect(resTeams.ok).toBe(true);
336-
expectAgentRoutingCall({
352+
await expectAgentRoutingCall({
337353
channel: "msteams",
338354
deliver: false,
339355
to: "conversation:teams-abc",
340-
fromEnd: 1,
356+
runId: "idem-agent-teams",
341357
});
342358
});
343359

@@ -389,7 +405,11 @@ describe("gateway server agent", () => {
389405
idempotencyKey: "idem-agent-webchat-best-effort",
390406
});
391407
expect(res.ok).toBe(true);
392-
expectAgentRoutingCall({ channel: "webchat", deliver: false });
408+
await expectAgentRoutingCall({
409+
channel: "webchat",
410+
deliver: false,
411+
runId: "idem-agent-webchat-best-effort",
412+
});
393413
});
394414

395415
test("agent downgrades to session-only when multiple channels are configured but no external target resolves", async () => {
@@ -417,7 +437,11 @@ describe("gateway server agent", () => {
417437
idempotencyKey: "idem-agent-multi-configured-best-effort",
418438
});
419439
expect(res.ok).toBe(true);
420-
expectAgentRoutingCall({ channel: "webchat", deliver: false });
440+
await expectAgentRoutingCall({
441+
channel: "webchat",
442+
deliver: false,
443+
runId: "idem-agent-multi-configured-best-effort",
444+
});
421445
});
422446

423447
test("agent uses webchat for internal runs when last provider is webchat", async () => {
@@ -435,7 +459,11 @@ describe("gateway server agent", () => {
435459
});
436460
expect(res.ok).toBe(true);
437461

438-
expectAgentRoutingCall({ channel: "webchat", deliver: false });
462+
await expectAgentRoutingCall({
463+
channel: "webchat",
464+
deliver: false,
465+
runId: "idem-agent-webchat-internal",
466+
});
439467
});
440468

441469
test("write-scoped callers cannot reset conversations via agent", async () => {

test/scripts/ci-node-test-plan.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,17 +328,23 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
328328
requiresDist: false,
329329
shardName: "auto-reply-reply-agent-runner",
330330
},
331+
{
332+
checkName: "checks-node-auto-reply-reply-commands",
333+
configs: ["test/vitest/vitest.auto-reply-reply.config.ts"],
334+
requiresDist: false,
335+
shardName: "auto-reply-reply-commands",
336+
},
331337
{
332338
checkName: "checks-node-auto-reply-reply-dispatch",
333339
configs: ["test/vitest/vitest.auto-reply-reply.config.ts"],
334340
requiresDist: false,
335341
shardName: "auto-reply-reply-dispatch",
336342
},
337343
{
338-
checkName: "checks-node-auto-reply-reply-commands-state-routing",
344+
checkName: "checks-node-auto-reply-reply-state-routing",
339345
configs: ["test/vitest/vitest.auto-reply-reply.config.ts"],
340346
requiresDist: false,
341-
shardName: "auto-reply-reply-commands-state-routing",
347+
shardName: "auto-reply-reply-state-routing",
342348
},
343349
]);
344350
});

0 commit comments

Comments
 (0)