fix(openai): bound video create-submit response reads

Reviewed and accepted after live preflight: mergeable clean, checks passing, no unresolved review threads.
This commit is contained in:
Alix-007
2026-06-29 12:10:55 +08:00
committed by GitHub
parent 63b089383a
commit f0e2f7b4f5
2 changed files with 58 additions and 64 deletions
@@ -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"),
@@ -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<OpenAIVideoResponse>(
response,
"OpenAI video generation failed",
);
const videoId = normalizeOptionalString(submitted.id);
if (!videoId) {
throw new Error("OpenAI video generation response missing video id");