diff --git a/.claude/agents/rest-agent.md b/.claude/agents/rest-agent.md new file mode 100644 index 0000000..b8bd04c --- /dev/null +++ b/.claude/agents/rest-agent.md @@ -0,0 +1,216 @@ +# Agent Purpose +This agent specializes in creating and editing .rest files for the REST Client VSCode extension (https://marketplace.visualstudio.com/items?itemName=humao.rest-client). The agent helps developers test and interact with REST APIs directly from VSCode. + +# Core Capabilities + +The agent MUST be proficient in: + +1. **HTTP Methods**: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS +2. **Request Bodies**: JSON, form data, multipart/form-data, XML, plain text +3. **Variables**: + - File-level variables + - Environment variables from .env files + - Dynamic variables extracted from responses + - System variables ({{$timestamp}}, {{$randomInt}}, {{$guid}}, etc.) +4. **Response Handling**: + - Extracting values from JSON responses + - Using response data in subsequent requests + - Chaining multiple requests in a workflow +5. **Authentication**: + - API keys in headers + - Bearer tokens + - Basic auth + - Custom auth schemes +6. **Headers**: Content-Type, Authorization, custom headers +7. **Query Parameters**: URL-encoded parameters +8. **Documentation Fetching**: Use WebFetch to get REST Client documentation when needed + +# REST Client Syntax Reference + +## Basic Request +```http +GET https://api.example.com/users +``` + +## Request with Headers +```http +POST https://api.example.com/users +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "John Doe", + "email": "john@example.com" +} +``` + +## Variables +```http +### Variables +@baseUrl = https://api.example.com +@apiKey = {{$dotenv API_KEY}} + +### Request using variables +GET {{baseUrl}}/users +X-API-Key: {{apiKey}} +``` + +## Dynamic Variables (Response Extraction) +```http +### Login to get token +POST {{baseUrl}}/auth/login +Content-Type: application/json + +{ + "username": "admin", + "password": "secret" +} + +### +@authToken = {{login.response.body.token}} + +### Use extracted token +GET {{baseUrl}}/protected +Authorization: Bearer {{authToken}} +``` + +## Form Data +```http +POST {{baseUrl}}/upload +Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW + +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="file"; filename="test.jpg" +Content-Type: image/jpeg + +< ./test.jpg +------WebKitFormBoundary7MA4YWxkTrZu0gW-- +``` + +## Request Separation +Use `###` to separate multiple requests in the same file. + +# Task Workflow + +When asked to create .rest files: + +1. **Understand Requirements**: Ask clarifying questions about: + - API endpoints needed + - Authentication method + - Request/response formats + - Variables needed from .env + - Workflow dependencies + +2. **Structure the File**: + - Start with variables section + - Group related requests together + - Add descriptive comments + - Use clear naming for dynamic variables + +3. **Implement Workflows**: + - Chain requests using response extraction + - Handle authentication tokens properly + - Add error handling examples + - Document expected responses + +4. **Best Practices**: + - Use environment variables for secrets + - Add comments explaining complex flows + - Include example responses in comments + - Group CRUD operations logically + +5. **Fetch Documentation**: + - When uncertain about syntax, use WebFetch to check: + - https://marketplace.visualstudio.com/items?itemName=humao.rest-client + - Search for specific features when needed + +# Example: Complete Workflow + +```http +### =========================================== +### Banatie API Testing Workflow +### =========================================== + +### Environment Variables +@baseUrl = http://localhost:3000 +@masterKey = {{$dotenv MASTER_KEY}} +@projectKey = {{$dotenv PROJECT_KEY}} + +### =========================================== +### 1. Health Check +### =========================================== +GET {{baseUrl}}/health + +### =========================================== +### 2. Create Project Key (Master Key Required) +### =========================================== +POST {{baseUrl}}/api/admin/keys +Content-Type: application/json +X-API-Key: {{masterKey}} + +{ + "type": "project", + "projectId": "test-project", + "name": "Test Project Key" +} + +### +@newProjectKey = {{$2.response.body.data.key}} + +### =========================================== +### 3. Generate Image +### =========================================== +POST {{baseUrl}}/api/v1/generations +Content-Type: application/json +X-API-Key: {{newProjectKey}} + +{ + "prompt": "A beautiful sunset over mountains", + "aspectRatio": "16:9", + "alias": "@test-sunset" +} + +### +@generationId = {{$3.response.body.data.id}} +@imageId = {{$3.response.body.data.outputImage.id}} + +### =========================================== +### 4. Get Generation Details +### =========================================== +GET {{baseUrl}}/api/v1/generations/{{generationId}} +X-API-Key: {{newProjectKey}} + +### =========================================== +### 5. List All Generations +### =========================================== +GET {{baseUrl}}/api/v1/generations?limit=10&offset=0 +X-API-Key: {{newProjectKey}} +``` + +# Agent Behavior + +- **Proactive**: Suggest improvements to API testing workflows +- **Thorough**: Include all necessary headers and parameters +- **Educational**: Explain REST Client syntax when creating files +- **Practical**: Focus on real-world API testing scenarios +- **Current**: Fetch documentation when uncertain about features + +# Tools Available + +- **Read**: Read existing .rest files +- **Write**: Create new .rest files +- **Edit**: Modify existing .rest files +- **Glob/Grep**: Find existing API-related files +- **WebFetch**: Fetch REST Client documentation +- **Bash**: Test API endpoints to verify .rest file correctness + +# Success Criteria + +A successful .rest file should: +1. Execute without syntax errors +2. Properly chain requests when needed +3. Use variables from .env for secrets +4. Include clear comments and structure +5. Cover the complete API workflow +6. Handle authentication correctly +7. Extract and use response data appropriately diff --git a/banatie-api-testing-requirements.md b/banatie-api-testing-requirements.md new file mode 100644 index 0000000..6da52e5 --- /dev/null +++ b/banatie-api-testing-requirements.md @@ -0,0 +1,1250 @@ +# Banatie API - Comprehensive Testing Requirements + +**Version:** 1.0 +**Target:** Claude Code +**Scope:** Complete API + DB validation +**Database Schema:** v2.0 +**API Requirements:** v2.0 + Final Refactoring + +--- + +## 🎯 Purpose + +This document provides comprehensive testing requirements for the Banatie API service. Tests must validate actual running service with real HTTP requests, actual file uploads/downloads, and real image generation - **NO mocks, stubs, or placeholders**. + +**Critical Requirements:** +- Tests call real running API service at `http://localhost:3000` +- All file operations use real files and MinIO storage +- Image generation uses real Google Gemini API +- Database operations use real PostgreSQL instance +- Test actual data flows from request → processing → storage → retrieval + +--- + +## 📁 Test Structure + +Tests are organized in `/tests/api/` folder: + +``` +tests/api/ +├── config.ts # API configuration, endpoints, test settings +├── utils.ts # Helper functions (api calls, file upload, etc.) +├── fixture/ +│ └── test-image.png # Test image file +├── 01-basic.ts # Basic CRUD operations +├── 02-flows.ts # Flow management and lifecycle +├── 03-aliases.ts # Alias resolution and management +├── 04-live.ts # Live URLs and caching +├── 05-edge-cases.ts # Error handling and edge cases +└── run-all.ts # Test runner script +``` + +**Existing Patterns to Follow:** +- Use `api()` helper for HTTP requests +- Use `uploadFile()` for multipart uploads +- Use `waitForGeneration()` for polling +- Use `testContext` to share data between tests +- Use `runTest()` for consistent test execution + +--- + +## 🔧 Test Environment Setup + +### Prerequisites +- API service running on `http://localhost:3000` +- PostgreSQL database initialized with schema v2.0 +- MinIO storage accessible and configured +- Valid API key in config.ts +- Google Gemini API credentials configured + +### Test Data Requirements +- Test image file: `tests/api/fixture/test-image.png` (PNG, ~100KB) +- Additional test images for reference scenarios +- Valid project context from API key + +--- + +## 📋 Test Coverage Matrix + +### Coverage Areas +1. **Images** - Upload, CRUD, alias management, resolution +2. **Generations** - Create, status tracking, regenerate, parameters +3. **Flows** - Lifecycle, lazy creation, alias management +4. **Aliases** - 3-tier resolution, technical aliases, conflicts +5. **Live URLs** - Scopes, caching, generation +6. **Reference Images** - Manual specification, auto-detection +7. **CDN Endpoints** - Image delivery, alias resolution +8. **Error Handling** - Validation, not found, conflicts + +--- + +## 📝 Detailed Test Scenarios + +### Test File: 01-basic.ts + +**Purpose:** Validate core CRUD operations and basic flows + +#### TEST GROUP 1: Image Upload and Management + +**Test 1.1: Upload image with project alias** +```typescript +Purpose: Verify basic image upload with project-scoped alias +Steps: +1. Upload test-image.png with alias "@test-logo" +2. Verify response contains imageId, storageKey, storageUrl +3. Verify alias is set correctly +4. Verify source = "uploaded" +5. Save imageId to testContext.uploadedImageId +Expected: +- HTTP 201 +- Valid image record +- File accessible in MinIO +- Alias "@test-logo" assigned +``` + +**Test 1.2: Upload image without alias** +```typescript +Purpose: Verify upload works without alias assignment +Steps: +1. Upload test image without alias parameter +2. Verify response contains image data +3. Verify alias is null +Expected: +- HTTP 201 +- Valid image record +- No alias assigned +``` + +**Test 1.3: List uploaded images** +```typescript +Purpose: Verify image listing endpoint +Steps: +1. GET /api/v1/images +2. Verify response is array +3. Verify pagination data present +4. Verify uploaded images included +5. Check source = "uploaded" filter works +Expected: +- HTTP 200 +- Array of images +- Pagination metadata +``` + +**Test 1.4: Get image by ID** +```typescript +Purpose: Verify image retrieval by UUID +Steps: +1. GET /api/v1/images/{uploadedImageId} +2. Verify all image fields present +3. Verify storageUrl is accessible +Expected: +- HTTP 200 +- Complete image data +- Accessible storage URL +``` + +**Test 1.5: Get image by alias (project-scoped)** +```typescript +Purpose: Verify image retrieval by alias +Steps: +1. GET /api/v1/images/@test-logo +2. Verify returns same image as by ID +3. Verify alias resolution works +Expected: +- HTTP 200 +- Correct image data +``` + +**Test 1.6: Update image metadata** +```typescript +Purpose: Verify image metadata updates +Steps: +1. PUT /api/v1/images/{imageId} +2. Update description, tags, focalPoint +3. GET image again to verify changes +Expected: +- HTTP 200 +- Updated fields reflected +``` + +**Test 1.7: Update image alias** +```typescript +Purpose: Verify alias assignment/change via dedicated endpoint +Steps: +1. PUT /api/v1/images/{imageId}/alias +2. Change alias from "@test-logo" to "@new-logo" +3. GET /api/v1/images/@new-logo +4. Verify old alias no longer works +Expected: +- HTTP 200 +- New alias works +- Old alias returns 404 +``` + +**Test 1.8: Remove image alias** +```typescript +Purpose: Verify alias removal +Steps: +1. PUT /api/v1/images/{imageId}/alias with { alias: null } +2. Verify image exists but has no alias +3. Verify alias query returns 404 +Expected: +- HTTP 200 +- Image exists without alias +- Alias query fails properly +``` + +#### TEST GROUP 2: Basic Image Generation + +**Test 2.1: Generate image without references (simple)** +```typescript +Purpose: Verify basic generation functionality +Steps: +1. POST /api/v1/generations + { + prompt: "A beautiful sunset over mountains", + aspectRatio: "16:9" + } +2. Verify generation record created +3. Poll for completion using waitForGeneration() +4. Verify status = "success" +5. Verify outputImageId present +6. Download and save generated image +7. Verify image exists in MinIO +Expected: +- HTTP 200 on creation +- Generation completes successfully +- Output image accessible +- processingTimeMs > 0 +``` + +**Test 2.2: Generate with manual reference image** +```typescript +Purpose: Verify generation with explicitly specified reference +Steps: +1. POST /api/v1/generations + { + prompt: "A product photo with the logo in corner", + referenceImages: ["@test-logo"], + aspectRatio: "1:1" + } +2. Wait for completion +3. Verify referencedImages field contains correct data +4. Verify output image generated +Expected: +- HTTP 200 +- Generation successful +- Referenced images tracked correctly +``` + +**Test 2.3: Generate with auto-detected references** +```typescript +Purpose: Verify automatic alias detection in prompts +Steps: +1. POST /api/v1/generations + { + prompt: "Create product image using @test-logo and @brand-colors", + aspectRatio: "4:3" + } + # Note: referenceImages NOT provided +2. Verify system auto-detected both aliases +3. Check referencedImages field +Expected: +- HTTP 200 +- Both aliases auto-detected +- Generation uses both references +``` + +**Test 2.4: List generations** +```typescript +Purpose: Verify generation listing with filters +Steps: +1. GET /api/v1/generations +2. Verify array returned +3. Test pagination parameters +4. Test status filter +5. Test sortBy and order +Expected: +- HTTP 200 +- Filtered results +- Pagination works +``` + +**Test 2.5: Get generation details** +```typescript +Purpose: Verify detailed generation retrieval +Steps: +1. GET /api/v1/generations/{generationId} +2. Verify includes: + - Generation data + - Output image data + - Referenced images array + - Processing metrics +Expected: +- HTTP 200 +- Complete generation details +``` + +#### TEST GROUP 3: Generation with Aliases + +**Test 3.1: Generate with project alias assignment** +```typescript +Purpose: Verify alias parameter assigns project-scoped alias +Steps: +1. POST /api/v1/generations + { + prompt: "Brand header image", + aspectRatio: "21:9", + alias: "@header" + } +2. Wait for completion +3. Verify output image has alias "@header" +4. GET /api/v1/images/@header +5. Verify returns generated image +Expected: +- HTTP 200 +- Alias assigned to output image +- Alias resolution works +``` + +**Test 3.2: Alias conflict resolution** +```typescript +Purpose: Verify new generation overwrites existing alias +Steps: +1. Generate image with alias "@hero" +2. Generate another image with same alias "@hero" +3. Verify second generation overwrites +4. Verify first image still exists but without alias +5. Verify "@hero" resolves to second image +Expected: +- Both generations successful +- Second image gets alias +- First image loses alias +- Both images preserved +``` + +--- + +### Test File: 02-flows.ts + +**Purpose:** Validate flow lifecycle and lazy creation patterns + +#### TEST GROUP 4: Flow Lazy Creation + +**Test 4.1: Generate without flowId returns flowId** +```typescript +Purpose: Verify lazy flow pattern - generation without flowId gets one +Steps: +1. POST /api/v1/generations (no flowId parameter) +2. Verify response includes generated flowId +3. Verify flowId is valid UUID format +4. Verify flow NOT yet in database (lazy) +Expected: +- HTTP 200 +- flowId present in response +- Flow record not created yet +``` + +**Test 4.2: Second request with flowId creates flow** +```typescript +Purpose: Verify flow created on second use +Steps: +1. Get flowId from previous generation +2. POST /api/v1/generations with this flowId +3. Verify flow now exists in database +4. GET /api/v1/flows/{flowId} +5. Verify flow contains both generations +Expected: +- HTTP 200 +- Flow record created +- Both generations linked +``` + +**Test 4.3: Flow created immediately with flowAlias** +```typescript +Purpose: Verify eager creation when flowAlias present +Steps: +1. POST /api/v1/generations + { + prompt: "Hero image", + flowAlias: "@hero" + } +2. Verify response includes flowId +3. GET /api/v1/flows/{flowId} +4. Verify flow exists immediately +5. Verify flow.aliases contains "@hero" +Expected: +- HTTP 200 +- Flow created immediately +- Flow alias set +``` + +**Test 4.4: Upload with flowId and flowAlias** +```typescript +Purpose: Verify uploads work with flow association +Steps: +1. Upload image with: + - flowId: (from previous test) + - flowAlias: "@upload-test" +2. Verify image linked to flow +3. GET flow details +4. Verify image appears in flow +5. Verify flowAlias in flow.aliases +Expected: +- HTTP 201 +- Image linked to flow +- Flow alias set +``` + +#### TEST GROUP 5: Flow Management + +**Test 5.1: List flows** +```typescript +Purpose: Verify flow listing endpoint +Steps: +1. GET /api/v1/flows +2. Verify array returned +3. Check computed fields: + - generationCount + - imageCount +4. Test pagination +Expected: +- HTTP 200 +- Array of flows with counts +``` + +**Test 5.2: Get flow details with generations** +```typescript +Purpose: Verify complete flow data retrieval +Steps: +1. GET /api/v1/flows/{flowId} +2. Verify response includes: + - Flow metadata + - All generations (chronological) + - All images + - Resolved aliases +Expected: +- HTTP 200 +- Complete flow data +- Chronological order maintained +``` + +**Test 5.3: Update flow aliases** +```typescript +Purpose: Verify manual alias updates +Steps: +1. PUT /api/v1/flows/{flowId}/aliases + { + aliases: { + "@custom": "image-uuid", + "@another": "image-uuid-2" + } + } +2. GET flow details +3. Verify aliases updated +Expected: +- HTTP 200 +- Aliases persisted correctly +``` + +**Test 5.4: Remove specific flow alias** +```typescript +Purpose: Verify alias deletion +Steps: +1. DELETE /api/v1/flows/{flowId}/aliases/@custom +2. GET flow details +3. Verify alias removed +4. Verify other aliases intact +Expected: +- HTTP 204 +- Specified alias removed +- Other aliases preserved +``` + +**Test 5.5: Delete flow with cascade** +```typescript +Purpose: Verify flow deletion behavior +Steps: +1. Create flow with: + - Generation with no alias + - Generation with project alias + - Upload with no alias + - Upload with project alias +2. DELETE /api/v1/flows/{flowId} +3. Verify: + - Flow record deleted + - Non-aliased images deleted + - Aliased images preserved (flowId = null) + - Generations deleted +Expected: +- HTTP 204 +- Correct cascade behavior +- Aliased resources protected +``` + +--- + +### Test File: 03-aliases.ts + +**Purpose:** Validate 3-tier alias resolution system + +#### TEST GROUP 6: Technical Aliases + +**Test 6.1: @last alias resolution** +```typescript +Purpose: Verify @last resolves to most recent generation +Steps: +1. Create flow with flowId +2. Generate image A +3. Generate image B +4. Generate image C +5. GET /api/v1/images/resolve/@last?flowId={flowId} +6. Verify returns image C +Expected: +- HTTP 200 +- Returns most recent generation +- Correct scope indicated +``` + +**Test 6.2: @first alias resolution** +```typescript +Purpose: Verify @first resolves to first generation +Steps: +1. Using same flow from Test 6.1 +2. GET /api/v1/images/resolve/@first?flowId={flowId} +3. Verify returns image A +Expected: +- HTTP 200 +- Returns first generation +``` + +**Test 6.3: @upload alias resolution** +```typescript +Purpose: Verify @upload resolves to last upload +Steps: +1. Upload image X to flow +2. Generate image Y +3. Upload image Z to flow +4. GET /api/v1/images/resolve/@upload?flowId={flowId} +5. Verify returns image Z +Expected: +- HTTP 200 +- Returns last uploaded image +``` + +**Test 6.4: Technical alias in generation prompt** +```typescript +Purpose: Verify technical aliases work in prompt +Steps: +1. POST /api/v1/generations + { + prompt: "New variation based on @last", + flowId: "{flowId}" + } +2. Verify @last resolved correctly +3. Verify referencedImages contains correct imageId +Expected: +- HTTP 200 +- Technical alias resolved +- Correct reference used +``` + +#### TEST GROUP 7: Alias Priority and Resolution + +**Test 7.1: Flow alias overrides project alias** +```typescript +Purpose: Verify flow-scoped takes precedence over project-scoped +Steps: +1. Create image with project alias "@logo" +2. Create flow +3. Upload different image with flowAlias "@logo" in this flow +4. GET /api/v1/images/resolve/@logo?flowId={flowId} +5. Verify returns flow-scoped image +6. GET /api/v1/images/resolve/@logo (no flowId) +7. Verify returns project-scoped image +Expected: +- Flow-scoped has priority when flowId provided +- Project-scoped used when no flowId +``` + +**Test 7.2: Technical alias highest priority** +```typescript +Purpose: Verify technical aliases override user aliases +Steps: +1. Create flow +2. Upload image with flowAlias "@last" +3. Generate image (becomes actual @last) +4. GET /api/v1/images/resolve/@last?flowId={flowId} +5. Verify returns generated image, not uploaded +Expected: +- Technical @last takes priority +- User-assigned "@last" ignored +``` + +**Test 7.3: Alias resolution without flow context** +```typescript +Purpose: Verify project-scoped-only resolution +Steps: +1. GET /api/v1/images/resolve/@logo (no flowId param) +2. Verify returns project-scoped alias +3. Verify scope = "project" +Expected: +- HTTP 200 +- Project alias resolved +``` + +**Test 7.4: Reserved alias validation** +```typescript +Purpose: Verify reserved aliases rejected +Steps: +1. Try to assign alias "@last" to image +2. Try to assign alias "@first" to image +3. Try to assign alias "@upload" to image +Expected: +- HTTP 400 for all +- Clear error messages +- Validation prevents reserved aliases +``` + +#### TEST GROUP 8: Alias Conflicts + +**Test 8.1: Project alias reassignment** +```typescript +Purpose: Verify alias can be moved between images +Steps: +1. Upload image A with alias "@product" +2. Verify A has alias +3. Upload image B with alias "@product" +4. Verify B now has alias +5. Verify A exists but alias is null +6. GET /api/v1/images/@product +7. Verify returns image B +Expected: +- Alias successfully moved +- Old image preserved without alias +``` + +**Test 8.2: Flow alias reassignment** +```typescript +Purpose: Verify flow-scoped alias reassignment +Steps: +1. Create flow +2. Upload image X with flowAlias "@hero" +3. Upload image Y with same flowAlias "@hero" +4. GET flow details +5. Verify flow.aliases["@hero"] = imageY.id +6. Verify imageX still exists in flow +Expected: +- Flow alias reassigned +- Both images in flow +``` + +**Test 8.3: Same alias in different flows** +```typescript +Purpose: Verify flow isolation for aliases +Steps: +1. Create flowA, upload image with flowAlias "@hero" +2. Create flowB, upload different image with flowAlias "@hero" +3. Resolve @hero in flowA +4. Resolve @hero in flowB +5. Verify different images returned +Expected: +- Same alias works independently in different flows +- Correct isolation maintained +``` + +--- + +### Test File: 04-live.ts + +**Purpose:** Validate live URL system and caching + +#### TEST GROUP 9: Live Scope Management + +**Test 9.1: Create scope manually** +```typescript +Purpose: Verify manual scope creation +Steps: +1. POST /api/v1/live/scopes + { + slug: "hero-section", + allowNewGenerations: true, + newGenerationsLimit: 50 + } +2. Verify scope created +3. GET /api/v1/live/scopes +4. Verify scope in list +Expected: +- HTTP 201 +- Scope created with settings +``` + +**Test 9.2: List scopes with stats** +```typescript +Purpose: Verify scope listing includes usage stats +Steps: +1. GET /api/v1/live/scopes +2. Verify each scope includes: + - currentGenerations count + - lastGeneratedAt timestamp + - Settings (allowNewGenerations, limit) +Expected: +- HTTP 200 +- Complete scope data with stats +``` + +**Test 9.3: Get scope details** +```typescript +Purpose: Verify detailed scope retrieval +Steps: +1. GET /api/v1/live/scopes/hero-section +2. Verify includes: + - Scope settings + - Usage statistics + - List of images in scope +Expected: +- HTTP 200 +- Complete scope information +``` + +**Test 9.4: Update scope settings** +```typescript +Purpose: Verify scope configuration changes +Steps: +1. PUT /api/v1/live/scopes/hero-section + { + allowNewGenerations: false, + newGenerationsLimit: 100 + } +2. GET scope details +3. Verify settings updated +Expected: +- HTTP 200 +- Settings persisted +``` + +#### TEST GROUP 10: Live URL Generation and Caching + +**Test 10.1: First live URL request (cache miss)** +```typescript +Purpose: Verify live URL triggers generation on first hit +Steps: +1. GET /cdn/{org}/{project}/live/hero-section?prompt=sunset&aspectRatio=16:9 +2. Verify: + - Generation triggered + - Image returned + - Headers include X-Cache-Status: MISS + - Content-Type: image/jpeg +3. Verify cache entry created in database +Expected: +- HTTP 200 +- Image bytes returned +- Cache miss indicated +- Cache entry persisted +``` + +**Test 10.2: Second live URL request (cache hit)** +```typescript +Purpose: Verify caching works on subsequent requests +Steps: +1. GET same URL as Test 10.1 +2. Verify: + - Same image returned immediately + - X-Cache-Status: HIT + - No new generation triggered +3. Check cache hit_count incremented +Expected: +- HTTP 200 +- Cached image returned +- Cache hit recorded +``` + +**Test 10.3: Live URL with underscores in prompt** +```typescript +Purpose: Verify URL encoding flexibility +Steps: +1. GET /cdn/{org}/{project}/live/test-scope?prompt=beautiful_sunset +2. Verify works same as %20 encoding +3. Generate with both formats +4. Verify same cache key used +Expected: +- HTTP 200 +- Both formats work +- Same cached result +``` + +**Test 10.4: Live URL scope auto-creation** +```typescript +Purpose: Verify new scope created if allowNewLiveScopes=true +Steps: +1. Ensure project allows new scopes +2. GET /cdn/{org}/{project}/live/new-auto-scope?prompt=test +3. Verify scope auto-created +4. GET /api/v1/live/scopes +5. Verify new-auto-scope in list +Expected: +- HTTP 200 +- Scope created automatically +- Generation successful +``` + +**Test 10.5: Live URL generation limit enforcement** +```typescript +Purpose: Verify scope limits respected +Steps: +1. Create scope with newGenerationsLimit: 2 +2. Make 2 live URL requests with different prompts +3. Verify both work +4. Make 3rd request with new prompt +5. Verify rejected with 429 +Expected: +- First 2 succeed +- 3rd request fails +- Error indicates limit exceeded +``` + +**Test 10.6: Live URL with disabled scope** +```typescript +Purpose: Verify allowNewGenerations setting enforced +Steps: +1. Create scope with allowNewGenerations: false +2. Add one cached image to scope +3. Request cached image (should work) +4. Request new image (should fail) +Expected: +- Cached images accessible +- New generations blocked +- HTTP 403 for new generation +``` + +**Test 10.7: Regenerate scope image** +```typescript +Purpose: Verify scope image regeneration +Steps: +1. POST /api/v1/live/scopes/{slug}/regenerate + { imageId: "uuid" } +2. Verify image regenerated +3. Verify cache updated +4. GET live URL for that image +5. Verify new version returned +Expected: +- HTTP 200 +- Image regenerated +- Cache updated +``` + +--- + +### Test File: 05-edge-cases.ts + +**Purpose:** Validate error handling and edge cases + +#### TEST GROUP 11: Validation and Errors + +**Test 11.1: Invalid alias format** +```typescript +Purpose: Verify alias validation +Steps: +1. Try alias without @ symbol +2. Try alias with spaces +3. Try alias with special chars +4. Try empty alias +Expected: +- HTTP 400 for all +- Clear validation errors +``` + +**Test 11.2: Invalid aspectRatio** +```typescript +Purpose: Verify aspect ratio validation +Steps: +1. POST generation with aspectRatio: "invalid" +2. POST generation with aspectRatio: "99:1" +3. POST generation with aspectRatio: "" +Expected: +- HTTP 400 +- Validation error messages +``` + +**Test 11.3: Missing required fields** +```typescript +Purpose: Verify required field validation +Steps: +1. POST generation without prompt +2. POST upload without file +3. POST scope without slug +Expected: +- HTTP 400 +- Field-specific errors +``` + +**Test 11.4: Non-existent resource access** +```typescript +Purpose: Verify 404 handling +Steps: +1. GET /api/v1/images/{fake-uuid} +2. GET /api/v1/generations/{fake-uuid} +3. GET /api/v1/flows/{fake-uuid} +4. GET /api/v1/images/@nonexistent +Expected: +- HTTP 404 for all +- Clear error messages +``` + +**Test 11.5: File upload size limit** +```typescript +Purpose: Verify file size validation +Steps: +1. Create file > 5MB +2. Try to upload +Expected: +- HTTP 400 +- File size error +``` + +**Test 11.6: Invalid file type** +```typescript +Purpose: Verify MIME type validation +Steps: +1. Upload .txt file as image +2. Upload .pdf file +Expected: +- HTTP 400 +- File type error +``` + +#### TEST GROUP 12: Regenerate Functionality + +**Test 12.1: Regenerate successful generation** +```typescript +Purpose: Verify regeneration works for any status +Steps: +1. Create successful generation +2. POST /api/v1/generations/{id}/regenerate +3. Verify: + - Same imageId used + - File overwritten in MinIO + - updatedAt changed + - createdAt preserved +Expected: +- HTTP 200 +- Image updated in place +``` + +**Test 12.2: Regenerate failed generation** +```typescript +Purpose: Verify regeneration works for failed +Steps: +1. Create generation that fails (bad prompt or force fail) +2. POST regenerate +3. Verify new attempt made +Expected: +- HTTP 200 +- New generation attempt +``` + +**Test 12.3: Regenerate preserves aliases** +```typescript +Purpose: Verify aliases maintained on regenerate +Steps: +1. Create generation with alias "@test" +2. Regenerate +3. Verify alias still "@test" +4. Verify alias resolution works +Expected: +- Alias preserved +- Resolution unchanged +``` + +**Test 12.4: Regenerate flow's last generation** +```typescript +Purpose: Verify flow regenerate endpoint +Steps: +1. Create flow with 3 generations +2. POST /api/v1/flows/{flowId}/regenerate +3. Verify last generation regenerated +Expected: +- HTTP 200 +- Last generation updated +``` + +**Test 12.5: Regenerate empty flow** +```typescript +Purpose: Verify error handling for empty flow +Steps: +1. Create empty flow (or delete all generations) +2. POST /api/v1/flows/{flowId}/regenerate +Expected: +- HTTP 400 or 404 +- Error: "Flow has no generations" +``` + +#### TEST GROUP 13: CDN Endpoints + +**Test 13.1: CDN image by filename** +```typescript +Purpose: Verify CDN endpoint for filename +Steps: +1. Upload image, note filename from storageKey +2. GET /cdn/{org}/{project}/img/{filename} +3. Verify image bytes returned +Expected: +- HTTP 200 +- Image content +- Proper headers +``` + +**Test 13.2: CDN image by alias** +```typescript +Purpose: Verify CDN endpoint with alias +Steps: +1. Upload image with alias "@cdn-test" +2. GET /cdn/{org}/{project}/img/@cdn-test +3. Verify image returned +Expected: +- HTTP 200 +- Correct image +- Cache headers +``` + +**Test 13.3: CDN alias priority** +```typescript +Purpose: Verify alias takes precedence over filename +Steps: +1. Create image with filename "test.png" and alias "@test" +2. Create another image with filename "test.png" but no alias +3. GET /cdn/{org}/{project}/img/@test +4. Verify returns aliased image +Expected: +- Alias resolution preferred +- Correct image returned +``` + +**Test 13.4: CDN with flowId context** +```typescript +Purpose: Verify flow-scoped CDN resolution +Steps: +1. Create flow with flowAlias "@flow-cdn" +2. GET /cdn/{org}/{project}/img/@flow-cdn?flowId={id} +3. Verify flow-scoped image returned +Expected: +- HTTP 200 +- Flow context respected +``` + +#### TEST GROUP 14: Concurrent Operations + +**Test 14.1: Concurrent generations in same flow** +```typescript +Purpose: Verify race conditions handled +Steps: +1. Start 3 generations simultaneously with same flowId +2. Wait for all to complete +3. Verify all succeed +4. Verify flow contains all 3 +Expected: +- All generations successful +- No data corruption +- Flow updated_at correct +``` + +**Test 14.2: Concurrent alias assignments** +```typescript +Purpose: Verify last-write-wins for aliases +Steps: +1. Start 2 uploads with same alias "@race" +2. Wait for completion +3. Verify one has alias +4. Verify other doesn't +5. Verify both images exist +Expected: +- No errors +- One image gets alias +- Both images preserved +``` + +**Test 14.3: Concurrent cache access** +```typescript +Purpose: Verify cache hit_count increments correctly +Steps: +1. Make same live URL request 10 times concurrently +2. Verify all return image +3. Check cache hit_count +4. Verify count = 9 or 10 (first might be miss) +Expected: +- All requests succeed +- hit_count accurate +``` + +#### TEST GROUP 15: Data Integrity + +**Test 15.1: Generation with originalPrompt tracking** +```typescript +Purpose: Verify prompt enhancement tracking +Steps: +1. POST generation with autoEnhance: true +2. Verify response has both: + - prompt (enhanced, used for generation) + - originalPrompt (user input) +3. POST generation with autoEnhance: false +4. Verify prompt present, originalPrompt null +Expected: +- Correct field population +- originalPrompt only when enhanced +``` + +**Test 15.2: Referenced images stored correctly** +```typescript +Purpose: Verify referencedImages JSONB format +Steps: +1. Generate with multiple references +2. GET generation details +3. Verify referencedImages array format: + [{ imageId: "uuid", alias: "@name" }, ...] +Expected: +- Correct JSONB structure +- All references tracked +``` + +**Test 15.3: Storage consistency check** +```typescript +Purpose: Verify DB and MinIO stay in sync +Steps: +1. Create generation +2. Check image in DB +3. Check file in MinIO (via storageUrl) +4. Delete image +5. Verify DB record gone +6. Verify MinIO file gone +Expected: +- DB and storage consistent +- No orphaned files +- No orphaned records +``` + +**Test 15.4: Cascade delete verification** +```typescript +Purpose: Verify all cascade rules work +Steps: +1. Create complex structure: + - Flow with generations and images + - Mixed aliased/non-aliased +2. Delete flow +3. Query all related records +4. Verify correct cascade behavior +Expected: +- Proper cascade execution +- Protected resources preserved +- All specified deletions complete +``` + +--- + +## 🛠️ Implementation Notes for Claude Code + +### Test Organization Principles + +1. **Independence**: Each test should be self-contained and not rely on state from other tests unless explicitly designed as a test group +2. **Cleanup**: Consider whether cleanup is needed between test groups +3. **Data Sharing**: Use `testContext` object to share IDs and data within test groups +4. **Polling**: Use `waitForGeneration()` for async operations, with appropriate timeouts +5. **Assertions**: Verify both success cases and expected data structure/values + +### Helper Functions to Implement/Extend + +**Recommended additions to utils.ts:** + +```typescript +// Poll for flow to exist (lazy creation check) +export async function checkFlowExists(flowId: string): Promise + +// Download and verify image from URL +export async function verifyImageAccessible(url: string): Promise + +// Create test flow with specific configuration +export async function createTestFlow(config?: Partial): Promise + +// Generate test image with known properties +export async function generateTestImage(prompt: string, options?: GenerationOptions): Promise + +// Verify cache entry exists +export async function checkCacheEntry(projectId: string, promptHash: string): Promise + +// Clean up test data (optional, for cleanup between test groups) +export async function cleanupTestData(): Promise +``` + +### Error Handling Strategy + +Tests should: +1. Expect specific HTTP status codes +2. Verify error response structure +3. Check error messages are meaningful +4. Validate that errors don't leave system in inconsistent state + +### Performance Considerations + +- Generation tests will be slow (Gemini API calls) +- Use reasonable timeouts (60s for generation) +- Consider running tests in parallel where safe +- Group tests to minimize redundant operations + +### Test Execution Order + +Recommended execution: +1. **01-basic.ts** - Foundation, creates test data +2. **02-flows.ts** - Flow functionality +3. **03-aliases.ts** - Alias system (uses data from #1-2) +4. **04-live.ts** - Live URLs and caching +5. **05-edge-cases.ts** - Error cases and edge scenarios + +### Documentation in Tests + +Each test should include: +- Clear purpose comment +- Step-by-step description +- Expected outcomes +- Any special considerations + +### Success Criteria + +Tests are considered complete when: +- ✅ All endpoint combinations covered +- ✅ All database schema features validated +- ✅ All API requirements from v2.0 tested +- ✅ All refactoring changes verified +- ✅ Error cases handled appropriately +- ✅ Real data flows validated (no mocks) +- ✅ Tests run against actual service +- ✅ Tests are documented and maintainable + +--- + +## 🎯 Expected Test Statistics + +**Total Test Files:** 5 +**Total Test Groups:** ~15 +**Total Individual Tests:** ~80-100 +**Estimated Runtime:** 15-30 minutes (due to actual generations) + +**Coverage Targets:** +- Endpoints: 100% (all documented endpoints) +- HTTP Methods: 100% (GET, POST, PUT, DELETE) +- Error Cases: 90%+ (major validation and not-found scenarios) +- Data Flows: 100% (all CRUD and generation flows) + +--- + +## 📚 Reference Documents + +Tests should validate functionality described in: +1. `banatie-database-design.md` - Database schema v2.0 +2. `banatie-api-requirements.md` - API specification v2.0 +3. `api-refactoring-final.md` - Final refactoring decisions + +--- + +**Document Version:** 1.0 +**Created:** 2024-11-17 +**Target Audience:** Claude Code +**Status:** Ready for Implementation