From ecd73e61bba3ca65ba32a7486c9fbc66f2b4b564 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 13 Jan 2026 14:42:41 +1300 Subject: [PATCH] docs(18): create API route tests plan Phase 18: API Route Tests - 5 plans created covering: - 18-01: File I/O routes (workflow, save-generation) - 18-02: LLM route (Google, OpenAI providers) - 18-03: Generate route (Gemini provider) - 18-04: Generate route (Replicate, fal.ai providers) - 18-05: Models route (caching, aggregation) - 10 total tasks defined - Ready for execution Co-Authored-By: Claude Opus 4.5 --- .planning/ROADMAP.md | 32 +++- .planning/STATE.md | 13 +- .../phases/18-api-route-tests/18-01-PLAN.md | 122 ++++++++++++++ .../phases/18-api-route-tests/18-02-PLAN.md | 140 ++++++++++++++++ .../phases/18-api-route-tests/18-03-PLAN.md | 125 ++++++++++++++ .../phases/18-api-route-tests/18-04-PLAN.md | 157 ++++++++++++++++++ .../phases/18-api-route-tests/18-05-PLAN.md | 134 +++++++++++++++ 7 files changed, 712 insertions(+), 11 deletions(-) create mode 100644 .planning/phases/18-api-route-tests/18-01-PLAN.md create mode 100644 .planning/phases/18-api-route-tests/18-02-PLAN.md create mode 100644 .planning/phases/18-api-route-tests/18-03-PLAN.md create mode 100644 .planning/phases/18-api-route-tests/18-04-PLAN.md create mode 100644 .planning/phases/18-api-route-tests/18-05-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 93e26cea..eaf86fb2 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -12,7 +12,7 @@ None - ✅ **v1.0 Multi-Provider Support** - Phases 1-6 (shipped 2026-01-11) - ✅ **v1.1 Improvements** - Phases 7-14 (shipped 2026-01-12) -- 🚧 **v1.2 Improvements** - Phases 15-21 (in progress) +- 🚧 **v1.2 Improvements** - Phases 15-22 (in progress) ## Phases @@ -230,13 +230,17 @@ Plans: #### Phase 18: API Route Tests -**Goal**: Add tests for generate, llm, and workflow API routes +**Goal**: Add tests for generate, llm, models, and workflow API routes **Depends on**: Phase 17 **Research**: Unlikely (Next.js API route testing patterns) -**Plans**: TBD +**Plans**: 5 plans Plans: -- [ ] 18-01: TBD (run /gsd:plan-phase 18 to break down) +- [ ] 18-01: File I/O routes (workflow, save-generation) +- [ ] 18-02: LLM route (Google, OpenAI providers) +- [ ] 18-03: Generate route (Gemini provider) +- [ ] 18-04: Generate route (Replicate, fal.ai providers) +- [ ] 18-05: Models route (caching, aggregation) #### Phase 19: Type Refactoring @@ -274,10 +278,27 @@ Plans: Plans: - [ ] 21-01: TBD (run /gsd:plan-phase 21 to break down) +#### Phase 22: Generate Node Dynamic Input Tests + +**Goal**: Test that generate nodes properly validate and render dynamic inputs from provider schemas, and that all inputs/parameters are correctly included in API calls +**Depends on**: Phase 21 +**Research**: Unlikely (existing test patterns from Phase 17) +**Plans**: TBD + +**Test coverage:** +1. Dynamic inputs from provider schemas render correctly as parameters and input handles on node creation +2. Validation of dynamic inputs (required fields, type checking, constraints) +3. Standard inputs (image, text) validate properly +4. On submission, all parameters and inputs are included in API call payload correctly +5. Coverage across all providers (Gemini, Replicate, fal.ai) + +Plans: +- [ ] 22-01: TBD (run /gsd:plan-phase 22 to break down) + ## Progress **Execution Order:** -Phases execute in numeric order: 1 → 2 → ... → 14 → 15 → 16 → 17 → 18 → 19 → 20 → 21 +Phases execute in numeric order: 1 → 2 → ... → 14 → 15 → 16 → 17 → 18 → 19 → 20 → 21 → 22 | Phase | Milestone | Plans Complete | Status | Completed | |-------|-----------|----------------|--------|-----------| @@ -302,3 +323,4 @@ Phases execute in numeric order: 1 → 2 → ... → 14 → 15 → 16 → 17 → | 19. Type Refactoring | v1.2 | 0/? | Not started | - | | 20. Integration Tests | v1.2 | 0/? | Not started | - | | 21. Fix Image Input & Deduplication | v1.2 | 0/? | Not started | - | +| 22. Generate Node Dynamic Input Tests | v1.2 | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 3e1a08e2..5821a1d8 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -9,10 +9,10 @@ See: .planning/PROJECT.md (updated 2026-01-09) ## Current Position -Phase: 17 of 21 (Component Tests) -Plan: 11 of 11 in current phase -Status: Phase complete -Last activity: 2026-01-13 - Completed 17-11-PLAN.md (Phase 17 complete!) +Phase: 18 of 22 (API Route Tests) +Plan: 0 of 5 in current phase +Status: Planning complete +Last activity: 2026-01-13 - Created 5 plans for Phase 18 Progress: █████████░ 91% @@ -123,10 +123,11 @@ Recent decisions affecting current work: - Phase 14 added: Fix drag-connect node creation bugs (consolidated from two phases) - Milestone v1.2 Improvements created: 6 phases (Phase 15-20), testing and modularization - Phase 21 added: Fix Gemini Pro image input handling and image deduplication issues +- Phase 22 added: Generate node dynamic input validation and API call tests ## Session Continuity Last session: 2026-01-13 -Stopped at: Completed Phase 17 (Component Tests) +Stopped at: Planned Phase 18 (API Route Tests) Resume file: None -Next action: Plan Phase 18 (run /gsd:plan-phase 18) +Next action: Execute Plan 18-01 (run /gsd:execute-plan .planning/phases/18-api-route-tests/18-01-PLAN.md) diff --git a/.planning/phases/18-api-route-tests/18-01-PLAN.md b/.planning/phases/18-api-route-tests/18-01-PLAN.md new file mode 100644 index 00000000..991f6e86 --- /dev/null +++ b/.planning/phases/18-api-route-tests/18-01-PLAN.md @@ -0,0 +1,122 @@ +--- +phase: 18-api-route-tests +plan: 01 +type: execute +--- + + +Add tests for workflow and save-generation API routes covering file I/O operations. + +Purpose: Establish API route testing patterns with file system mocking for subsequent plans. +Output: Test files for workflow and save-generation routes with fs mocking patterns. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +**Routes to test:** +@src/app/api/workflow/route.ts +@src/app/api/save-generation/route.ts + +**Test patterns reference:** +@src/store/utils/__tests__/localStorage.test.ts +@vitest.config.ts + + + + + + Task 1: Create workflow route tests + src/app/api/workflow/__tests__/route.test.ts + +Create tests for /api/workflow route covering: +- POST: Save workflow - validates required fields, creates directories, writes JSON +- POST: Rejects missing directoryPath/filename/workflow +- POST: Rejects non-directory paths +- POST: Handles directory creation for inputs/generations +- GET: Validates directory existence +- GET: Returns exists: false for non-existent paths + +Mock fs/promises (stat, mkdir, writeFile, readdir) with vi.mock. +Mock NextRequest/NextResponse using vitest patterns. +Use vi.spyOn for fs methods to control return values per test. + +Pattern for mocking Next.js API routes: +```typescript +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { NextRequest } from "next/server"; +import * as fs from "fs/promises"; + +vi.mock("fs/promises"); + +// Create mock request helper +function createMockRequest(body: unknown, method = "POST") { + return { + json: vi.fn().mockResolvedValue(body), + nextUrl: { searchParams: new URLSearchParams() }, + } as unknown as NextRequest; +} +``` + +Do NOT mock the entire route module - import and call POST/GET directly. + + npm test -- src/app/api/workflow/__tests__/route.test.ts + All workflow route tests pass covering POST save and GET validation + + + + Task 2: Create save-generation route tests + src/app/api/save-generation/__tests__/route.test.ts + +Create tests for /api/save-generation route covering: +- POST: Saves base64 image with hash-based filename +- POST: Saves base64 video with hash-based filename +- POST: Deduplicates existing files by hash suffix +- POST: Rejects missing directoryPath or content +- POST: Rejects non-directory paths +- POST: Handles data URL with various MIME types +- POST: Handles HTTP URLs (mock fetch for external URL) + +Mock fs/promises (stat, mkdir, writeFile, readdir) with vi.mock. +Mock global fetch for HTTP URL handling. +Use crypto.createHash for consistent hash generation in tests. + +Test the deduplication logic: +1. Mock readdir to return file with matching hash suffix +2. Verify no writeFile called, isDuplicate: true returned + +Test hash generation: +1. Provide known content, verify hash in filename +2. Use crypto.createHash('md5').update(buffer).digest('hex') to compute expected hash + + npm test -- src/app/api/save-generation/__tests__/route.test.ts + All save-generation route tests pass covering save, deduplication, and validation + + + + + +Before declaring plan complete: +- [ ] `npm test -- src/app/api/workflow/__tests__/route.test.ts` passes +- [ ] `npm test -- src/app/api/save-generation/__tests__/route.test.ts` passes +- [ ] Both test files use consistent mocking patterns +- [ ] No TypeScript errors in test files + + + +- All tasks completed +- All verification checks pass +- Tests cover validation, success, and error paths +- Mocking patterns established for fs and fetch + + + +After completion, create `.planning/phases/18-api-route-tests/18-01-SUMMARY.md` + diff --git a/.planning/phases/18-api-route-tests/18-02-PLAN.md b/.planning/phases/18-api-route-tests/18-02-PLAN.md new file mode 100644 index 00000000..f377f616 --- /dev/null +++ b/.planning/phases/18-api-route-tests/18-02-PLAN.md @@ -0,0 +1,140 @@ +--- +phase: 18-api-route-tests +plan: 02 +type: execute +--- + + +Add tests for LLM API route covering Google and OpenAI provider paths. + +Purpose: Test text generation API with provider mocking patterns. +Output: Comprehensive tests for /api/llm route with mocked external APIs. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +**Route to test:** +@src/app/api/llm/route.ts + +**Test patterns from previous plan:** +@src/app/api/workflow/__tests__/route.test.ts + + + + + + Task 1: Create LLM route tests for Google provider + src/app/api/llm/__tests__/route.test.ts + +Create tests for /api/llm route Google provider path: +- POST: Generates text with Google/Gemini successfully +- POST: Handles multimodal input (images + prompt) +- POST: Rejects missing prompt +- POST: Rejects missing API key (no env var, no header) +- POST: Returns 429 on rate limit errors +- POST: Returns 500 on API errors + +Mock @google/genai GoogleGenAI class: +```typescript +vi.mock("@google/genai", () => ({ + GoogleGenAI: vi.fn().mockImplementation(() => ({ + models: { + generateContent: vi.fn().mockResolvedValue({ + text: "Generated response text", + }), + }, + })), +})); +``` + +Mock process.env.GEMINI_API_KEY for auth tests. +Test X-Gemini-API-Key header takes precedence over env var. + +Test request structure: +```typescript +const mockRequest = { + json: vi.fn().mockResolvedValue({ + prompt: "Test prompt", + provider: "google", + model: "gemini-2.5-flash", + temperature: 0.7, + maxTokens: 1024, + }), + headers: new Headers(), +} as unknown as NextRequest; +``` + + npm test -- src/app/api/llm/__tests__/route.test.ts --testNamePattern="google" + Google provider tests pass covering success, validation, and error paths + + + + Task 2: Add LLM route tests for OpenAI provider + src/app/api/llm/__tests__/route.test.ts + +Add tests for /api/llm route OpenAI provider path to existing test file: +- POST: Generates text with OpenAI successfully +- POST: Handles vision input (images + prompt) +- POST: Rejects unknown provider +- POST: Rejects missing OpenAI API key +- POST: Returns 429 on rate limit errors +- POST: Handles OpenAI API error responses + +Mock global fetch for OpenAI API calls: +```typescript +const mockFetch = vi.fn(); +global.fetch = mockFetch; + +// Success response +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + choices: [{ message: { content: "OpenAI response" } }], + }), +}); + +// Error response +mockFetch.mockResolvedValueOnce({ + ok: false, + status: 401, + json: () => Promise.resolve({ + error: { message: "Invalid API key" }, + }), +}); +``` + +Test X-OpenAI-API-Key header takes precedence over env var. +Test that correct model ID is sent to OpenAI API. + + npm test -- src/app/api/llm/__tests__/route.test.ts + All LLM route tests pass covering both providers + + + + + +Before declaring plan complete: +- [ ] `npm test -- src/app/api/llm/__tests__/route.test.ts` passes +- [ ] Tests cover Google and OpenAI providers +- [ ] Error handling tested (rate limits, API errors, missing keys) +- [ ] No TypeScript errors + + + +- All tasks completed +- All verification checks pass +- Both provider paths tested +- API mocking patterns work correctly + + + +After completion, create `.planning/phases/18-api-route-tests/18-02-SUMMARY.md` + diff --git a/.planning/phases/18-api-route-tests/18-03-PLAN.md b/.planning/phases/18-api-route-tests/18-03-PLAN.md new file mode 100644 index 00000000..91f6a4f1 --- /dev/null +++ b/.planning/phases/18-api-route-tests/18-03-PLAN.md @@ -0,0 +1,125 @@ +--- +phase: 18-api-route-tests +plan: 03 +type: execute +--- + + +Add tests for generate API route covering Gemini provider path. + +Purpose: Test image generation with Gemini, the default and most common path. +Output: Tests for /api/generate Gemini path with mocked GoogleGenAI SDK. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +**Route to test:** +@src/app/api/generate/route.ts + +**Test patterns from previous plans:** +@src/app/api/llm/__tests__/route.test.ts + + + + + + Task 1: Create generate route tests for Gemini provider + src/app/api/generate/__tests__/route.test.ts + +Create tests for /api/generate route Gemini (default) path: +- POST: Generates image successfully with prompt only +- POST: Generates image with prompt and input images +- POST: Applies aspect ratio config +- POST: Applies resolution config for nano-banana-pro model +- POST: Applies Google Search tool for nano-banana-pro +- POST: Rejects missing prompt (when no images/dynamicInputs) +- POST: Returns 500 when API key missing +- POST: Returns 429 on rate limit errors +- POST: Handles no candidates in response +- POST: Handles text-only response (no image) + +Mock @google/genai GoogleGenAI class: +```typescript +vi.mock("@google/genai", () => ({ + GoogleGenAI: vi.fn().mockImplementation(() => ({ + models: { + generateContent: vi.fn().mockResolvedValue({ + candidates: [{ + content: { + parts: [{ + inlineData: { + mimeType: "image/png", + data: "base64ImageData", + }, + }], + }, + }], + }), + }, + })), +})); +``` + +Test default model selection (nano-banana-pro when not specified). +Test X-Gemini-API-Key header takes precedence over env var. +Test response structure matches GenerateResponse type. + + npm test -- src/app/api/generate/__tests__/route.test.ts --testNamePattern="Gemini" + Gemini provider tests pass covering success, config options, and error paths + + + + Task 2: Add generate route validation and edge case tests + src/app/api/generate/__tests__/route.test.ts + +Add validation and edge case tests to existing test file: +- POST: Accepts request with only images (image-to-image) +- POST: Accepts request with dynamicInputs containing prompt +- POST: Accepts request with dynamicInputs containing image frames +- POST: Handles large response payloads (warns but succeeds) +- POST: Extracts MIME type from data URL correctly +- POST: Falls back to image/png for raw base64 + +Test provider routing: +- POST: Routes to Gemini when no selectedModel provided +- POST: Routes to Gemini when selectedModel.provider is "gemini" + +Test the getInputMappingFromSchema helper (if exported or through behavior): +- Verify schema parsing maps generic names to model-specific params +- Test array detection for multi-image inputs + +Do NOT test Replicate/fal paths in this plan - those are in 18-04. + + npm test -- src/app/api/generate/__tests__/route.test.ts + All Gemini path tests pass including validation and edge cases + + + + + +Before declaring plan complete: +- [ ] `npm test -- src/app/api/generate/__tests__/route.test.ts` passes +- [ ] Tests cover Gemini provider path only (Replicate/fal in next plan) +- [ ] Config options tested (aspectRatio, resolution, googleSearch) +- [ ] Error handling tested +- [ ] No TypeScript errors + + + +- All tasks completed +- All verification checks pass +- Gemini path thoroughly tested +- Input validation tested + + + +After completion, create `.planning/phases/18-api-route-tests/18-03-SUMMARY.md` + diff --git a/.planning/phases/18-api-route-tests/18-04-PLAN.md b/.planning/phases/18-api-route-tests/18-04-PLAN.md new file mode 100644 index 00000000..44037934 --- /dev/null +++ b/.planning/phases/18-api-route-tests/18-04-PLAN.md @@ -0,0 +1,157 @@ +--- +phase: 18-api-route-tests +plan: 04 +type: execute +--- + + +Add tests for generate API route covering Replicate and fal.ai provider paths. + +Purpose: Complete generate route test coverage with external provider mocking. +Output: Tests for /api/generate Replicate and fal.ai paths with mocked fetch calls. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +**Route to test:** +@src/app/api/generate/route.ts + +**Existing tests to extend:** +@src/app/api/generate/__tests__/route.test.ts + + + + + + Task 1: Add generate route tests for Replicate provider + src/app/api/generate/__tests__/route.test.ts + +Add tests for /api/generate route Replicate path to existing test file: +- POST: Generates image successfully via Replicate +- POST: Generates video successfully via Replicate +- POST: Returns 401 when Replicate API key missing +- POST: Handles rate limit (429) from Replicate +- POST: Handles prediction failure +- POST: Handles prediction timeout (5 min max) +- POST: Polls for prediction completion +- POST: Returns video URL for large videos (>20MB) + +Mock global fetch for Replicate API calls: +```typescript +// Model info fetch +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + latest_version: { id: "version123", openapi_schema: {} }, + }), +}); + +// Create prediction +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + id: "prediction123", + status: "starting", + }), +}); + +// Poll prediction (succeeded) +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + id: "prediction123", + status: "succeeded", + output: ["https://replicate.delivery/output.png"], + }), +}); + +// Fetch output media +mockFetch.mockResolvedValueOnce({ + ok: true, + headers: new Headers({ "content-type": "image/png" }), + arrayBuffer: () => Promise.resolve(new ArrayBuffer(1024)), +}); +``` + +Test X-Replicate-API-Key header is passed correctly. +Test dynamicInputs are passed to prediction input. + + npm test -- src/app/api/generate/__tests__/route.test.ts --testNamePattern="Replicate" + Replicate provider tests pass covering success, polling, and error paths + + + + Task 2: Add generate route tests for fal.ai provider + src/app/api/generate/__tests__/route.test.ts + +Add tests for /api/generate route fal.ai path to existing test file: +- POST: Generates image successfully via fal.ai (images array response) +- POST: Generates video successfully via fal.ai (video object response) +- POST: Works without API key (fal.ai allows unauthenticated) +- POST: Handles rate limit (429) with and without key +- POST: Handles various response formats (image, images, video, output) +- POST: Returns video URL for large videos (>20MB) +- POST: Filters empty dynamicInputs values + +Mock global fetch for fal.ai API calls: +```typescript +// Sync API call (fal.run) +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + images: [{ url: "https://fal.media/output.png" }], + }), +}); + +// Fetch output media +mockFetch.mockResolvedValueOnce({ + ok: true, + headers: new Headers({ "content-type": "image/png" }), + arrayBuffer: () => Promise.resolve(new ArrayBuffer(1024)), +}); + +// Video response format +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + video: { url: "https://fal.media/output.mp4" }, + }), +}); +``` + +Test X-Fal-API-Key header is passed when provided. +Test getFalInputMapping schema parsing (via behavior). + + npm test -- src/app/api/generate/__tests__/route.test.ts + All generate route tests pass covering all three providers + + + + + +Before declaring plan complete: +- [ ] `npm test -- src/app/api/generate/__tests__/route.test.ts` passes +- [ ] Tests cover Replicate and fal.ai provider paths +- [ ] Polling logic tested for Replicate +- [ ] Various response formats tested for fal.ai +- [ ] No TypeScript errors + + + +- All tasks completed +- All verification checks pass +- All three providers thoroughly tested +- Error handling tested for each provider + + + +After completion, create `.planning/phases/18-api-route-tests/18-04-SUMMARY.md` + diff --git a/.planning/phases/18-api-route-tests/18-05-PLAN.md b/.planning/phases/18-api-route-tests/18-05-PLAN.md new file mode 100644 index 00000000..986954e0 --- /dev/null +++ b/.planning/phases/18-api-route-tests/18-05-PLAN.md @@ -0,0 +1,134 @@ +--- +phase: 18-api-route-tests +plan: 05 +type: execute +--- + + +Add tests for models API route covering caching and provider aggregation. + +Purpose: Test model discovery and caching logic for multi-provider support. +Output: Tests for /api/models route with mocked provider APIs and cache behavior. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +**Route to test:** +@src/app/api/models/route.ts + +**Cache module to mock:** +@src/lib/providers/cache.ts + + + + + + Task 1: Create models route tests for basic functionality + src/app/api/models/__tests__/route.test.ts + +Create tests for /api/models route core functionality: +- GET: Returns models from fal.ai when no Replicate key (fal.ai works without key) +- GET: Returns models from both providers when both keys present +- GET: Returns 400 when no providers available (Replicate needs key, fal.ai not tested) +- GET: Filters by provider query param +- GET: Filters by capabilities query param +- GET: Searches by query param +- GET: Returns cached=true when all from cache +- GET: Returns cached=false when fresh fetch + +Mock @/lib/providers/cache: +```typescript +vi.mock("@/lib/providers/cache", () => ({ + getCachedModels: vi.fn(), + setCachedModels: vi.fn(), + getCacheKey: vi.fn((provider, search) => `${provider}:${search || ""}`), +})); +``` + +Mock global fetch for provider API calls: +```typescript +// Replicate models response +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + results: [{ owner: "stability-ai", name: "sdxl", description: "SDXL" }], + next: null, + }), +}); + +// fal.ai models response +mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ + models: [{ + endpoint_id: "fal-ai/flux", + metadata: { display_name: "Flux", category: "text-to-image", description: "" }, + }], + has_more: false, + }), +}); +``` + + npm test -- src/app/api/models/__tests__/route.test.ts --testNamePattern="basic" + Basic models route tests pass covering filtering and caching + + + + Task 2: Add models route tests for caching and error handling + src/app/api/models/__tests__/route.test.ts + +Add caching and error tests to existing test file: +- GET: Returns cached models when available (no fetch) +- GET: Bypasses cache when refresh=true +- GET: Client-side filters Replicate models (caches full list, filters on read) +- GET: Handles partial provider failures gracefully +- GET: Returns 500 when all providers fail +- GET: Paginates through Replicate results (max 15 pages) +- GET: Paginates through fal.ai results (max 15 pages) + +Test capability inference for Replicate: +- Video keywords (video, animate, motion) → text-to-video +- Image-to-video keywords (img2vid, i2v) → image-to-video +- Image keywords → text-to-image + +Test fal.ai category mapping: +- Category maps directly to ModelCapability +- Non-relevant categories are filtered out + +Test sorting: +- Models sorted by provider, then by name + + npm test -- src/app/api/models/__tests__/route.test.ts + All models route tests pass covering caching, pagination, and error handling + + + + + +Before declaring plan complete: +- [ ] `npm test -- src/app/api/models/__tests__/route.test.ts` passes +- [ ] Cache behavior tested (hit, miss, refresh) +- [ ] Both providers tested +- [ ] Filtering and search tested +- [ ] Error handling tested +- [ ] No TypeScript errors + + + +- All tasks completed +- All verification checks pass +- Cache logic thoroughly tested +- Provider aggregation tested + + + +After completion, create `.planning/phases/18-api-route-tests/18-05-SUMMARY.md` +