Skip to content

Commit 92932aa

Browse files
committed
test: add unit tests for LDAIConfigTrackerImpl metrics tracking
1 parent eb54ec6 commit 92932aa

File tree

1 file changed

+268
-0
lines changed

1 file changed

+268
-0
lines changed
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
import { LDAIConfigTrackerImpl } from '../src/LDAIConfigTrackerImpl';
2+
import type { LDClientMin } from '../src/LDClientMin';
3+
4+
describe('LDAIConfigTrackerImpl metrics', () => {
5+
let mockLDClient: jest.Mocked<LDClientMin>;
6+
let tracker: LDAIConfigTrackerImpl;
7+
const context = { kind: 'user', key: 'example-user-key', name: 'Sandy' } as any;
8+
9+
beforeEach(() => {
10+
mockLDClient = {
11+
variation: jest.fn(),
12+
variationDetail: jest.fn(),
13+
track: jest.fn(),
14+
};
15+
16+
tracker = new LDAIConfigTrackerImpl(
17+
mockLDClient,
18+
'ai-config-key',
19+
'variation-key',
20+
3,
21+
'@cf/test-model',
22+
'cloudflare-workers-ai',
23+
context,
24+
);
25+
});
26+
27+
afterEach(() => {
28+
jest.restoreAllMocks();
29+
});
30+
31+
it('tracks success metric with metadata', () => {
32+
tracker.trackSuccess();
33+
34+
expect(mockLDClient.track).toHaveBeenCalledWith(
35+
'$ld:ai:generation:success',
36+
context,
37+
expect.objectContaining({
38+
aiConfigKey: 'ai-config-key',
39+
variationKey: 'variation-key',
40+
version: 3,
41+
model: '@cf/test-model',
42+
provider: 'cloudflare-workers-ai',
43+
}),
44+
1,
45+
);
46+
});
47+
48+
it('tracks error metric with metadata', () => {
49+
tracker.trackError();
50+
51+
expect(mockLDClient.track).toHaveBeenCalledWith(
52+
'$ld:ai:generation:error',
53+
context,
54+
expect.any(Object),
55+
1,
56+
);
57+
});
58+
59+
it('tracks duration metric', () => {
60+
tracker.trackDuration(1250);
61+
62+
expect(mockLDClient.track).toHaveBeenCalledWith(
63+
'$ld:ai:duration:total',
64+
context,
65+
expect.objectContaining({
66+
aiConfigKey: 'ai-config-key',
67+
variationKey: 'variation-key',
68+
}),
69+
1250,
70+
);
71+
});
72+
73+
it('tracks token metrics', () => {
74+
tracker.trackTokens({ input: 5, output: 7, total: 12 });
75+
76+
expect(mockLDClient.track).toHaveBeenNthCalledWith(
77+
1,
78+
'$ld:ai:tokens:total',
79+
context,
80+
expect.any(Object),
81+
12,
82+
);
83+
expect(mockLDClient.track).toHaveBeenNthCalledWith(
84+
2,
85+
'$ld:ai:tokens:input',
86+
context,
87+
expect.any(Object),
88+
5,
89+
);
90+
expect(mockLDClient.track).toHaveBeenNthCalledWith(
91+
3,
92+
'$ld:ai:tokens:output',
93+
context,
94+
expect.any(Object),
95+
7,
96+
);
97+
});
98+
99+
it('tracks time to first token metric', () => {
100+
tracker.trackTimeToFirstToken(321);
101+
102+
expect(mockLDClient.track).toHaveBeenCalledWith(
103+
'$ld:ai:tokens:ttf',
104+
context,
105+
expect.objectContaining({
106+
model: '@cf/test-model',
107+
}),
108+
321,
109+
);
110+
});
111+
112+
it('tracks aggregated metrics via trackMetrics helper', () => {
113+
tracker.trackMetrics({
114+
durationMs: 640,
115+
usage: { input: 2, output: 3, total: 5 },
116+
success: true,
117+
});
118+
119+
expect(mockLDClient.track).toHaveBeenCalledWith(
120+
'$ld:ai:duration:total',
121+
context,
122+
expect.any(Object),
123+
640,
124+
);
125+
expect(mockLDClient.track).toHaveBeenCalledWith(
126+
'$ld:ai:generation:success',
127+
context,
128+
expect.any(Object),
129+
1,
130+
);
131+
expect(mockLDClient.track).toHaveBeenCalledWith(
132+
'$ld:ai:tokens:total',
133+
context,
134+
expect.any(Object),
135+
5,
136+
);
137+
});
138+
139+
it('tracks metrics from Workers AI promise responses', async () => {
140+
const dateSpy = jest.spyOn(Date, 'now');
141+
dateSpy.mockReturnValueOnce(1).mockReturnValueOnce(101);
142+
143+
const result = await tracker.trackWorkersAIMetrics(async () => ({
144+
usage: {
145+
prompt_tokens: 4,
146+
completion_tokens: 6,
147+
total_tokens: 10,
148+
},
149+
}));
150+
151+
expect(result).toEqual({
152+
usage: {
153+
prompt_tokens: 4,
154+
completion_tokens: 6,
155+
total_tokens: 10,
156+
},
157+
});
158+
159+
expect(mockLDClient.track).toHaveBeenCalledWith(
160+
'$ld:ai:duration:total',
161+
context,
162+
expect.any(Object),
163+
100,
164+
);
165+
expect(mockLDClient.track).toHaveBeenCalledWith(
166+
'$ld:ai:generation:success',
167+
context,
168+
expect.any(Object),
169+
1,
170+
);
171+
expect(mockLDClient.track).toHaveBeenCalledWith(
172+
'$ld:ai:tokens:input',
173+
context,
174+
expect.any(Object),
175+
4,
176+
);
177+
expect(mockLDClient.track).toHaveBeenCalledWith(
178+
'$ld:ai:tokens:output',
179+
context,
180+
expect.any(Object),
181+
6,
182+
);
183+
expect(mockLDClient.track).toHaveBeenCalledWith(
184+
'$ld:ai:tokens:total',
185+
context,
186+
expect.any(Object),
187+
10,
188+
);
189+
});
190+
191+
it('tracks errors from Workers AI promise responses', async () => {
192+
const dateSpy = jest.spyOn(Date, 'now');
193+
dateSpy.mockReturnValueOnce(10).mockReturnValueOnce(30);
194+
195+
await expect(
196+
tracker.trackWorkersAIMetrics(async () => {
197+
throw new Error('boom');
198+
}),
199+
).rejects.toThrow('boom');
200+
201+
expect(mockLDClient.track).toHaveBeenCalledWith(
202+
'$ld:ai:duration:total',
203+
context,
204+
expect.any(Object),
205+
20,
206+
);
207+
expect(mockLDClient.track).toHaveBeenCalledWith(
208+
'$ld:ai:generation:error',
209+
context,
210+
expect.any(Object),
211+
1,
212+
);
213+
});
214+
215+
it('tracks metrics from Workers AI streaming responses', async () => {
216+
const dateSpy = jest.spyOn(Date, 'now');
217+
dateSpy.mockReturnValueOnce(5).mockReturnValueOnce(155);
218+
219+
const usagePromise = Promise.resolve({
220+
input_tokens: 8,
221+
output_tokens: 9,
222+
});
223+
const finishReason = Promise.resolve('stop');
224+
225+
tracker.trackWorkersAIStreamMetrics(
226+
() =>
227+
({
228+
usage: usagePromise,
229+
finishReason,
230+
}) as any,
231+
);
232+
233+
await finishReason;
234+
await usagePromise;
235+
await Promise.resolve();
236+
237+
expect(mockLDClient.track).toHaveBeenCalledWith(
238+
'$ld:ai:duration:total',
239+
context,
240+
expect.any(Object),
241+
150,
242+
);
243+
expect(mockLDClient.track).toHaveBeenCalledWith(
244+
'$ld:ai:generation:success',
245+
context,
246+
expect.any(Object),
247+
1,
248+
);
249+
expect(mockLDClient.track).toHaveBeenCalledWith(
250+
'$ld:ai:tokens:input',
251+
context,
252+
expect.any(Object),
253+
8,
254+
);
255+
expect(mockLDClient.track).toHaveBeenCalledWith(
256+
'$ld:ai:tokens:output',
257+
context,
258+
expect.any(Object),
259+
9,
260+
);
261+
expect(mockLDClient.track).toHaveBeenCalledWith(
262+
'$ld:ai:tokens:total',
263+
context,
264+
expect.any(Object),
265+
17,
266+
);
267+
});
268+
});

0 commit comments

Comments
 (0)