# 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<boolean>

// Download and verify image from URL
export async function verifyImageAccessible(url: string): Promise<boolean>

// Create test flow with specific configuration
export async function createTestFlow(config?: Partial<Flow>): Promise<Flow>

// Generate test image with known properties
export async function generateTestImage(prompt: string, options?: GenerationOptions): Promise<Generation>

// Verify cache entry exists
export async function checkCacheEntry(projectId: string, promptHash: string): Promise<boolean>

// Clean up test data (optional, for cleanup between test groups)
export async function cleanupTestData(): Promise<void>
```

### 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
