Skip to content

Commit 6dce4d0

Browse files
committed
refactor: pilot shared stream test utilities
1 parent 38d7e23 commit 6dce4d0

3 files changed

Lines changed: 35 additions & 44 deletions

File tree

src/api/providers/__tests__/openai-native.spec.ts

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ApiProviderError, OpenAiServiceTier, SERVICE_TIER_KEY, serviceTiers } f
1818
import { OpenAiNativeHandler } from "../openai-native"
1919
import { ApiHandlerOptions } from "../../../shared/api"
2020
import { Package } from "../../../shared/package"
21+
import { asyncStreamFrom, collectStream } from "../../../test-utils/stream"
2122

2223
// Mock OpenAI client - now everything uses Responses API
2324
const mockResponsesCreate = vitest.fn()
@@ -141,18 +142,14 @@ describe("OpenAiNativeHandler", () => {
141142

142143
describe("createMessage", () => {
143144
it.each(serviceTiers)("should include the selected %s service tier", async (serviceTier) => {
144-
mockResponsesCreate.mockResolvedValue({
145-
async *[Symbol.asyncIterator]() {},
146-
})
145+
mockResponsesCreate.mockResolvedValue(asyncStreamFrom([]))
147146
handler = new OpenAiNativeHandler({
148147
...mockOptions,
149148
apiModelId: "gpt-5.6-sol",
150149
openAiNativeServiceTier: serviceTier,
151150
})
152151

153-
for await (const chunk of handler.createMessage(systemPrompt, messages)) {
154-
void chunk
155-
}
152+
await collectStream(handler.createMessage(systemPrompt, messages))
156153

157154
expect(mockResponsesCreate).toHaveBeenCalledWith(
158155
expect.objectContaining({ [SERVICE_TIER_KEY]: serviceTier }),
@@ -163,27 +160,24 @@ describe("OpenAiNativeHandler", () => {
163160
it.each(serviceTierPricingCases)(
164161
"prices SDK stream usage using resolved $resolvedTier tier instead of requested $requestedTier tier",
165162
async ({ requestedTier, resolvedTier, expectedCost }) => {
166-
mockResponsesCreate.mockResolvedValue({
167-
async *[Symbol.asyncIterator]() {
168-
yield {
163+
mockResponsesCreate.mockResolvedValue(
164+
asyncStreamFrom([
165+
{
169166
type: "response.done",
170167
response: {
171168
[SERVICE_TIER_KEY]: resolvedTier,
172169
usage: { input_tokens: 100, output_tokens: 20 },
173170
},
174-
}
175-
},
176-
})
171+
},
172+
]),
173+
)
177174
handler = new OpenAiNativeHandler({
178175
...mockOptions,
179176
apiModelId: "gpt-5.6-sol",
180177
openAiNativeServiceTier: requestedTier,
181178
})
182179

183-
const chunks = []
184-
for await (const chunk of handler.createMessage(systemPrompt, messages)) {
185-
chunks.push(chunk)
186-
}
180+
const chunks = await collectStream(handler.createMessage(systemPrompt, messages))
187181

188182
expect(chunks).toContainEqual(
189183
expect.objectContaining({
@@ -219,9 +213,9 @@ describe("OpenAiNativeHandler", () => {
219213
expectedCost: 0.088,
220214
},
221215
])("retains standard pricing for $name", async ({ modelId, requestedTier, resolvedTier, expectedCost }) => {
222-
mockResponsesCreate.mockResolvedValue({
223-
async *[Symbol.asyncIterator]() {
224-
yield {
216+
mockResponsesCreate.mockResolvedValue(
217+
asyncStreamFrom([
218+
{
225219
type: "response.done",
226220
response: {
227221
...(resolvedTier ? { [SERVICE_TIER_KEY]: resolvedTier } : {}),
@@ -231,19 +225,16 @@ describe("OpenAiNativeHandler", () => {
231225
cache_read_input_tokens: 20_000,
232226
},
233227
},
234-
}
235-
},
236-
})
228+
},
229+
]),
230+
)
237231
handler = new OpenAiNativeHandler({
238232
...mockOptions,
239233
apiModelId: modelId,
240234
openAiNativeServiceTier: requestedTier,
241235
})
242236

243-
const chunks = []
244-
for await (const chunk of handler.createMessage(systemPrompt, messages)) {
245-
chunks.push(chunk)
246-
}
237+
const chunks = await collectStream(handler.createMessage(systemPrompt, messages))
247238

248239
const usageChunk = chunks.find((chunk) => chunk.type === "usage")
249240
expect(usageChunk).toBeDefined()
@@ -281,10 +272,7 @@ describe("OpenAiNativeHandler", () => {
281272
openAiNativeServiceTier: requestedTier,
282273
})
283274

284-
const chunks = []
285-
for await (const chunk of handler.createMessage(systemPrompt, messages)) {
286-
chunks.push(chunk)
287-
}
275+
const chunks = await collectStream(handler.createMessage(systemPrompt, messages))
288276

289277
const [, request] = mockFetch.mock.calls[0]
290278
expect(JSON.parse(request.body)).toMatchObject({ [SERVICE_TIER_KEY]: requestedTier })
@@ -328,10 +316,7 @@ describe("OpenAiNativeHandler", () => {
328316
openAiNativeServiceTier: requestedTier,
329317
})
330318

331-
const chunks = []
332-
for await (const chunk of handler.createMessage(systemPrompt, messages)) {
333-
chunks.push(chunk)
334-
}
319+
const chunks = await collectStream(handler.createMessage(systemPrompt, messages))
335320

336321
expect(chunks).toContainEqual(
337322
expect.objectContaining({
@@ -371,11 +356,7 @@ describe("OpenAiNativeHandler", () => {
371356
// Mock SDK to fail so it falls back to fetch
372357
mockResponsesCreate.mockRejectedValue(new Error("SDK not available"))
373358

374-
const stream = handler.createMessage(systemPrompt, messages)
375-
const chunks: any[] = []
376-
for await (const chunk of stream) {
377-
chunks.push(chunk)
378-
}
359+
const chunks = await collectStream(handler.createMessage(systemPrompt, messages))
379360

380361
expect(chunks.length).toBeGreaterThan(0)
381362
const textChunks = chunks.filter((chunk) => chunk.type === "text")
@@ -2023,10 +2004,7 @@ describe("GPT-5 streaming event coverage (additional)", () => {
20232004

20242005
// Should throw an error when encountering error event
20252006
await expect(async () => {
2026-
const chunks = []
2027-
for await (const chunk of stream) {
2028-
chunks.push(chunk)
2029-
}
2007+
await collectStream(stream)
20302008
}).rejects.toThrow("Responses API error: Model overloaded")
20312009
})
20322010

src/eslint-suppressions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@
226226
},
227227
"api/providers/__tests__/openai-native.spec.ts": {
228228
"@typescript-eslint/no-explicit-any": {
229-
"count": 79
229+
"count": 78
230230
}
231231
},
232232
"api/providers/__tests__/openai-timeout.spec.ts": {

src/test-utils/stream.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export async function* asyncStreamFrom<T>(chunks: T[]): AsyncGenerator<T> {
2+
for (const chunk of chunks) {
3+
yield chunk
4+
}
5+
}
6+
7+
export async function collectStream<T>(stream: AsyncIterable<T>): Promise<T[]> {
8+
const chunks: T[] = []
9+
for await (const chunk of stream) {
10+
chunks.push(chunk)
11+
}
12+
return chunks
13+
}

0 commit comments

Comments
 (0)