Skip to content

Commit e285ede

Browse files
authored
Merge pull request #1398 from ag-ui-protocol/fix/dojo-cpk-package-consolidation
feat(client): add granular debug mode to AG-UI event pipeline
2 parents 3a5958f + 6dddd4b commit e285ede

18 files changed

Lines changed: 1831 additions & 75 deletions

File tree

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2+
import { DebugLogger, createDebugLogger } from "@/debug-logger";
3+
import { resolveAgentDebugConfig, ResolvedAgentDebugConfig } from "@/agent/types";
4+
5+
describe("resolveAgentDebugConfig", () => {
6+
it("undefined -> all fields false", () => {
7+
const result = resolveAgentDebugConfig(undefined);
8+
expect(result).toEqual({
9+
enabled: false,
10+
events: false,
11+
lifecycle: false,
12+
verbose: false,
13+
});
14+
});
15+
16+
it("false -> all fields false", () => {
17+
const result = resolveAgentDebugConfig(false);
18+
expect(result).toEqual({
19+
enabled: false,
20+
events: false,
21+
lifecycle: false,
22+
verbose: false,
23+
});
24+
});
25+
26+
it("true -> all fields true", () => {
27+
const result = resolveAgentDebugConfig(true);
28+
expect(result).toEqual({
29+
enabled: true,
30+
events: true,
31+
lifecycle: true,
32+
verbose: true,
33+
});
34+
});
35+
36+
it("{} -> events true, lifecycle true, verbose false, enabled true", () => {
37+
const result = resolveAgentDebugConfig({});
38+
expect(result).toEqual({
39+
enabled: true,
40+
events: true,
41+
lifecycle: true,
42+
verbose: false,
43+
});
44+
});
45+
46+
it("{ events: true } -> events true, lifecycle true (default), verbose false", () => {
47+
const result = resolveAgentDebugConfig({ events: true });
48+
expect(result).toEqual({
49+
enabled: true,
50+
events: true,
51+
lifecycle: true,
52+
verbose: false,
53+
});
54+
});
55+
56+
it("{ events: false } -> events false, lifecycle true, verbose false", () => {
57+
const result = resolveAgentDebugConfig({ events: false });
58+
expect(result).toEqual({
59+
enabled: true,
60+
events: false,
61+
lifecycle: true,
62+
verbose: false,
63+
});
64+
});
65+
66+
it("{ lifecycle: false } -> events true, lifecycle false, verbose false", () => {
67+
const result = resolveAgentDebugConfig({ lifecycle: false });
68+
expect(result).toEqual({
69+
enabled: true,
70+
events: true,
71+
lifecycle: false,
72+
verbose: false,
73+
});
74+
});
75+
76+
it("{ verbose: true } -> events true, lifecycle true, verbose true", () => {
77+
const result = resolveAgentDebugConfig({ verbose: true });
78+
expect(result).toEqual({
79+
enabled: true,
80+
events: true,
81+
lifecycle: true,
82+
verbose: true,
83+
});
84+
});
85+
86+
it("{ events: false, lifecycle: false } -> enabled false", () => {
87+
const result = resolveAgentDebugConfig({
88+
events: false,
89+
lifecycle: false,
90+
});
91+
expect(result).toEqual({
92+
enabled: false,
93+
events: false,
94+
lifecycle: false,
95+
verbose: false,
96+
});
97+
});
98+
99+
it("{ events: false, lifecycle: false, verbose: true } -> enabled false (verbose alone doesn't enable)", () => {
100+
const result = resolveAgentDebugConfig({
101+
events: false,
102+
lifecycle: false,
103+
verbose: true,
104+
});
105+
expect(result).toEqual({
106+
enabled: false,
107+
events: false,
108+
lifecycle: false,
109+
verbose: true,
110+
});
111+
});
112+
113+
it("{ events: true, lifecycle: false, verbose: true } -> enabled true", () => {
114+
const result = resolveAgentDebugConfig({
115+
events: true,
116+
lifecycle: false,
117+
verbose: true,
118+
});
119+
expect(result).toEqual({
120+
enabled: true,
121+
events: true,
122+
lifecycle: false,
123+
verbose: true,
124+
});
125+
});
126+
});
127+
128+
describe("createDebugLogger", () => {
129+
it("returns undefined when config has enabled: false", () => {
130+
const config: ResolvedAgentDebugConfig = {
131+
enabled: false,
132+
events: false,
133+
lifecycle: false,
134+
verbose: false,
135+
};
136+
expect(createDebugLogger(config)).toBeUndefined();
137+
});
138+
139+
it("returns DebugLogger instance when config has enabled: true", () => {
140+
const config: ResolvedAgentDebugConfig = {
141+
enabled: true,
142+
events: true,
143+
lifecycle: true,
144+
verbose: false,
145+
};
146+
const logger = createDebugLogger(config);
147+
expect(logger).toBeInstanceOf(DebugLogger);
148+
});
149+
});
150+
151+
describe("DebugLogger.event()", () => {
152+
let debugSpy: ReturnType<typeof vi.spyOn>;
153+
154+
beforeEach(() => {
155+
debugSpy = vi.spyOn(console, "debug").mockImplementation(() => {});
156+
});
157+
158+
afterEach(() => {
159+
vi.restoreAllMocks();
160+
});
161+
162+
it("does NOT call console.debug when events is disabled", () => {
163+
const logger = new DebugLogger({
164+
enabled: true,
165+
events: false,
166+
lifecycle: true,
167+
verbose: false,
168+
});
169+
logger.event("PREFIX", "some label", { foo: "bar" });
170+
expect(debugSpy).not.toHaveBeenCalled();
171+
});
172+
173+
it("calls console.debug with [PREFIX] label and JSON.stringify(data) when verbose is true", () => {
174+
const logger = new DebugLogger({
175+
enabled: true,
176+
events: true,
177+
lifecycle: true,
178+
verbose: true,
179+
});
180+
const data = { type: "TEST", value: 42 };
181+
logger.event("PREFIX", "some label", data);
182+
expect(debugSpy).toHaveBeenCalledTimes(1);
183+
expect(debugSpy).toHaveBeenCalledWith("[PREFIX] some label", JSON.stringify(data));
184+
});
185+
186+
it("calls console.debug with [PREFIX] label and summary object when verbose is false and summary provided", () => {
187+
const logger = new DebugLogger({
188+
enabled: true,
189+
events: true,
190+
lifecycle: true,
191+
verbose: false,
192+
});
193+
const data = { type: "TEST", value: 42, bigPayload: "lots of data" };
194+
const summary = { type: "TEST" };
195+
logger.event("PREFIX", "some label", data, summary);
196+
expect(debugSpy).toHaveBeenCalledTimes(1);
197+
expect(debugSpy).toHaveBeenCalledWith("[PREFIX] some label", summary);
198+
});
199+
200+
it("calls console.debug with [PREFIX] label and raw data when verbose is false and no summary provided", () => {
201+
const logger = new DebugLogger({
202+
enabled: true,
203+
events: true,
204+
lifecycle: true,
205+
verbose: false,
206+
});
207+
const data = { type: "TEST", value: 42 };
208+
logger.event("PREFIX", "some label", data);
209+
expect(debugSpy).toHaveBeenCalledTimes(1);
210+
expect(debugSpy).toHaveBeenCalledWith("[PREFIX] some label", data);
211+
});
212+
213+
it("handles string data correctly in verbose mode (no double-stringify)", () => {
214+
const logger = new DebugLogger({
215+
enabled: true,
216+
events: true,
217+
lifecycle: true,
218+
verbose: true,
219+
});
220+
const data = "just a string";
221+
logger.event("PREFIX", "some label", data);
222+
expect(debugSpy).toHaveBeenCalledTimes(1);
223+
// String data should be passed directly, not JSON.stringify'd
224+
expect(debugSpy).toHaveBeenCalledWith("[PREFIX] some label", "just a string");
225+
});
226+
});
227+
228+
describe("DebugLogger.lifecycle()", () => {
229+
let debugSpy: ReturnType<typeof vi.spyOn>;
230+
231+
beforeEach(() => {
232+
debugSpy = vi.spyOn(console, "debug").mockImplementation(() => {});
233+
});
234+
235+
afterEach(() => {
236+
vi.restoreAllMocks();
237+
});
238+
239+
it("does NOT call console.debug when lifecycle is disabled", () => {
240+
const logger = new DebugLogger({
241+
enabled: true,
242+
events: true,
243+
lifecycle: false,
244+
verbose: false,
245+
});
246+
logger.lifecycle("PREFIX", "some label", { key: "value" });
247+
expect(debugSpy).not.toHaveBeenCalled();
248+
});
249+
250+
it("calls console.debug with [PREFIX] label and data when data provided", () => {
251+
const logger = new DebugLogger({
252+
enabled: true,
253+
events: true,
254+
lifecycle: true,
255+
verbose: false,
256+
});
257+
const data = { agentId: "agent-1", threadId: "thread-1" };
258+
logger.lifecycle("PREFIX", "some label", data);
259+
expect(debugSpy).toHaveBeenCalledTimes(1);
260+
expect(debugSpy).toHaveBeenCalledWith("[PREFIX] some label", data);
261+
});
262+
263+
it("calls console.debug with [PREFIX] label only when no data provided", () => {
264+
const logger = new DebugLogger({
265+
enabled: true,
266+
events: true,
267+
lifecycle: true,
268+
verbose: false,
269+
});
270+
logger.lifecycle("PREFIX", "some label");
271+
expect(debugSpy).toHaveBeenCalledTimes(1);
272+
expect(debugSpy).toHaveBeenCalledWith("[PREFIX] some label");
273+
});
274+
});
275+
276+
describe("DebugLogger getters", () => {
277+
it("enabled returns config.enabled", () => {
278+
const logger = new DebugLogger({
279+
enabled: true,
280+
events: false,
281+
lifecycle: false,
282+
verbose: false,
283+
});
284+
expect(logger.enabled).toBe(true);
285+
286+
const logger2 = new DebugLogger({
287+
enabled: false,
288+
events: false,
289+
lifecycle: false,
290+
verbose: false,
291+
});
292+
expect(logger2.enabled).toBe(false);
293+
});
294+
295+
it("eventsEnabled returns config.events", () => {
296+
const logger = new DebugLogger({
297+
enabled: true,
298+
events: true,
299+
lifecycle: false,
300+
verbose: false,
301+
});
302+
expect(logger.eventsEnabled).toBe(true);
303+
304+
const logger2 = new DebugLogger({
305+
enabled: true,
306+
events: false,
307+
lifecycle: true,
308+
verbose: false,
309+
});
310+
expect(logger2.eventsEnabled).toBe(false);
311+
});
312+
313+
it("lifecycleEnabled returns config.lifecycle", () => {
314+
const logger = new DebugLogger({
315+
enabled: true,
316+
events: false,
317+
lifecycle: true,
318+
verbose: false,
319+
});
320+
expect(logger.lifecycleEnabled).toBe(true);
321+
322+
const logger2 = new DebugLogger({
323+
enabled: true,
324+
events: true,
325+
lifecycle: false,
326+
verbose: false,
327+
});
328+
expect(logger2.lifecycleEnabled).toBe(false);
329+
});
330+
});

0 commit comments

Comments
 (0)