Browse Source

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 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
ecd73e61bb
  1. 32
      .planning/ROADMAP.md
  2. 13
      .planning/STATE.md
  3. 122
      .planning/phases/18-api-route-tests/18-01-PLAN.md
  4. 140
      .planning/phases/18-api-route-tests/18-02-PLAN.md
  5. 125
      .planning/phases/18-api-route-tests/18-03-PLAN.md
  6. 157
      .planning/phases/18-api-route-tests/18-04-PLAN.md
  7. 134
      .planning/phases/18-api-route-tests/18-05-PLAN.md

32
.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 | - |

13
.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)

122
.planning/phases/18-api-route-tests/18-01-PLAN.md

@ -0,0 +1,122 @@
---
phase: 18-api-route-tests
plan: 01
type: execute
---
<objective>
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.
</objective>
<execution_context>
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
</context>
<tasks>
<task type="auto">
<name>Task 1: Create workflow route tests</name>
<files>src/app/api/workflow/__tests__/route.test.ts</files>
<action>
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.
</action>
<verify>npm test -- src/app/api/workflow/__tests__/route.test.ts</verify>
<done>All workflow route tests pass covering POST save and GET validation</done>
</task>
<task type="auto">
<name>Task 2: Create save-generation route tests</name>
<files>src/app/api/save-generation/__tests__/route.test.ts</files>
<action>
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
</action>
<verify>npm test -- src/app/api/save-generation/__tests__/route.test.ts</verify>
<done>All save-generation route tests pass covering save, deduplication, and validation</done>
</task>
</tasks>
<verification>
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
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Tests cover validation, success, and error paths
- Mocking patterns established for fs and fetch
</success_criteria>
<output>
After completion, create `.planning/phases/18-api-route-tests/18-01-SUMMARY.md`
</output>

140
.planning/phases/18-api-route-tests/18-02-PLAN.md

@ -0,0 +1,140 @@
---
phase: 18-api-route-tests
plan: 02
type: execute
---
<objective>
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.
</objective>
<execution_context>
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
</context>
<tasks>
<task type="auto">
<name>Task 1: Create LLM route tests for Google provider</name>
<files>src/app/api/llm/__tests__/route.test.ts</files>
<action>
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;
```
</action>
<verify>npm test -- src/app/api/llm/__tests__/route.test.ts --testNamePattern="google"</verify>
<done>Google provider tests pass covering success, validation, and error paths</done>
</task>
<task type="auto">
<name>Task 2: Add LLM route tests for OpenAI provider</name>
<files>src/app/api/llm/__tests__/route.test.ts</files>
<action>
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.
</action>
<verify>npm test -- src/app/api/llm/__tests__/route.test.ts</verify>
<done>All LLM route tests pass covering both providers</done>
</task>
</tasks>
<verification>
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
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Both provider paths tested
- API mocking patterns work correctly
</success_criteria>
<output>
After completion, create `.planning/phases/18-api-route-tests/18-02-SUMMARY.md`
</output>

125
.planning/phases/18-api-route-tests/18-03-PLAN.md

@ -0,0 +1,125 @@
---
phase: 18-api-route-tests
plan: 03
type: execute
---
<objective>
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.
</objective>
<execution_context>
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
</context>
<tasks>
<task type="auto">
<name>Task 1: Create generate route tests for Gemini provider</name>
<files>src/app/api/generate/__tests__/route.test.ts</files>
<action>
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.
</action>
<verify>npm test -- src/app/api/generate/__tests__/route.test.ts --testNamePattern="Gemini"</verify>
<done>Gemini provider tests pass covering success, config options, and error paths</done>
</task>
<task type="auto">
<name>Task 2: Add generate route validation and edge case tests</name>
<files>src/app/api/generate/__tests__/route.test.ts</files>
<action>
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.
</action>
<verify>npm test -- src/app/api/generate/__tests__/route.test.ts</verify>
<done>All Gemini path tests pass including validation and edge cases</done>
</task>
</tasks>
<verification>
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
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Gemini path thoroughly tested
- Input validation tested
</success_criteria>
<output>
After completion, create `.planning/phases/18-api-route-tests/18-03-SUMMARY.md`
</output>

157
.planning/phases/18-api-route-tests/18-04-PLAN.md

@ -0,0 +1,157 @@
---
phase: 18-api-route-tests
plan: 04
type: execute
---
<objective>
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.
</objective>
<execution_context>
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
</context>
<tasks>
<task type="auto">
<name>Task 1: Add generate route tests for Replicate provider</name>
<files>src/app/api/generate/__tests__/route.test.ts</files>
<action>
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.
</action>
<verify>npm test -- src/app/api/generate/__tests__/route.test.ts --testNamePattern="Replicate"</verify>
<done>Replicate provider tests pass covering success, polling, and error paths</done>
</task>
<task type="auto">
<name>Task 2: Add generate route tests for fal.ai provider</name>
<files>src/app/api/generate/__tests__/route.test.ts</files>
<action>
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).
</action>
<verify>npm test -- src/app/api/generate/__tests__/route.test.ts</verify>
<done>All generate route tests pass covering all three providers</done>
</task>
</tasks>
<verification>
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
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- All three providers thoroughly tested
- Error handling tested for each provider
</success_criteria>
<output>
After completion, create `.planning/phases/18-api-route-tests/18-04-SUMMARY.md`
</output>

134
.planning/phases/18-api-route-tests/18-05-PLAN.md

@ -0,0 +1,134 @@
---
phase: 18-api-route-tests
plan: 05
type: execute
---
<objective>
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.
</objective>
<execution_context>
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
</context>
<tasks>
<task type="auto">
<name>Task 1: Create models route tests for basic functionality</name>
<files>src/app/api/models/__tests__/route.test.ts</files>
<action>
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,
}),
});
```
</action>
<verify>npm test -- src/app/api/models/__tests__/route.test.ts --testNamePattern="basic"</verify>
<done>Basic models route tests pass covering filtering and caching</done>
</task>
<task type="auto">
<name>Task 2: Add models route tests for caching and error handling</name>
<files>src/app/api/models/__tests__/route.test.ts</files>
<action>
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
</action>
<verify>npm test -- src/app/api/models/__tests__/route.test.ts</verify>
<done>All models route tests pass covering caching, pagination, and error handling</done>
</task>
</tasks>
<verification>
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
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Cache logic thoroughly tested
- Provider aggregation tested
</success_criteria>
<output>
After completion, create `.planning/phases/18-api-route-tests/18-05-SUMMARY.md`
</output>
Loading…
Cancel
Save