Browse Source

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 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
8444f2a9e6
  1. 50
      src/app/api/generate/__tests__/route.test.ts
  2. 8
      src/app/api/generate/route.ts
  3. 2
      src/components/__tests__/AudioInputNode.test.tsx
  4. 2
      src/components/__tests__/EaseCurveNode.test.tsx
  5. 2
      src/components/__tests__/VideoStitchNode.test.tsx

50
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 () => {

8
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<GenerateResponse>(
{
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

2
src/components/__tests__/AudioInputNode.test.tsx

@ -89,7 +89,7 @@ function setMockStoreState(overrides: Record<string, unknown> = {}) {
edges: [],
nodes: [],
isRunning: false,
currentNodeId: null,
currentNodeIds: [],
groups: {},
getNodesWithComments: vi.fn(() => []),
markCommentViewed: vi.fn(),

2
src/components/__tests__/EaseCurveNode.test.tsx

@ -95,7 +95,7 @@ function setMockStoreState(overrides: Record<string, unknown> = {}) {
edges: [],
nodes: [],
isRunning: false,
currentNodeId: null,
currentNodeIds: [],
groups: {},
getNodesWithComments: vi.fn(() => []),
markCommentViewed: vi.fn(),

2
src/components/__tests__/VideoStitchNode.test.tsx

@ -82,7 +82,7 @@ function setMockStoreState(overrides: Record<string, unknown> = {}) {
edges: [],
nodes: [],
isRunning: false,
currentNodeId: null,
currentNodeIds: [],
groups: {},
getNodesWithComments: vi.fn(() => []),
markCommentViewed: vi.fn(),

Loading…
Cancel
Save