

























@@ -126,6 +126,24 @@ describe("provider-catalog-live-runtime", () => {
126126).resolves.toEqual(["custom-a", "custom-b"]);
127127});
128128129+it("accepts UTF-8 BOM-prefixed catalog responses", async () => {
130+const release = vi.fn(async () => undefined);
131+const fetchGuardMock: MockedFunction<LiveModelCatalogFetchGuard> = vi.fn(async () => ({
132+response: new Response("\uFEFF" + JSON.stringify({ data: [{ id: "model-a" }] })),
133+finalUrl: "https://provider.example.test/v1/models",
134+ release,
135+}));
136+137+await expect(
138+fetchLiveProviderModelIds({
139+providerId: "provider",
140+endpoint: "https://provider.example.test/v1/models",
141+fetchGuard: fetchGuardMock,
142+}),
143+).resolves.toEqual(["model-a"]);
144+expect(release).toHaveBeenCalledTimes(1);
145+});
146+129147it("caches raw live model rows for provider-specific projection", async () => {
130148const { fetchGuard, fetchGuardMock } = buildFetchGuard({
131149models: [{ slug: "custom-a" }, { slug: "custom-b" }],
@@ -157,6 +175,91 @@ describe("provider-catalog-live-runtime", () => {
157175expect(fetchGuardMock).toHaveBeenCalledTimes(1);
158176});
159177178+it("bounds an unbounded live catalog success stream and cancels the body", async () => {
179+const encoder = new TextEncoder();
180+let pullCount = 0;
181+let cancelled = false;
182+const release = vi.fn(async () => undefined);
183+const fetchGuardMock: MockedFunction<LiveModelCatalogFetchGuard> = vi.fn(async () => ({
184+response: new Response(
185+new ReadableStream<Uint8Array>({
186+pull(controller) {
187+pullCount += 1;
188+// Stream a JSON array prefix followed by an effectively endless run of
189+// padding so the body never terminates under its own power.
190+if (pullCount === 1) {
191+controller.enqueue(encoder.encode('[{"id":"model-a","object":"model"},'));
192+return;
193+}
194+controller.enqueue(encoder.encode("0".repeat(1024 * 1024)));
195+},
196+cancel() {
197+cancelled = true;
198+},
199+}),
200+{ headers: { "content-type": "application/json" } },
201+),
202+finalUrl: "https://provider.example.test/v1/models",
203+ release,
204+}));
205+206+const error = await fetchLiveProviderModelIds({
207+providerId: "provider",
208+endpoint: "https://provider.example.test/v1/models",
209+fetchGuard: fetchGuardMock,
210+}).catch((err: unknown) => err);
211+212+expect(error).toBeInstanceOf(Error);
213+expect((error as Error).message).toMatch(/Live model catalog response exceeded \d+ bytes/);
214+expect(cancelled).toBe(true);
215+expect(release).toHaveBeenCalledTimes(1);
216+});
217+218+it("aborts a stalled live catalog success stream", async () => {
219+vi.useFakeTimers();
220+try {
221+const encoder = new TextEncoder();
222+let cancelReason: unknown;
223+const release = vi.fn(async () => undefined);
224+const fetchGuardMock: MockedFunction<LiveModelCatalogFetchGuard> = vi.fn(async () => ({
225+response: new Response(
226+new ReadableStream<Uint8Array>({
227+start(controller) {
228+// Emit a partial JSON prefix and then idle forever without closing.
229+controller.enqueue(encoder.encode('[{"id":"model-a",'));
230+},
231+cancel(reason) {
232+cancelReason = reason;
233+},
234+}),
235+{ headers: { "content-type": "application/json" } },
236+),
237+finalUrl: "https://provider.example.test/v1/models",
238+ release,
239+}));
240+241+const resultPromise = fetchLiveProviderModelIds({
242+providerId: "provider",
243+endpoint: "https://provider.example.test/v1/models",
244+fetchGuard: fetchGuardMock,
245+timeoutMs: 1234,
246+}).catch((err: unknown) => err);
247+248+await vi.advanceTimersByTimeAsync(0);
249+await vi.advanceTimersByTimeAsync(1234);
250+const error = await resultPromise;
251+252+expect(error).toBeInstanceOf(Error);
253+expect((error as Error).message).toBe(
254+"Live model catalog response stalled: no data received for 1234ms",
255+);
256+expect(cancelReason).toBeInstanceOf(Error);
257+expect(release).toHaveBeenCalledTimes(1);
258+} finally {
259+vi.useRealTimers();
260+}
261+});
262+160263it("throws structured HTTP errors after releasing guarded fetches", async () => {
161264const release = vi.fn(async () => undefined);
162265const response = new Response("{}", { status: 401 });
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。