



























@@ -12,6 +12,7 @@ const {
1212 fetchWithTimeoutGuardedMock,
1313 pollProviderOperationJsonMock,
1414 assertOkOrThrowHttpErrorMock,
15+ executeProviderOperationWithRetryMock,
1516 resolveProviderHttpRequestConfigMock,
1617 sanitizeConfiguredModelProviderRequestMock,
1718} = getProviderHttpMocks();
@@ -304,11 +305,84 @@ describe("openai video generation provider", () => {
304305});
305306});
306307308+it("retries guarded local video downloads after transient HTTP errors", async () => {
309+const firstRelease = vi.fn(async () => {});
310+const secondRelease = vi.fn(async () => {});
311+assertOkOrThrowHttpErrorMock
312+.mockImplementationOnce(async () => {})
313+.mockImplementationOnce(async () => {})
314+.mockImplementationOnce(async (_response, label) => {
315+throw new Error(label);
316+})
317+.mockImplementationOnce(async () => {});
318+postJsonRequestMock.mockResolvedValue({
319+response: {
320+json: async () => ({
321+id: "vid_local",
322+model: "sora-2",
323+status: "queued",
324+}),
325+},
326+release: vi.fn(async () => {}),
327+});
328+fetchWithTimeoutMock.mockResolvedValueOnce({
329+json: async () => ({
330+id: "vid_local",
331+model: "sora-2",
332+status: "completed",
333+}),
334+});
335+fetchWithTimeoutGuardedMock
336+.mockResolvedValueOnce({
337+response: new Response("busy", { status: 503, statusText: "Service Unavailable" }),
338+finalUrl: "http://127.0.0.1:44080/v1/videos/vid_local/content?variant=video",
339+release: firstRelease,
340+})
341+.mockResolvedValueOnce({
342+response: {
343+headers: new Headers({ "content-type": "video/mp4" }),
344+arrayBuffer: async () => Buffer.from("mp4-bytes"),
345+},
346+finalUrl: "http://127.0.0.1:44080/v1/videos/vid_local/content?variant=video",
347+release: secondRelease,
348+});
349+350+const provider = buildOpenAIVideoGenerationProvider();
351+const result = await provider.generateVideo({
352+provider: "openai",
353+model: "sora-2",
354+prompt: "Render via local relay",
355+cfg: {
356+models: {
357+providers: {
358+openai: {
359+baseUrl: "http://127.0.0.1:44080/v1",
360+request: { allowPrivateNetwork: true },
361+models: [],
362+},
363+},
364+},
365+},
366+});
367+368+expect(result.videos[0]?.buffer.toString()).toBe("mp4-bytes");
369+expect(executeProviderOperationWithRetryMock).toHaveBeenCalledWith(
370+expect.objectContaining({ provider: "openai", stage: "download" }),
371+);
372+expect(fetchWithTimeoutGuardedMock).toHaveBeenCalledTimes(2);
373+expect(firstRelease).toHaveBeenCalledTimes(1);
374+expect(secondRelease).toHaveBeenCalledTimes(1);
375+});
376+307377it("releases guarded local video download requests when HTTP errors throw", async () => {
308-const release = vi.fn(async () => {});
378+const firstRelease = vi.fn(async () => {});
379+const secondRelease = vi.fn(async () => {});
309380assertOkOrThrowHttpErrorMock
310381.mockImplementationOnce(async () => {})
311382.mockImplementationOnce(async () => {})
383+.mockImplementationOnce(async (_response, label) => {
384+throw new Error(label);
385+})
312386.mockImplementationOnce(async (_response, label) => {
313387throw new Error(label);
314388});
@@ -329,11 +403,17 @@ describe("openai video generation provider", () => {
329403status: "completed",
330404}),
331405});
332-fetchWithTimeoutGuardedMock.mockResolvedValueOnce({
333-response: new Response("busy", { status: 503, statusText: "Service Unavailable" }),
334-finalUrl: "http://127.0.0.1:44080/v1/videos/vid_local/content?variant=video",
335- release,
336-});
406+fetchWithTimeoutGuardedMock
407+.mockResolvedValueOnce({
408+response: new Response("busy", { status: 503, statusText: "Service Unavailable" }),
409+finalUrl: "http://127.0.0.1:44080/v1/videos/vid_local/content?variant=video",
410+release: firstRelease,
411+})
412+.mockResolvedValueOnce({
413+response: new Response("busy", { status: 503, statusText: "Service Unavailable" }),
414+finalUrl: "http://127.0.0.1:44080/v1/videos/vid_local/content?variant=video",
415+release: secondRelease,
416+});
337417338418const provider = buildOpenAIVideoGenerationProvider();
339419await expect(
@@ -355,7 +435,9 @@ describe("openai video generation provider", () => {
355435}),
356436).rejects.toThrow("OpenAI video download failed");
357437358-expect(release).toHaveBeenCalledTimes(1);
438+expect(fetchWithTimeoutGuardedMock).toHaveBeenCalledTimes(2);
439+expect(firstRelease).toHaveBeenCalledTimes(1);
440+expect(secondRelease).toHaveBeenCalledTimes(1);
359441});
360442361443it("uses multipart input_reference for video-to-video uploads", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。