























@@ -182,6 +182,28 @@ function buildCtx(): NodeEventContext {
182182};
183183}
184184185+function expectFields(value: unknown, expected: Record<string, unknown>): void {
186+expect(value).toBeTypeOf("object");
187+expect(value).not.toBeNull();
188+const record = value as Record<string, unknown>;
189+for (const [key, expectedValue] of Object.entries(expected)) {
190+expect(record[key], key).toEqual(expectedValue);
191+}
192+}
193+194+function expectPresencePersistCall(
195+mock: ReturnType<typeof vi.fn>,
196+deviceId: string,
197+reason: string,
198+): void {
199+expect(mock).toHaveBeenCalledTimes(1);
200+const [actualDeviceId, metadata] = mock.mock.calls[0] ?? [];
201+expect(actualDeviceId).toBe(deviceId);
202+expectFields(metadata, { lastSeenReason: reason });
203+const lastSeenAtMs = (metadata as { lastSeenAtMs?: unknown } | undefined)?.lastSeenAtMs;
204+expect(typeof lastSeenAtMs).toBe("number");
205+}
206+185207describe("node exec events", () => {
186208beforeEach(() => {
187209resetNodeEventDeduplicationForTests();
@@ -588,22 +610,25 @@ describe("voice transcript events", () => {
588610589611expect(agentCommandMock).toHaveBeenCalledTimes(1);
590612const [opts] = agentCommandMock.mock.calls[0] ?? [];
591-expect(opts).toMatchObject({
613+expectFields(opts, {
592614message: "check provenance",
593615deliver: false,
594616messageChannel: "node",
595-inputProvenance: {
596-kind: "external_user",
597-sourceChannel: "voice",
598-sourceTool: "gateway.voice.transcript",
599-},
600617});
601-expect(typeof opts.runId).toBe("string");
602-expect(opts.runId).not.toBe(opts.sessionId);
603-expect(addChatRun).toHaveBeenCalledWith(
604-opts.runId,
605-expect.objectContaining({ clientRunId: expect.stringMatching(/^voice-/) }),
606-);
618+const optsRecord = opts as Record<string, unknown>;
619+expectFields(optsRecord.inputProvenance, {
620+kind: "external_user",
621+sourceChannel: "voice",
622+sourceTool: "gateway.voice.transcript",
623+});
624+expect(typeof optsRecord.runId).toBe("string");
625+expect(optsRecord.runId).not.toBe(optsRecord.sessionId);
626+expect(addChatRun).toHaveBeenCalledTimes(1);
627+const [runId, runMetadata] = addChatRun.mock.calls[0] ?? [];
628+expect(runId).toBe(optsRecord.runId);
629+const clientRunId = (runMetadata as { clientRunId?: unknown } | undefined)?.clientRunId;
630+expect(typeof clientRunId).toBe("string");
631+expect(clientRunId).toMatch(/^voice-/);
607632});
608633609634it("does not block agent dispatch when session-store touch fails", async () => {
@@ -622,7 +647,8 @@ describe("voice transcript events", () => {
622647await Promise.resolve();
623648624649expect(agentCommandMock).toHaveBeenCalledTimes(1);
625-expect(warn).toHaveBeenCalledWith(expect.stringContaining("voice session-store update failed"));
650+expect(warn).toHaveBeenCalledTimes(1);
651+expect(String(warn.mock.calls[0]?.[0])).toContain("voice session-store update failed");
626652});
627653628654it("preserves existing session metadata when touching the store for voice transcripts", async () => {
@@ -669,17 +695,15 @@ describe("voice transcript events", () => {
669695});
670696await Promise.resolve();
671697672-expect(updatedStore).toMatchObject({
673-"voice-preserve-session": {
674-sessionId: "sess-preserve",
675-label: "existing label",
676-spawnedBy: "agent:main:parent",
677-parentSessionKey: "agent:main:parent",
678-lastChannel: "discord",
679-lastTo: "thread-1",
680-lastAccountId: "acct-1",
681-lastThreadId: 42,
682-},
698+expectFields(updatedStore?.["voice-preserve-session"], {
699+sessionId: "sess-preserve",
700+label: "existing label",
701+spawnedBy: "agent:main:parent",
702+parentSessionKey: "agent:main:parent",
703+lastChannel: "discord",
704+lastTo: "thread-1",
705+lastAccountId: "acct-1",
706+lastThreadId: 42,
683707});
684708});
685709});
@@ -904,15 +928,16 @@ describe("agent request events", () => {
904928905929expect(agentCommandMock).toHaveBeenCalledTimes(1);
906930const [opts] = agentCommandMock.mock.calls[0] ?? [];
907-expect(opts).toMatchObject({
931+expectFields(opts, {
908932message: "summarize this",
909933sessionKey: "agent:main:main",
910934deliver: false,
911935channel: undefined,
912936to: undefined,
913937});
914-expect(warn).toHaveBeenCalledWith(
915-expect.stringContaining("agent delivery disabled node=node-route-miss"),
938+expect(warn).toHaveBeenCalledTimes(1);
939+expect(String(warn.mock.calls[0]?.[0])).toContain(
940+"agent delivery disabled node=node-route-miss",
916941);
917942});
918943@@ -938,7 +963,7 @@ describe("agent request events", () => {
938963939964expect(agentCommandMock).toHaveBeenCalledTimes(1);
940965const [opts] = agentCommandMock.mock.calls[0] ?? [];
941-expect(opts).toMatchObject({
966+expectFields(opts, {
942967message: "route on session",
943968sessionKey: "agent:main:main",
944969deliver: true,
@@ -982,11 +1007,11 @@ describe("agent request events", () => {
9821007}),
9831008});
9841009985-expect(parseMessageWithAttachmentsMock).toHaveBeenCalledWith(
986- "describe",
987- expect.any(Array),
988- expect.objectContaining({ supportsInlineImages: false }),
989-);
1010+expect(parseMessageWithAttachmentsMock).toHaveBeenCalledTimes(1);
1011+const parseCall = parseMessageWithAttachmentsMock.mock.calls[0];
1012+expect(parseCall?.[0]).toBe("describe");
1013+expect(Array.isArray(parseCall?.[1])).toBe(true);
1014+expectFields(parseCall?.[2], { supportsInlineImages: false });
9901015});
99110169921017it("declines non-image attachments cleanly when parse throws UnsupportedAttachmentError", async () => {
@@ -1052,14 +1077,8 @@ describe("agent request events", () => {
10521077handled: true,
10531078reason: "persisted",
10541079});
1055-expect(updatePairedNodeMetadataMock).toHaveBeenCalledWith("ios-node", {
1056-lastSeenAtMs: expect.any(Number),
1057-lastSeenReason: "bg_app_refresh",
1058-});
1059-expect(updatePairedDeviceMetadataMock).toHaveBeenCalledWith("ios-node", {
1060-lastSeenAtMs: expect.any(Number),
1061-lastSeenReason: "bg_app_refresh",
1062-});
1080+expectPresencePersistCall(updatePairedNodeMetadataMock, "ios-node", "bg_app_refresh");
1081+expectPresencePersistCall(updatePairedDeviceMetadataMock, "ios-node", "bg_app_refresh");
10631082expect(getRecentNodePresencePersistCountForTests()).toBe(1);
10641083});
10651084@@ -1093,14 +1112,8 @@ describe("agent request events", () => {
10931112{ deviceId: "ios-node" },
10941113);
109511141096-expect(updatePairedNodeMetadataMock).toHaveBeenCalledWith("ios-node", {
1097-lastSeenAtMs: expect.any(Number),
1098-lastSeenReason: "background",
1099-});
1100-expect(updatePairedDeviceMetadataMock).toHaveBeenCalledWith("ios-node", {
1101-lastSeenAtMs: expect.any(Number),
1102-lastSeenReason: "background",
1103-});
1115+expectPresencePersistCall(updatePairedNodeMetadataMock, "ios-node", "background");
1116+expectPresencePersistCall(updatePairedDeviceMetadataMock, "ios-node", "background");
11041117});
1105111811061119it("does not throttle unknown node presence alive identities", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。