From f0e2f7b4f5ae61cf3ec08521e1c46f462ee3e6a0 Mon Sep 17 00:00:00 2001
From: Alix-007
Date: Mon, 29 Jun 2026 12:10:55 +0800
Subject: [PATCH] fix(openai): bound video create-submit response reads
Reviewed and accepted after live preflight: mergeable clean, checks passing, no unresolved review threads.
---
.../openai/video-generation-provider.test.ts | 116 ++++++++----------
.../openai/video-generation-provider.ts | 6 +-
2 files changed, 58 insertions(+), 64 deletions(-)
diff --git a/extensions/openai/video-generation-provider.test.ts b/extensions/openai/video-generation-provider.test.ts
index 1fc7dab48a8f..c9eedea0662d 100644
--- a/extensions/openai/video-generation-provider.test.ts
+++ b/extensions/openai/video-generation-provider.test.ts
@@ -104,6 +104,12 @@ function streamedVideoResponse(bytes: string): Response {
);
}
+// Response.json keeps object fixtures on the standard Response body path so the
+// create read exercises the byte-bounded reader instead of an unbounded res.json().
+function streamedJsonResponse(payload: unknown): Response {
+ return Response.json(payload);
+}
+
describe("openai video generation provider", () => {
it("declares explicit mode capabilities", () => {
expectExplicitVideoGenerationCapabilities(buildOpenAIVideoGenerationProvider());
@@ -178,13 +184,11 @@ describe("openai video generation provider", () => {
it("uses JSON for text-only Sora requests", async () => {
postJsonRequestMock.mockResolvedValue({
- response: {
- json: async () => ({
- id: "vid_123",
- model: "sora-2",
- status: "queued",
- }),
- },
+ response: streamedJsonResponse({
+ id: "vid_123",
+ model: "sora-2",
+ status: "queued",
+ }),
release: vi.fn(async () => {}),
});
fetchWithTimeoutMock
@@ -226,13 +230,11 @@ describe("openai video generation provider", () => {
it("rejects generated video downloads that exceed the configured media cap", async () => {
postJsonRequestMock.mockResolvedValue({
- response: {
- json: async () => ({
- id: "vid_too_large",
- model: "sora-2",
- status: "queued",
- }),
- },
+ response: streamedJsonResponse({
+ id: "vid_too_large",
+ model: "sora-2",
+ status: "queued",
+ }),
release: vi.fn(async () => {}),
});
fetchWithTimeoutMock
@@ -258,13 +260,11 @@ describe("openai video generation provider", () => {
it("uses JSON input_reference.image_url for image-to-video requests", async () => {
postJsonRequestMock.mockResolvedValue({
- response: {
- json: async () => ({
- id: "vid_456",
- model: "sora-2",
- status: "queued",
- }),
- },
+ response: streamedJsonResponse({
+ id: "vid_456",
+ model: "sora-2",
+ status: "queued",
+ }),
release: vi.fn(async () => {}),
});
fetchWithTimeoutMock
@@ -303,13 +303,11 @@ describe("openai video generation provider", () => {
it("keeps configured local baseUrl private-network blocked unless explicitly enabled", async () => {
postJsonRequestMock.mockResolvedValue({
- response: {
- json: async () => ({
- id: "vid_local",
- model: "sora-2",
- status: "queued",
- }),
- },
+ response: streamedJsonResponse({
+ id: "vid_local",
+ model: "sora-2",
+ status: "queued",
+ }),
release: vi.fn(async () => {}),
});
fetchWithTimeoutMock
@@ -351,13 +349,11 @@ describe("openai video generation provider", () => {
it("honors configured request allowPrivateNetwork for local video providers", async () => {
postJsonRequestMock.mockResolvedValue({
- response: {
- json: async () => ({
- id: "vid_local",
- model: "sora-2",
- status: "queued",
- }),
- },
+ response: streamedJsonResponse({
+ id: "vid_local",
+ model: "sora-2",
+ status: "queued",
+ }),
release: vi.fn(async () => {}),
});
fetchWithTimeoutMock
@@ -426,13 +422,11 @@ describe("openai video generation provider", () => {
})
.mockImplementationOnce(async () => {});
postJsonRequestMock.mockResolvedValue({
- response: {
- json: async () => ({
- id: "vid_local",
- model: "sora-2",
- status: "queued",
- }),
- },
+ response: streamedJsonResponse({
+ id: "vid_local",
+ model: "sora-2",
+ status: "queued",
+ }),
release: vi.fn(async () => {}),
});
fetchWithTimeoutMock.mockResolvedValueOnce({
@@ -497,13 +491,11 @@ describe("openai video generation provider", () => {
throw new Error(label);
});
postJsonRequestMock.mockResolvedValue({
- response: {
- json: async () => ({
- id: "vid_local",
- model: "sora-2",
- status: "queued",
- }),
- },
+ response: streamedJsonResponse({
+ id: "vid_local",
+ model: "sora-2",
+ status: "queued",
+ }),
release: vi.fn(async () => {}),
});
fetchWithTimeoutMock.mockResolvedValueOnce({
@@ -552,21 +544,20 @@ describe("openai video generation provider", () => {
it("uses the video edits endpoint for video-to-video uploads", async () => {
fetchWithTimeoutMock
- .mockResolvedValueOnce({
- ok: true,
- json: async () => ({
+ .mockResolvedValueOnce(
+ streamedJsonResponse({
id: "vid_789",
model: "sora-2",
status: "queued",
}),
- })
- .mockResolvedValueOnce({
- json: async () => ({
+ )
+ .mockResolvedValueOnce(
+ streamedJsonResponse({
id: "vid_789",
model: "sora-2",
status: "completed",
}),
- })
+ )
.mockResolvedValueOnce({
headers: new Headers({ "content-type": "video/mp4" }),
arrayBuffer: async () => Buffer.from("mp4-bytes"),
@@ -597,21 +588,20 @@ describe("openai video generation provider", () => {
it("honors configured request allowPrivateNetwork for multipart video uploads", async () => {
fetchWithTimeoutMock
- .mockResolvedValueOnce({
- ok: true,
- json: async () => ({
+ .mockResolvedValueOnce(
+ streamedJsonResponse({
id: "vid_789",
model: "sora-2",
status: "queued",
}),
- })
- .mockResolvedValueOnce({
- json: async () => ({
+ )
+ .mockResolvedValueOnce(
+ streamedJsonResponse({
id: "vid_789",
model: "sora-2",
status: "completed",
}),
- })
+ )
.mockResolvedValueOnce({
headers: new Headers({ "content-type": "video/mp4" }),
arrayBuffer: async () => Buffer.from("mp4-bytes"),
diff --git a/extensions/openai/video-generation-provider.ts b/extensions/openai/video-generation-provider.ts
index 88684631da44..74627710ccbd 100644
--- a/extensions/openai/video-generation-provider.ts
+++ b/extensions/openai/video-generation-provider.ts
@@ -12,6 +12,7 @@ import {
pollProviderOperationJson,
postJsonRequest,
postMultipartRequest,
+ readProviderJsonResponse,
resolveProviderOperationTimeoutMs,
resolveProviderHttpRequestConfig,
sanitizeConfiguredModelProviderRequest,
@@ -424,7 +425,10 @@ export function buildOpenAIVideoGenerationProvider(): VideoGenerationProvider {
try {
await assertOkOrThrowHttpError(response, "OpenAI video generation failed");
- const submitted = (await response.json()) as OpenAIVideoResponse;
+ const submitted = await readProviderJsonResponse(
+ response,
+ "OpenAI video generation failed",
+ );
const videoId = normalizeOptionalString(submitted.id);
if (!videoId) {
throw new Error("OpenAI video generation response missing video id");