

























@@ -16,6 +16,42 @@ function flushDiagnosticEvents() {
1616return new Promise<void>((resolve) => setImmediate(resolve));
1717}
181819+type ObservedDiagnostic = { event: DiagnosticEventPayload; trusted: boolean };
20+21+function stableDiagnosticPayload<TEvent extends DiagnosticEventPayload>(
22+event: TEvent,
23+): Omit<TEvent, "seq" | "trace" | "ts"> {
24+expect(event.seq).toBeGreaterThan(0);
25+expect(event.ts).toBeGreaterThan(0);
26+const { seq: _seq, ts: _ts, trace, ...stable } = event;
27+expect(trace).toBeUndefined();
28+return stable;
29+}
30+31+function stableLogRecordPayload(event: Extract<DiagnosticEventPayload, { type: "log.record" }>) {
32+const { code, loggerParents, ...stable } = stableDiagnosticPayload(event);
33+expect(loggerParents).toStrictEqual(["openclaw"]);
34+expect(code?.functionName).toBe("recordTalkLogEvent");
35+expect(code?.line).toBeGreaterThan(0);
36+return stable;
37+}
38+39+function requireObservedDiagnostic<TType extends DiagnosticEventPayload["type"]>(
40+observed: readonly ObservedDiagnostic[],
41+type: TType,
42+): { event: Extract<DiagnosticEventPayload, { type: TType }>; trusted: boolean } {
43+const event = observed.find(
44+(
45+entry,
46+): entry is { event: Extract<DiagnosticEventPayload, { type: TType }>; trusted: boolean } =>
47+entry.event.type === type,
48+);
49+if (!event) {
50+throw new Error(`Expected ${type} diagnostic event`);
51+}
52+return event;
53+}
54+1955describe("talk logging", () => {
2056let tmpDir: string;
2157let logFile: string;
@@ -81,7 +117,7 @@ describe("talk logging", () => {
81117unsubscribe();
8211883119expect(logs).toHaveLength(1);
84-expect(logs[0]).toMatchObject({
120+expect(stableLogRecordPayload(logs[0])).toStrictEqual({
85121type: "log.record",
86122level: "INFO",
87123message: "talk event output.text.done",
@@ -148,7 +184,7 @@ describe("talk logging", () => {
148184});
149185150186it("records diagnostics and logs through the combined observability hook", async () => {
151-const observed: Array<{ event: DiagnosticEventPayload; trusted: boolean }> = [];
187+const observed: ObservedDiagnostic[] = [];
152188const unsubscribe = onInternalDiagnosticEvent((event, metadata) => {
153189observed.push({ event, trusted: metadata.trusted });
154190});
@@ -170,26 +206,40 @@ describe("talk logging", () => {
170206await flushDiagnosticEvents();
171207unsubscribe();
172208173-expect(observed).toEqual(
174-expect.arrayContaining([
175-expect.objectContaining({
176-trusted: true,
177-event: expect.objectContaining({
178-type: "talk.event",
179-talkEventType: "session.error",
180-sessionId: "talk-session",
181-}),
182-}),
183-expect.objectContaining({
184-trusted: false,
185-event: expect.objectContaining({
186-type: "log.record",
187-level: "WARN",
188-message: "talk event session.error",
189-}),
190-}),
191-]),
192-);
209+expect(observed).toHaveLength(2);
210+const talkEvent = requireObservedDiagnostic(observed, "talk.event");
211+const logRecord = requireObservedDiagnostic(observed, "log.record");
212+expect(talkEvent.trusted).toBe(true);
213+expect(stableDiagnosticPayload(talkEvent.event)).toStrictEqual({
214+type: "talk.event",
215+sessionId: "talk-session",
216+turnId: undefined,
217+captureId: undefined,
218+talkEventType: "session.error",
219+mode: "realtime",
220+transport: "gateway-relay",
221+brain: "agent-consult",
222+provider: "openai",
223+final: true,
224+durationMs: undefined,
225+byteLength: undefined,
226+});
227+expect(logRecord.trusted).toBe(false);
228+expect(stableLogRecordPayload(logRecord.event)).toStrictEqual({
229+type: "log.record",
230+level: "WARN",
231+message: "talk event session.error",
232+attributes: {
233+subsystem: "talk",
234+sessionId: "talk-session",
235+talkEventType: "session.error",
236+talkMode: "realtime",
237+talkTransport: "gateway-relay",
238+talkBrain: "agent-consult",
239+talkProvider: "openai",
240+talkFinal: true,
241+},
242+});
193243expect(JSON.stringify(observed)).not.toContain("private detail");
194244});
195245});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。