From 8444f2a9e621ed39f19db1ee8da32797ccfc5f6e Mon Sep 17 00:00:00 2001 From: shrimbly Date: Thu, 5 Feb 2026 21:21:06 +1300 Subject: [PATCH] fix: restore backward compatibility for test mocks and FAL API key Update stale test mocks from currentNodeId to currentNodeIds in AudioInputNode, EaseCurveNode, and VideoStitchNode tests. Replace hard 401 return for missing FAL API key with console.warn, allowing requests to proceed without auth (rate-limited) since generateWithFal() already handles null keys gracefully. Co-Authored-By: Claude Opus 4.5 --- src/app/api/generate/__tests__/route.test.ts | 50 ++++++++++++++++--- src/app/api/generate/route.ts | 8 +-- .../__tests__/AudioInputNode.test.tsx | 2 +- .../__tests__/EaseCurveNode.test.tsx | 2 +- .../__tests__/VideoStitchNode.test.tsx | 2 +- 5 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/app/api/generate/__tests__/route.test.ts b/src/app/api/generate/__tests__/route.test.ts index ec8fd9e8..7940b21e 100644 --- a/src/app/api/generate/__tests__/route.test.ts +++ b/src/app/api/generate/__tests__/route.test.ts @@ -1720,9 +1720,30 @@ describe("/api/generate route", () => { expect(data.contentType).toBe("video"); }); - it("should return 401 without API key", async () => { + it("should proceed without API key (rate-limited)", async () => { delete process.env.FAL_API_KEY; + // Schema fetch (for input mapping when no dynamicInputs) + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ models: [] }), + }); + + // fal.run API call succeeds without auth + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + images: [{ url: "https://example.com/image.png", content_type: "image/png" }], + }), + }); + + // Image download + mockFetch.mockResolvedValueOnce({ + ok: true, + headers: new Headers({ "content-type": "image/png" }), + arrayBuffer: () => Promise.resolve(new ArrayBuffer(8)), + }); + const request = createMockPostRequest({ prompt: "A beautiful landscape", selectedModel: { @@ -1735,9 +1756,9 @@ describe("/api/generate route", () => { const response = await POST(request); const data = await response.json(); - expect(response.status).toBe(401); - expect(data.success).toBe(false); - expect(data.error).toContain("API key not configured"); + // Should proceed without key (no 401 early return) + expect(response.status).toBe(200); + expect(data.success).toBe(true); }); it("should handle rate limit (429) with API key", async () => { @@ -1775,9 +1796,22 @@ describe("/api/generate route", () => { expect(data.error).toContain("Try again in a moment"); }); - it("should return 401 for rate limit scenario without API key", async () => { + it("should handle rate limit (429) without API key", async () => { delete process.env.FAL_API_KEY; + // Schema fetch (for input mapping when no dynamicInputs) + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ models: [] }), + }); + + // fal.run returns 429 since no auth + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 429, + text: () => Promise.resolve(JSON.stringify({ detail: "Rate limit exceeded" })), + }); + const request = createMockPostRequest({ prompt: "Test prompt", selectedModel: { @@ -1790,10 +1824,10 @@ describe("/api/generate route", () => { const response = await POST(request); const data = await response.json(); - // Without API key, route returns 401 before reaching fal.ai - expect(response.status).toBe(401); + // Without API key, request proceeds but may get rate-limited by fal.ai + expect(response.status).toBe(500); expect(data.success).toBe(false); - expect(data.error).toContain("API key not configured"); + expect(data.error).toContain("Rate limit exceeded"); }); it("should handle image object response format", async () => { diff --git a/src/app/api/generate/route.ts b/src/app/api/generate/route.ts index 9e0a2bb1..2a43e8b4 100644 --- a/src/app/api/generate/route.ts +++ b/src/app/api/generate/route.ts @@ -2339,13 +2339,7 @@ export async function POST(request: NextRequest) { const falApiKey = request.headers.get("X-Fal-API-Key") || process.env.FAL_API_KEY || null; if (!falApiKey) { - return NextResponse.json( - { - success: false, - error: "fal.ai API key not configured. Add FAL_API_KEY to .env.local or configure in Settings.", - }, - { status: 401 } - ); + console.warn(`[API:${requestId}] No FAL API key configured. Proceeding without auth (rate-limited).`); } // For fal.ai, keep Data URIs as-is since localhost URLs won't work diff --git a/src/components/__tests__/AudioInputNode.test.tsx b/src/components/__tests__/AudioInputNode.test.tsx index 925c3121..4b38d537 100644 --- a/src/components/__tests__/AudioInputNode.test.tsx +++ b/src/components/__tests__/AudioInputNode.test.tsx @@ -89,7 +89,7 @@ function setMockStoreState(overrides: Record = {}) { edges: [], nodes: [], isRunning: false, - currentNodeId: null, + currentNodeIds: [], groups: {}, getNodesWithComments: vi.fn(() => []), markCommentViewed: vi.fn(), diff --git a/src/components/__tests__/EaseCurveNode.test.tsx b/src/components/__tests__/EaseCurveNode.test.tsx index 55398714..584b4e96 100644 --- a/src/components/__tests__/EaseCurveNode.test.tsx +++ b/src/components/__tests__/EaseCurveNode.test.tsx @@ -95,7 +95,7 @@ function setMockStoreState(overrides: Record = {}) { edges: [], nodes: [], isRunning: false, - currentNodeId: null, + currentNodeIds: [], groups: {}, getNodesWithComments: vi.fn(() => []), markCommentViewed: vi.fn(), diff --git a/src/components/__tests__/VideoStitchNode.test.tsx b/src/components/__tests__/VideoStitchNode.test.tsx index 2e8842c7..657928c5 100644 --- a/src/components/__tests__/VideoStitchNode.test.tsx +++ b/src/components/__tests__/VideoStitchNode.test.tsx @@ -82,7 +82,7 @@ function setMockStoreState(overrides: Record = {}) { edges: [], nodes: [], isRunning: false, - currentNodeId: null, + currentNodeIds: [], groups: {}, getNodesWithComments: vi.fn(() => []), markCommentViewed: vi.fn(),