


















@@ -61,6 +61,24 @@ function streamedVideoResponse(bytes: string): Response {
6161);
6262}
636364+function jsonResponse(payload: unknown): Response {
65+return new Response(JSON.stringify(payload), {
66+headers: { "content-type": "application/json" },
67+});
68+}
69+70+function oversizedJsonResponse(): Response {
71+return new Response(
72+new ReadableStream({
73+pull(controller) {
74+controller.enqueue(new Uint8Array(1024 * 1024).fill(0x20));
75+},
76+cancel: vi.fn(),
77+}),
78+{ headers: { "content-type": "application/json" } },
79+);
80+}
81+6482describe("minimax video generation provider", () => {
6583it("declares explicit mode capabilities", () => {
6684const provider = buildMinimaxVideoGenerationProvider();
@@ -71,24 +89,22 @@ describe("minimax video generation provider", () => {
71897290it("creates a task, polls status, and downloads the generated video", async () => {
7391postJsonRequestMock.mockResolvedValue({
74-response: {
75-json: async () => ({
76-task_id: "task-123",
77-base_resp: { status_code: 0 },
78-}),
79-},
92+response: jsonResponse({
93+task_id: "task-123",
94+base_resp: { status_code: 0 },
95+}),
8096release: vi.fn(async () => {}),
8197});
8298fetchWithTimeoutMock
83-.mockResolvedValueOnce({
84-json: async () => ({
99+.mockResolvedValueOnce(
100+jsonResponse({
85101task_id: "task-123",
86102status: "Success",
87103video_url: "https://example.com/out.mp4",
88104file_id: "file-1",
89105base_resp: { status_code: 0 },
90106}),
91-})
107+)
92108.mockResolvedValueOnce({
93109headers: new Headers({ "content-type": "video/webm" }),
94110arrayBuffer: async () => Buffer.from("webm-bytes"),
@@ -117,23 +133,21 @@ describe("minimax video generation provider", () => {
117133118134it("rejects generated video downloads that exceed the configured media cap", async () => {
119135postJsonRequestMock.mockResolvedValue({
120-response: {
121-json: async () => ({
122-task_id: "task-too-large",
123-base_resp: { status_code: 0 },
124-}),
125-},
136+response: jsonResponse({
137+task_id: "task-too-large",
138+base_resp: { status_code: 0 },
139+}),
126140release: vi.fn(async () => {}),
127141});
128142fetchWithTimeoutMock
129-.mockResolvedValueOnce({
130-json: async () => ({
143+.mockResolvedValueOnce(
144+jsonResponse({
131145task_id: "task-too-large",
132146status: "Success",
133147video_url: "https://example.com/too-large.mp4",
134148base_resp: { status_code: 0 },
135149}),
136-})
150+)
137151.mockResolvedValueOnce(streamedVideoResponse("too-large"));
138152139153const provider = buildMinimaxVideoGenerationProvider();
@@ -149,33 +163,31 @@ describe("minimax video generation provider", () => {
149163150164it("downloads via file_id when the status response omits video_url", async () => {
151165postJsonRequestMock.mockResolvedValue({
152-response: {
153-json: async () => ({
154-task_id: "task-456",
155-base_resp: { status_code: 0 },
156-}),
157-},
166+response: jsonResponse({
167+task_id: "task-456",
168+base_resp: { status_code: 0 },
169+}),
158170release: vi.fn(async () => {}),
159171});
160172fetchWithTimeoutMock
161-.mockResolvedValueOnce({
162-json: async () => ({
173+.mockResolvedValueOnce(
174+jsonResponse({
163175task_id: "task-456",
164176status: "Success",
165177file_id: "file-9",
166178base_resp: { status_code: 0 },
167179}),
168-})
169-.mockResolvedValueOnce({
170-json: async () => ({
180+)
181+.mockResolvedValueOnce(
182+jsonResponse({
171183file: {
172184file_id: "file-9",
173185filename: "output_aigc.mp4",
174186download_url: "https://example.com/download.mp4",
175187},
176188base_resp: { status_code: 0 },
177189}),
178-})
190+)
179191.mockResolvedValueOnce({
180192headers: new Headers({ "content-type": "video/mp4" }),
181193arrayBuffer: async () => Buffer.from("mp4-bytes"),
@@ -197,25 +209,60 @@ describe("minimax video generation provider", () => {
197209expect(result.metadata?.videoUrl).toBeUndefined();
198210});
199211200-it("routes portal video generation through minimax-portal auth and HTTP config", async () => {
212+it("rejects oversized file_id metadata JSON before downloading", async () => {
201213postJsonRequestMock.mockResolvedValue({
202-response: {
203-json: async () => ({
204-task_id: "task-portal",
214+response: new Response(
215+JSON.stringify({
216+task_id: "task-metadata-too-large",
205217base_resp: { status_code: 0 },
206218}),
207-},
219+),
208220release: vi.fn(async () => {}),
209221});
210222fetchWithTimeoutMock
211-.mockResolvedValueOnce({
212-json: async () => ({
223+.mockResolvedValueOnce(
224+new Response(
225+JSON.stringify({
226+task_id: "task-metadata-too-large",
227+status: "Success",
228+file_id: "file-too-large",
229+base_resp: { status_code: 0 },
230+}),
231+),
232+)
233+.mockResolvedValueOnce(oversizedJsonResponse());
234+235+const provider = buildMinimaxVideoGenerationProvider();
236+await expect(
237+provider.generateVideo({
238+provider: "minimax",
239+model: "MiniMax-Hailuo-2.3",
240+prompt: "A fox sprints across snowy hills",
241+cfg: {},
242+}),
243+).rejects.toThrow("MiniMax generated video metadata: JSON response exceeds 16777216 bytes");
244+245+expectMinimaxFetchCall(1, "https://api.minimax.io/v1/files/retrieve?file_id=file-too-large");
246+expect(fetchWithTimeoutMock).toHaveBeenCalledTimes(2);
247+});
248+249+it("routes portal video generation through minimax-portal auth and HTTP config", async () => {
250+postJsonRequestMock.mockResolvedValue({
251+response: jsonResponse({
252+task_id: "task-portal",
253+base_resp: { status_code: 0 },
254+}),
255+release: vi.fn(async () => {}),
256+});
257+fetchWithTimeoutMock
258+.mockResolvedValueOnce(
259+jsonResponse({
213260task_id: "task-portal",
214261status: "Success",
215262video_url: "https://example.com/portal.mp4",
216263base_resp: { status_code: 0 },
217264}),
218-})
265+)
219266.mockResolvedValueOnce({
220267headers: new Headers({ "content-type": "video/mp4" }),
221268arrayBuffer: async () => Buffer.from("mp4-bytes"),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。