feat: added manual tests
This commit is contained in:
parent
0ca1a4576e
commit
df3737ed44
|
|
@ -0,0 +1,296 @@
|
|||
@base = http://localhost:3000
|
||||
@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d
|
||||
|
||||
###############################################################################
|
||||
# FLOW LIFECYCLE TESTS
|
||||
# Tests: Lazy flow creation, Eager flow creation, Flow operations
|
||||
#
|
||||
# Test Coverage:
|
||||
# 1. Lazy flow pattern - first generation without flowId
|
||||
# 2. Lazy flow - verify flow not created yet
|
||||
# 3. Lazy flow - second generation creates flow
|
||||
# 4. Eager flow creation with flowAlias
|
||||
# 5. List all flows
|
||||
# 6. Get flow with computed counts
|
||||
# 7. List flow generations
|
||||
# 8. List flow images
|
||||
# 9. Update flow aliases
|
||||
# 10. Remove specific flow alias
|
||||
# 11. Regenerate flow
|
||||
###############################################################################
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 1: Lazy Flow Pattern - First Generation
|
||||
# Generation without flowId should return auto-generated flowId
|
||||
# but NOT create flow in database yet (Section 4.1)
|
||||
###############################################################################
|
||||
|
||||
### Step 1.1: Create Generation without flowId
|
||||
# @name lazyFlowGen1
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "A red sports car on a mountain road",
|
||||
"aspectRatio": "16:9"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@lazyFlowId = {{lazyFlowGen1.response.body.$.data.flowId}}
|
||||
@lazyGenId1 = {{lazyFlowGen1.response.body.$.data.id}}
|
||||
|
||||
### Step 1.2: Poll Generation Status
|
||||
# @name checkLazyGen1
|
||||
GET {{base}}/api/v1/generations/{{lazyGenId1}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - flowId is returned (auto-generated UUID)
|
||||
# - status = "success"
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 2: Verify Lazy Flow Not Created Yet
|
||||
# Flow should NOT exist in database after first generation
|
||||
###############################################################################
|
||||
|
||||
### Step 2.1: Try to get flow (should return 404)
|
||||
# @name checkLazyFlowNotExists
|
||||
GET {{base}}/api/v1/flows/{{lazyFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 Not Found
|
||||
# Flow record not created yet (lazy creation pattern)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 3: Lazy Flow - Second Generation Creates Flow
|
||||
# Using same flowId should create the flow record
|
||||
###############################################################################
|
||||
|
||||
### Step 3.1: Create second generation with same flowId
|
||||
# @name lazyFlowGen2
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Same car but blue color",
|
||||
"aspectRatio": "16:9",
|
||||
"flowId": "{{lazyFlowId}}"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@lazyGenId2 = {{lazyFlowGen2.response.body.$.data.id}}
|
||||
|
||||
### Step 3.2: Poll Generation Status
|
||||
# @name checkLazyGen2
|
||||
GET {{base}}/api/v1/generations/{{lazyGenId2}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 3.3: Verify flow now exists
|
||||
# @name verifyLazyFlowExists
|
||||
GET {{base}}/api/v1/flows/{{lazyFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 200 OK
|
||||
# Flow record now exists after second use
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 4: Eager Flow Creation with flowAlias
|
||||
# Using flowAlias should create flow immediately
|
||||
###############################################################################
|
||||
|
||||
### Step 4.1: Create generation with flowAlias
|
||||
# @name eagerFlowGen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "A hero banner image",
|
||||
"aspectRatio": "21:9",
|
||||
"flowAlias": "@hero-flow"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@eagerFlowId = {{eagerFlowGen.response.body.$.data.flowId}}
|
||||
@eagerGenId = {{eagerFlowGen.response.body.$.data.id}}
|
||||
|
||||
### Step 4.2: Poll Generation Status
|
||||
# @name checkEagerGen
|
||||
GET {{base}}/api/v1/generations/{{eagerGenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 4.3: Verify flow exists immediately (eager creation)
|
||||
# @name verifyEagerFlowExists
|
||||
GET {{base}}/api/v1/flows/{{eagerFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Flow exists immediately
|
||||
# - aliases contains "@hero-flow"
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 5: List All Flows
|
||||
###############################################################################
|
||||
|
||||
### Step 5.1: List flows
|
||||
# @name listFlows
|
||||
GET {{base}}/api/v1/flows
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns array of flows
|
||||
# - Contains our lazyFlowId and eagerFlowId
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 6: Get Flow with Computed Counts
|
||||
###############################################################################
|
||||
|
||||
### Step 6.1: Get flow details
|
||||
# @name getFlowDetails
|
||||
GET {{base}}/api/v1/flows/{{lazyFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - generationCount is number (should be 2)
|
||||
# - imageCount is number (should be 2)
|
||||
# - aliases object present
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 7: List Flow Generations
|
||||
###############################################################################
|
||||
|
||||
### Step 7.1: Get flow's generations
|
||||
# @name getFlowGenerations
|
||||
GET {{base}}/api/v1/flows/{{lazyFlowId}}/generations
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns array of generations
|
||||
# - Contains 2 generations from lazy flow tests
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 8: List Flow Images
|
||||
###############################################################################
|
||||
|
||||
### Step 8.1: Get flow's images
|
||||
# @name getFlowImages
|
||||
GET {{base}}/api/v1/flows/{{lazyFlowId}}/images
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns array of images
|
||||
# - Contains output images from generations
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 9: Update Flow Aliases
|
||||
###############################################################################
|
||||
|
||||
### Step 9.1: Update flow aliases
|
||||
# @name updateFlowAliases
|
||||
PUT {{base}}/api/v1/flows/{{lazyFlowId}}/aliases
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"aliases": {
|
||||
"@latest": "{{checkLazyGen2.response.body.$.data.outputImageId}}",
|
||||
"@best": "{{checkLazyGen2.response.body.$.data.outputImageId}}"
|
||||
}
|
||||
}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns updated flow with new aliases
|
||||
# - aliases contains @latest and @best
|
||||
|
||||
|
||||
### Step 9.2: Verify aliases set
|
||||
# @name verifyAliasesSet
|
||||
GET {{base}}/api/v1/flows/{{lazyFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 10: Remove Specific Flow Alias
|
||||
###############################################################################
|
||||
|
||||
### Step 10.1: Delete @best alias
|
||||
# @name deleteFlowAlias
|
||||
DELETE {{base}}/api/v1/flows/{{lazyFlowId}}/aliases/@best
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 10.2: Verify alias removed
|
||||
# @name verifyAliasRemoved
|
||||
GET {{base}}/api/v1/flows/{{lazyFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - @best not in aliases
|
||||
# - @latest still in aliases
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 11: Regenerate Flow
|
||||
# Regenerates the most recent generation in a flow
|
||||
###############################################################################
|
||||
|
||||
### Step 11.1: Trigger regeneration
|
||||
# @name regenerateFlow
|
||||
POST {{base}}/api/v1/flows/{{lazyFlowId}}/regenerate
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns new generation object
|
||||
# - New generation is in the same flow
|
||||
|
||||
|
||||
###############################################################################
|
||||
# NOTES
|
||||
###############################################################################
|
||||
#
|
||||
# Lazy Flow Pattern (Section 4.1):
|
||||
# 1. First request without flowId -> return generated flowId, but DO NOT create in DB
|
||||
# 2. Any request with valid flowId -> create flow in DB if doesn't exist
|
||||
# 3. If flowAlias specified -> create flow immediately (eager creation)
|
||||
#
|
||||
# Flow Aliases:
|
||||
# - Stored in flow.aliases JSONB field
|
||||
# - Map alias names to image IDs
|
||||
# - Can be updated via PUT /flows/:id/aliases
|
||||
# - Individual aliases deleted via DELETE /flows/:id/aliases/:alias
|
||||
#
|
||||
|
|
@ -0,0 +1,590 @@
|
|||
@base = http://localhost:3000
|
||||
@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d
|
||||
|
||||
###############################################################################
|
||||
# ALIAS RESOLUTION TESTS
|
||||
# Tests: 3-Tier Alias Resolution (Technical -> Flow -> Project)
|
||||
#
|
||||
# Test Coverage:
|
||||
# 1. Technical alias @last
|
||||
# 2. Technical alias @first
|
||||
# 3. Technical alias @upload
|
||||
# 4. Technical alias requires flowId
|
||||
# 5. Flow-scoped alias resolution
|
||||
# 6. Project-scoped alias resolution
|
||||
# 7. Alias precedence (flow > project)
|
||||
# 8. Reserved aliases cannot be assigned
|
||||
# 9. Alias reassignment removes old
|
||||
# 10. Same alias in different flows
|
||||
# 11. Technical alias in generation prompt
|
||||
# 12. Upload with both project and flow alias
|
||||
###############################################################################
|
||||
|
||||
|
||||
###############################################################################
|
||||
# SETUP: Create Test Flow
|
||||
###############################################################################
|
||||
|
||||
### Setup: Create flow for alias tests
|
||||
# @name setupGen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Setup image for alias tests",
|
||||
"aspectRatio": "1:1",
|
||||
"flowAlias": "@alias-test-flow"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@aliasFlowId = {{setupGen.response.body.$.data.flowId}}
|
||||
@setupGenId = {{setupGen.response.body.$.data.id}}
|
||||
|
||||
### Poll setup generation
|
||||
# @name checkSetupGen
|
||||
GET {{base}}/api/v1/generations/{{setupGenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@setupImageId = {{checkSetupGen.response.body.$.data.outputImageId}}
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 1: Technical Alias @last
|
||||
# Resolves to last generated image in flow
|
||||
###############################################################################
|
||||
|
||||
### Step 1.1: Resolve @last (requires flowId)
|
||||
# @name resolveLast
|
||||
GET {{base}}/api/v1/images/@last?flowId={{aliasFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns image (status 200)
|
||||
# - Returns the most recently generated image in the flow
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 2: Technical Alias @first
|
||||
# Resolves to first generated image in flow
|
||||
###############################################################################
|
||||
|
||||
### Step 2.1: Resolve @first (requires flowId)
|
||||
# @name resolveFirst
|
||||
GET {{base}}/api/v1/images/@first?flowId={{aliasFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns image (status 200)
|
||||
# - Returns the first generated image in the flow
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 3: Technical Alias @upload
|
||||
# Resolves to last uploaded image in flow
|
||||
###############################################################################
|
||||
|
||||
### Step 3.1: Upload image to flow
|
||||
# @name uploadForTest
|
||||
POST {{base}}/api/v1/images/upload
|
||||
X-API-Key: {{apiKey}}
|
||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
Content-Disposition: form-data; name="file"; filename="test-image.png"
|
||||
Content-Type: image/png
|
||||
|
||||
< ./fixture/test-image.png
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
Content-Disposition: form-data; name="flowId"
|
||||
|
||||
{{aliasFlowId}}
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
Content-Disposition: form-data; name="description"
|
||||
|
||||
Uploaded for @upload test
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW--
|
||||
|
||||
###
|
||||
|
||||
@uploadedImageId = {{uploadForTest.response.body.$.data.id}}
|
||||
|
||||
### Step 3.2: Resolve @upload (requires flowId)
|
||||
# @name resolveUpload
|
||||
GET {{base}}/api/v1/images/@upload?flowId={{aliasFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns image (status 200)
|
||||
# - Returns uploaded image (source = "uploaded")
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 4: Technical Alias Requires Flow Context
|
||||
# @last, @first, @upload require flowId parameter
|
||||
###############################################################################
|
||||
|
||||
### Step 4.1: Try @last without flowId (should fail)
|
||||
# @name resolveLastNoFlow
|
||||
GET {{base}}/api/v1/images/@last
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 with error "Technical aliases require flowId"
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 5: Flow-Scoped Alias Resolution
|
||||
###############################################################################
|
||||
|
||||
### Step 5.1: Create generation with flow alias
|
||||
# @name flowAliasGen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Image for flow alias test",
|
||||
"aspectRatio": "1:1",
|
||||
"flowId": "{{aliasFlowId}}",
|
||||
"flowAlias": "@flow-hero"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@flowAliasGenId = {{flowAliasGen.response.body.$.data.id}}
|
||||
|
||||
### Step 5.2: Poll generation
|
||||
# @name checkFlowAliasGen
|
||||
GET {{base}}/api/v1/generations/{{flowAliasGenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@flowHeroImageId = {{checkFlowAliasGen.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 5.3: Resolve flow alias
|
||||
# @name resolveFlowAlias
|
||||
GET {{base}}/api/v1/images/@flow-hero?flowId={{aliasFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns the image from step 5.1
|
||||
# - Only works with flowId parameter
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 6: Project-Scoped Alias Resolution
|
||||
###############################################################################
|
||||
|
||||
### Step 6.1: Create generation with project alias
|
||||
# @name projectAliasGen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Image for project alias test",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@project-logo",
|
||||
"flowId": null
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@projectAliasGenId = {{projectAliasGen.response.body.$.data.id}}
|
||||
|
||||
### Step 6.2: Poll generation
|
||||
# @name checkProjectAliasGen
|
||||
GET {{base}}/api/v1/generations/{{projectAliasGenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@projectLogoImageId = {{checkProjectAliasGen.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 6.3: Resolve project alias (no flowId needed)
|
||||
# @name resolveProjectAlias
|
||||
GET {{base}}/api/v1/images/@project-logo
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns the image from step 6.1
|
||||
# - Works without flowId parameter
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 7: Alias Precedence (Flow > Project)
|
||||
###############################################################################
|
||||
|
||||
### Step 7.1: Create project-scoped alias @priority-test
|
||||
# @name priorityProject
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Project scoped image for priority test",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@priority-test",
|
||||
"flowId": null
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@priorityProjectGenId = {{priorityProject.response.body.$.data.id}}
|
||||
|
||||
### Step 7.2: Poll generation
|
||||
# @name checkPriorityProject
|
||||
GET {{base}}/api/v1/generations/{{priorityProjectGenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@priorityProjectImageId = {{checkPriorityProject.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 7.3: Create flow-scoped alias @priority-test (same name)
|
||||
# @name priorityFlow
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Flow scoped image for priority test",
|
||||
"aspectRatio": "1:1",
|
||||
"flowId": "{{aliasFlowId}}",
|
||||
"flowAlias": "@priority-test"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@priorityFlowGenId = {{priorityFlow.response.body.$.data.id}}
|
||||
|
||||
### Step 7.4: Poll generation
|
||||
# @name checkPriorityFlow
|
||||
GET {{base}}/api/v1/generations/{{priorityFlowGenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@priorityFlowImageId = {{checkPriorityFlow.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 7.5: Resolve WITHOUT flowId (should get project)
|
||||
# @name resolvePriorityNoFlow
|
||||
GET {{base}}/api/v1/images/@priority-test
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Returns project image ({{priorityProjectImageId}})
|
||||
|
||||
### Step 7.6: Resolve WITH flowId (should get flow)
|
||||
# @name resolvePriorityWithFlow
|
||||
GET {{base}}/api/v1/images/@priority-test?flowId={{aliasFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Returns flow image ({{priorityFlowImageId}})
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 8: Reserved Aliases Cannot Be Assigned
|
||||
###############################################################################
|
||||
|
||||
### Step 8.1: Try to use @last as alias (should fail or warn)
|
||||
# @name reservedLast
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test reserved alias",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@last"
|
||||
}
|
||||
|
||||
###
|
||||
# Expected: 400 validation error OR generation succeeds but @last not assigned
|
||||
|
||||
### Step 8.2: Try to use @first as alias
|
||||
# @name reservedFirst
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test reserved alias",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@first"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
### Step 8.3: Try to use @upload as alias
|
||||
# @name reservedUpload
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test reserved alias",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@upload"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 9: Alias Reassignment (Override Behavior)
|
||||
###############################################################################
|
||||
|
||||
### Step 9.1: Create first image with alias
|
||||
# @name reassign1
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "First image for reassign test",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@reassign-test",
|
||||
"flowId": null
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@reassign1GenId = {{reassign1.response.body.$.data.id}}
|
||||
|
||||
### Step 9.2: Poll first generation
|
||||
# @name checkReassign1
|
||||
GET {{base}}/api/v1/generations/{{reassign1GenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@reassign1ImageId = {{checkReassign1.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 9.3: Create second image with SAME alias
|
||||
# @name reassign2
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Second image for reassign test",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@reassign-test",
|
||||
"flowId": null
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@reassign2GenId = {{reassign2.response.body.$.data.id}}
|
||||
|
||||
### Step 9.4: Poll second generation
|
||||
# @name checkReassign2
|
||||
GET {{base}}/api/v1/generations/{{reassign2GenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@reassign2ImageId = {{checkReassign2.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 9.5: Resolve alias (should be second image)
|
||||
# @name resolveReassign
|
||||
GET {{base}}/api/v1/images/@reassign-test
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Returns second image ({{reassign2ImageId}})
|
||||
|
||||
### Step 9.6: Check first image lost alias
|
||||
# @name checkFirstLostAlias
|
||||
GET {{base}}/api/v1/images/{{reassign1ImageId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: alias = null
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 10: Same Alias in Different Flows
|
||||
###############################################################################
|
||||
|
||||
### Step 10.1: Create flow 1 with @shared-name alias
|
||||
# @name sharedFlow1
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Flow 1 image with shared name",
|
||||
"aspectRatio": "1:1",
|
||||
"flowAlias": "@shared-name"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@sharedFlow1Id = {{sharedFlow1.response.body.$.data.flowId}}
|
||||
@sharedGen1Id = {{sharedFlow1.response.body.$.data.id}}
|
||||
|
||||
### Step 10.2: Poll generation 1
|
||||
# @name checkSharedGen1
|
||||
GET {{base}}/api/v1/generations/{{sharedGen1Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@sharedImage1Id = {{checkSharedGen1.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 10.3: Create flow 2 with SAME @shared-name alias
|
||||
# @name sharedFlow2
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Flow 2 image with shared name",
|
||||
"aspectRatio": "1:1",
|
||||
"flowAlias": "@shared-name"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@sharedFlow2Id = {{sharedFlow2.response.body.$.data.flowId}}
|
||||
@sharedGen2Id = {{sharedFlow2.response.body.$.data.id}}
|
||||
|
||||
### Step 10.4: Poll generation 2
|
||||
# @name checkSharedGen2
|
||||
GET {{base}}/api/v1/generations/{{sharedGen2Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@sharedImage2Id = {{checkSharedGen2.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 10.5: Resolve @shared-name in flow 1
|
||||
# @name resolveSharedFlow1
|
||||
GET {{base}}/api/v1/images/@shared-name?flowId={{sharedFlow1Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Returns {{sharedImage1Id}}
|
||||
|
||||
### Step 10.6: Resolve @shared-name in flow 2
|
||||
# @name resolveSharedFlow2
|
||||
GET {{base}}/api/v1/images/@shared-name?flowId={{sharedFlow2Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Returns {{sharedImage2Id}} (different from flow 1)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 11: Technical Alias in Generation Prompt
|
||||
###############################################################################
|
||||
|
||||
### Step 11.1: Generate using @last in prompt
|
||||
# @name techAliasPrompt
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "New variation based on @last",
|
||||
"aspectRatio": "1:1",
|
||||
"flowId": "{{aliasFlowId}}"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@techAliasGenId = {{techAliasPrompt.response.body.$.data.id}}
|
||||
|
||||
### Step 11.2: Poll generation
|
||||
# @name checkTechAliasGen
|
||||
GET {{base}}/api/v1/generations/{{techAliasGenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - status = "success"
|
||||
# - referencedImages contains @last alias
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 12: Upload with Both Project and Flow Alias
|
||||
###############################################################################
|
||||
|
||||
### Step 12.1: Upload with both aliases
|
||||
# @name dualAliasUpload
|
||||
POST {{base}}/api/v1/images/upload
|
||||
X-API-Key: {{apiKey}}
|
||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
Content-Disposition: form-data; name="file"; filename="test-image.png"
|
||||
Content-Type: image/png
|
||||
|
||||
< ./fixture/test-image.png
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
Content-Disposition: form-data; name="alias"
|
||||
|
||||
@dual-project
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
Content-Disposition: form-data; name="flowId"
|
||||
|
||||
{{aliasFlowId}}
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW
|
||||
Content-Disposition: form-data; name="flowAlias"
|
||||
|
||||
@dual-flow
|
||||
------WebKitFormBoundary7MA4YWxkTrZu0gW--
|
||||
|
||||
###
|
||||
|
||||
@dualAliasImageId = {{dualAliasUpload.response.body.$.data.id}}
|
||||
|
||||
### Step 12.2: Resolve project alias
|
||||
# @name resolveDualProject
|
||||
GET {{base}}/api/v1/images/@dual-project
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Returns {{dualAliasImageId}}
|
||||
|
||||
### Step 12.3: Resolve flow alias
|
||||
# @name resolveDualFlow
|
||||
GET {{base}}/api/v1/images/@dual-flow?flowId={{aliasFlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Returns {{dualAliasImageId}} (same image)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# NOTES
|
||||
###############################################################################
|
||||
#
|
||||
# 3-Tier Alias Resolution Order:
|
||||
# 1. Technical (@last, @first, @upload) - require flowId
|
||||
# 2. Flow-scoped (stored in flow.aliases) - require flowId
|
||||
# 3. Project-scoped (stored in images.alias) - no flowId needed
|
||||
#
|
||||
# Alias Format:
|
||||
# - Must start with @
|
||||
# - Alphanumeric + hyphens only
|
||||
# - Reserved: @last, @first, @upload
|
||||
#
|
||||
# Override Behavior (Section 5.2):
|
||||
# - New alias assignment takes priority
|
||||
# - Previous image loses its alias
|
||||
# - Previous image is NOT deleted
|
||||
#
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
@base = http://localhost:3000
|
||||
@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d
|
||||
|
||||
###############################################################################
|
||||
# LIVE URL & SCOPE MANAGEMENT TESTS
|
||||
# Tests: Live generation with caching, Scope management
|
||||
#
|
||||
# Test Coverage:
|
||||
# 1. Create live scope
|
||||
# 2. List all scopes
|
||||
# 3. Get scope details
|
||||
# 4. Update scope settings
|
||||
# 5. Live URL - basic generation
|
||||
# 6. Regenerate scope images
|
||||
# 7. Delete scope
|
||||
###############################################################################
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 1: Create Live Scope
|
||||
###############################################################################
|
||||
|
||||
### Step 1.1: Create scope
|
||||
# @name createScope
|
||||
POST {{base}}/api/v1/live/scopes
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"slug": "test-scope",
|
||||
"allowNewGenerations": true,
|
||||
"newGenerationsLimit": 50
|
||||
}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns scope object
|
||||
# - slug = "test-scope"
|
||||
# - allowNewGenerations = true
|
||||
# - newGenerationsLimit = 50
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 2: List All Scopes
|
||||
###############################################################################
|
||||
|
||||
### Step 2.1: List scopes
|
||||
# @name listScopes
|
||||
GET {{base}}/api/v1/live/scopes
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns array of scopes
|
||||
# - Contains "test-scope"
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 3: Get Scope Details
|
||||
###############################################################################
|
||||
|
||||
### Step 3.1: Get scope by slug
|
||||
# @name getScope
|
||||
GET {{base}}/api/v1/live/scopes/test-scope
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns scope object
|
||||
# - slug = "test-scope"
|
||||
# - currentGenerations is number
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 4: Update Scope Settings
|
||||
###############################################################################
|
||||
|
||||
### Step 4.1: Disable new generations
|
||||
# @name updateScopeDisable
|
||||
PUT {{base}}/api/v1/live/scopes/test-scope
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"allowNewGenerations": false,
|
||||
"newGenerationsLimit": 100
|
||||
}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - allowNewGenerations = false
|
||||
# - newGenerationsLimit = 100
|
||||
|
||||
|
||||
### Step 4.2: Re-enable for testing
|
||||
# @name updateScopeEnable
|
||||
PUT {{base}}/api/v1/live/scopes/test-scope
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"allowNewGenerations": true
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 5: Live URL - Basic Generation
|
||||
# GET /api/v1/live?prompt=...
|
||||
# Returns image bytes directly with cache headers
|
||||
###############################################################################
|
||||
|
||||
### Step 5.1: Generate via live URL
|
||||
# @name liveGenerate
|
||||
GET {{base}}/api/v1/live?prompt=A%20simple%20blue%20square%20on%20white%20background
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns 200
|
||||
# - Response is image bytes (Content-Type: image/*)
|
||||
# - X-Cache-Status header (HIT or MISS)
|
||||
|
||||
|
||||
### Step 5.2: Same prompt again (should be cached)
|
||||
# @name liveGenerateCached
|
||||
GET {{base}}/api/v1/live?prompt=A%20simple%20blue%20square%20on%20white%20background
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - X-Cache-Status: HIT
|
||||
# - Faster response time
|
||||
|
||||
|
||||
### Step 5.3: Different prompt
|
||||
# @name liveGenerateNew
|
||||
GET {{base}}/api/v1/live?prompt=A%20red%20circle%20on%20black%20background
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - X-Cache-Status: MISS (new prompt)
|
||||
|
||||
|
||||
### Step 5.4: With aspect ratio
|
||||
# @name liveGenerateWithAspect
|
||||
GET {{base}}/api/v1/live?prompt=A%20landscape%20scene&aspectRatio=16:9
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 6: Regenerate Scope Images
|
||||
###############################################################################
|
||||
|
||||
### Step 6.1: Trigger regeneration
|
||||
# @name regenerateScope
|
||||
POST {{base}}/api/v1/live/scopes/test-scope/regenerate
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns 200
|
||||
# - Regeneration triggered
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 7: Delete Scope
|
||||
###############################################################################
|
||||
|
||||
### Step 7.1: Delete scope
|
||||
# @name deleteScope
|
||||
DELETE {{base}}/api/v1/live/scopes/test-scope
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns 200
|
||||
|
||||
|
||||
### Step 7.2: Verify deleted (should 404)
|
||||
# @name verifyScopeDeleted
|
||||
GET {{base}}/api/v1/live/scopes/test-scope
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 Not Found
|
||||
|
||||
|
||||
###############################################################################
|
||||
# NOTES
|
||||
###############################################################################
|
||||
#
|
||||
# Live URL Endpoint:
|
||||
# - GET /api/v1/live?prompt=...
|
||||
# - Returns image bytes directly (not JSON)
|
||||
# - Supports prompt caching via SHA-256 hash
|
||||
#
|
||||
# Response Headers:
|
||||
# - Content-Type: image/jpeg (or image/png, etc.)
|
||||
# - X-Cache-Status: HIT | MISS
|
||||
# - X-Cache-Hit-Count: number (on HIT)
|
||||
# - X-Generation-Id: UUID (on MISS)
|
||||
# - X-Image-Id: UUID
|
||||
# - Cache-Control: public, max-age=31536000
|
||||
#
|
||||
# Scope Management:
|
||||
# - Scopes group generations for management
|
||||
# - allowNewGenerations controls if new prompts generate
|
||||
# - newGenerationsLimit caps generations per scope
|
||||
#
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
@base = http://localhost:3000
|
||||
@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d
|
||||
|
||||
###############################################################################
|
||||
# EDGE CASES & VALIDATION TESTS
|
||||
# Tests: Input validation, Error handling, Edge cases
|
||||
#
|
||||
# Test Coverage:
|
||||
# 1. Invalid alias format
|
||||
# 2. Invalid aspect ratio
|
||||
# 3. Missing required fields
|
||||
# 4. 404 for non-existent resources
|
||||
# 5. Regenerate generation
|
||||
# 6. CDN endpoints
|
||||
###############################################################################
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 1: Invalid Alias Format
|
||||
# Aliases must start with @ and contain only alphanumeric + hyphens
|
||||
###############################################################################
|
||||
|
||||
### Step 1.1: Alias without @ symbol (should fail)
|
||||
# @name invalidNoAt
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test invalid alias",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "no-at-symbol"
|
||||
}
|
||||
|
||||
###
|
||||
# Expected: 400 validation error OR 500 with alias error
|
||||
|
||||
### Step 1.2: Alias with spaces (should fail)
|
||||
# @name invalidWithSpaces
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test invalid alias",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@has spaces"
|
||||
}
|
||||
|
||||
###
|
||||
# Expected: 400 validation error
|
||||
|
||||
### Step 1.3: Alias with special characters (should fail)
|
||||
# @name invalidSpecialChars
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test invalid alias",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": "@special!chars"
|
||||
}
|
||||
|
||||
###
|
||||
# Expected: 400 validation error
|
||||
|
||||
### Step 1.4: Empty alias (should fail or be ignored)
|
||||
# @name invalidEmpty
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test invalid alias",
|
||||
"aspectRatio": "1:1",
|
||||
"alias": ""
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 2: Invalid Aspect Ratio
|
||||
###############################################################################
|
||||
|
||||
### Step 2.1: Invalid aspect ratio string
|
||||
# @name invalidAspectRatio
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test invalid aspect ratio",
|
||||
"aspectRatio": "invalid"
|
||||
}
|
||||
|
||||
###
|
||||
# Expected: 400 validation error
|
||||
|
||||
### Step 2.2: Unsupported aspect ratio
|
||||
# @name unsupportedAspectRatio
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test unsupported aspect ratio",
|
||||
"aspectRatio": "5:7"
|
||||
}
|
||||
|
||||
###
|
||||
# Expected: 400 validation error (only 1:1, 16:9, 9:16, 4:3, 3:4, 21:9 supported)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 3: Missing Required Fields
|
||||
###############################################################################
|
||||
|
||||
### Step 3.1: Missing prompt
|
||||
# @name missingPrompt
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"aspectRatio": "1:1"
|
||||
}
|
||||
|
||||
###
|
||||
# Expected: 400 - "Prompt is required"
|
||||
|
||||
### Step 3.2: Empty body
|
||||
# @name emptyBody
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{}
|
||||
|
||||
###
|
||||
# Expected: 400 - "Prompt is required"
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 4: 404 for Non-Existent Resources
|
||||
###############################################################################
|
||||
|
||||
### Step 4.1: Non-existent image
|
||||
# @name notFoundImage
|
||||
GET {{base}}/api/v1/images/00000000-0000-0000-0000-000000000000
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 Not Found
|
||||
|
||||
### Step 4.2: Non-existent generation
|
||||
# @name notFoundGeneration
|
||||
GET {{base}}/api/v1/generations/00000000-0000-0000-0000-000000000000
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 Not Found
|
||||
|
||||
### Step 4.3: Non-existent flow
|
||||
# @name notFoundFlow
|
||||
GET {{base}}/api/v1/flows/00000000-0000-0000-0000-000000000000
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 Not Found
|
||||
|
||||
### Step 4.4: Non-existent alias
|
||||
# @name notFoundAlias
|
||||
GET {{base}}/api/v1/images/@non-existent-alias
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 - "Alias '@non-existent-alias' not found"
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 5: Regenerate Generation
|
||||
###############################################################################
|
||||
|
||||
### Step 5.1: Create generation for regenerate test
|
||||
# @name createForRegen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test image for regenerate",
|
||||
"aspectRatio": "1:1"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@regenSourceId = {{createForRegen.response.body.$.data.id}}
|
||||
|
||||
### Step 5.2: Poll until success
|
||||
# @name checkForRegen
|
||||
GET {{base}}/api/v1/generations/{{regenSourceId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 5.3: Regenerate
|
||||
# @name regenerateGen
|
||||
POST {{base}}/api/v1/generations/{{regenSourceId}}/regenerate
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Returns new generation
|
||||
# - New generation has same prompt
|
||||
|
||||
### Step 5.4: Regenerate non-existent generation (should 404)
|
||||
# @name regenerateNotFound
|
||||
POST {{base}}/api/v1/generations/00000000-0000-0000-0000-000000000000/regenerate
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{}
|
||||
|
||||
###
|
||||
# Expected: 404 Not Found
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 6: CDN Endpoints
|
||||
###############################################################################
|
||||
|
||||
### Step 6.1: CDN image by path (if implemented)
|
||||
# @name cdnImage
|
||||
GET {{base}}/api/v1/cdn/default/test-project/generated/2024-01/test.jpg
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Note: Endpoint structure check only - actual paths depend on storage
|
||||
|
||||
### Step 6.2: Health check
|
||||
# @name healthCheck
|
||||
GET {{base}}/health
|
||||
|
||||
###
|
||||
# Expected: 200 with status info
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 7: Authentication Errors
|
||||
###############################################################################
|
||||
|
||||
### Step 7.1: Missing API key
|
||||
# @name noApiKey
|
||||
GET {{base}}/api/v1/generations
|
||||
|
||||
###
|
||||
# Expected: 401 Unauthorized
|
||||
|
||||
### Step 7.2: Invalid API key
|
||||
# @name invalidApiKey
|
||||
GET {{base}}/api/v1/generations
|
||||
X-API-Key: bnt_invalid_key_12345
|
||||
|
||||
###
|
||||
# Expected: 401 Unauthorized
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 8: Malformed Requests
|
||||
###############################################################################
|
||||
|
||||
### Step 8.1: Invalid JSON
|
||||
# @name invalidJson
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{invalid json}
|
||||
|
||||
###
|
||||
# Expected: 400 Bad Request
|
||||
|
||||
### Step 8.2: Wrong content type
|
||||
# @name wrongContentType
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: text/plain
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
prompt=test&aspectRatio=1:1
|
||||
|
||||
###
|
||||
|
||||
|
||||
###############################################################################
|
||||
# NOTES
|
||||
###############################################################################
|
||||
#
|
||||
# Validation Rules:
|
||||
# - Prompt: required, non-empty string
|
||||
# - Aspect ratio: must be supported (1:1, 16:9, 9:16, 4:3, 3:4, 21:9)
|
||||
# - Alias: must start with @, alphanumeric + hyphens only
|
||||
# - UUID: must be valid UUID format
|
||||
#
|
||||
# Error Responses:
|
||||
# - 400: Validation error (missing/invalid fields)
|
||||
# - 401: Authentication error (missing/invalid API key)
|
||||
# - 404: Resource not found
|
||||
# - 429: Rate limit exceeded
|
||||
# - 500: Internal server error
|
||||
#
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
@base = http://localhost:3000
|
||||
@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d
|
||||
|
||||
###############################################################################
|
||||
# KNOWN ISSUES TESTS
|
||||
# These tests document known bugs and implementation gaps
|
||||
#
|
||||
# ⚠️ EXPECTED TO FAIL until issues are fixed
|
||||
#
|
||||
# Test Coverage:
|
||||
# 1. Project alias on flow image
|
||||
# 2. Flow delete cascades non-aliased images
|
||||
# 3. Flow delete preserves aliased images
|
||||
# 4. Flow delete cascades generations
|
||||
###############################################################################
|
||||
|
||||
|
||||
###############################################################################
|
||||
# ISSUE 1: Project Alias on Flow Image
|
||||
# An image in a flow should be able to have a project-scoped alias
|
||||
###############################################################################
|
||||
|
||||
### Step 1.1: Create image with both flow and project alias
|
||||
# @name issue1Gen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Image in flow with project alias",
|
||||
"aspectRatio": "1:1",
|
||||
"flowAlias": "@flow-test",
|
||||
"alias": "@project-test"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@issue1FlowId = {{issue1Gen.response.body.$.data.flowId}}
|
||||
@issue1GenId = {{issue1Gen.response.body.$.data.id}}
|
||||
|
||||
### Step 1.2: Poll generation
|
||||
# @name checkIssue1Gen
|
||||
GET {{base}}/api/v1/generations/{{issue1GenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@issue1ImageId = {{checkIssue1Gen.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 1.3: Resolve project alias (via deprecated /resolve endpoint)
|
||||
# @name resolveProjectOnFlow
|
||||
GET {{base}}/api/v1/images/resolve/@project-test
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# BUG: Project alias on flow image should be resolvable
|
||||
# Expected: Returns image with id = {{issue1ImageId}}
|
||||
|
||||
### Step 1.4: Resolve project alias (via direct path - Section 6.2)
|
||||
# @name resolveProjectOnFlowDirect
|
||||
GET {{base}}/api/v1/images/@project-test
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# This should work after Section 6.2 implementation
|
||||
|
||||
|
||||
###############################################################################
|
||||
# ISSUE 2: Flow Delete Cascades Non-Aliased Images
|
||||
# When deleting a flow, images without project alias should be deleted
|
||||
###############################################################################
|
||||
|
||||
### Step 2.1: Create flow with non-aliased image
|
||||
# @name issue2Gen1
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "No alias image",
|
||||
"aspectRatio": "1:1",
|
||||
"flowAlias": "@issue-flow"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@issue2FlowId = {{issue2Gen1.response.body.$.data.flowId}}
|
||||
@issue2Gen1Id = {{issue2Gen1.response.body.$.data.id}}
|
||||
|
||||
### Step 2.2: Poll generation
|
||||
# @name checkIssue2Gen1
|
||||
GET {{base}}/api/v1/generations/{{issue2Gen1Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@issue2Image1Id = {{checkIssue2Gen1.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 2.3: Add aliased image to same flow
|
||||
# @name issue2Gen2
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "With alias image",
|
||||
"aspectRatio": "1:1",
|
||||
"flowId": "{{issue2FlowId}}",
|
||||
"alias": "@protected-image"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@issue2Gen2Id = {{issue2Gen2.response.body.$.data.id}}
|
||||
|
||||
### Step 2.4: Poll generation
|
||||
# @name checkIssue2Gen2
|
||||
GET {{base}}/api/v1/generations/{{issue2Gen2Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@issue2Image2Id = {{checkIssue2Gen2.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 2.5: Delete flow
|
||||
# @name deleteIssue2Flow
|
||||
DELETE {{base}}/api/v1/flows/{{issue2FlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 2.6: Check non-aliased image (should be 404)
|
||||
# @name checkIssue2Image1Deleted
|
||||
GET {{base}}/api/v1/images/{{issue2Image1Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 - Non-aliased image should be deleted with flow
|
||||
|
||||
### Step 2.7: Check aliased image (should still exist)
|
||||
# @name checkIssue2Image2Exists
|
||||
GET {{base}}/api/v1/images/{{issue2Image2Id}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 200 - Aliased image should be preserved
|
||||
|
||||
|
||||
###############################################################################
|
||||
# ISSUE 3: Flow Delete Preserves Aliased Images
|
||||
# Aliased images should have flowId set to null after flow deletion
|
||||
###############################################################################
|
||||
|
||||
### Step 3.1: Create flow with aliased image
|
||||
# @name issue3Gen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Protected image",
|
||||
"aspectRatio": "1:1",
|
||||
"flowAlias": "@test-flow-2",
|
||||
"alias": "@keep-this"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@issue3FlowId = {{issue3Gen.response.body.$.data.flowId}}
|
||||
@issue3GenId = {{issue3Gen.response.body.$.data.id}}
|
||||
|
||||
### Step 3.2: Poll generation
|
||||
# @name checkIssue3Gen
|
||||
GET {{base}}/api/v1/generations/{{issue3GenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
@issue3ImageId = {{checkIssue3Gen.response.body.$.data.outputImageId}}
|
||||
|
||||
### Step 3.3: Delete flow
|
||||
# @name deleteIssue3Flow
|
||||
DELETE {{base}}/api/v1/flows/{{issue3FlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 3.4: Check aliased image (should exist with flowId=null)
|
||||
# @name checkIssue3ImagePreserved
|
||||
GET {{base}}/api/v1/images/{{issue3ImageId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 200 with flowId = null
|
||||
# BUG: flowId might not be set to null
|
||||
|
||||
|
||||
###############################################################################
|
||||
# ISSUE 4: Flow Delete Cascades Generations
|
||||
# Generations should be deleted when flow is deleted
|
||||
###############################################################################
|
||||
|
||||
### Step 4.1: Create flow with generation
|
||||
# @name issue4Gen
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "Test generation",
|
||||
"aspectRatio": "1:1",
|
||||
"flowAlias": "@gen-flow"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@issue4FlowId = {{issue4Gen.response.body.$.data.flowId}}
|
||||
@issue4GenId = {{issue4Gen.response.body.$.data.id}}
|
||||
|
||||
### Step 4.2: Poll generation
|
||||
# @name checkIssue4Gen
|
||||
GET {{base}}/api/v1/generations/{{issue4GenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 4.3: Delete flow
|
||||
# @name deleteIssue4Flow
|
||||
DELETE {{base}}/api/v1/flows/{{issue4FlowId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
### Step 4.4: Check generation (should be 404)
|
||||
# @name checkIssue4GenDeleted
|
||||
GET {{base}}/api/v1/generations/{{issue4GenId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected: 404 - Generation should be deleted with flow
|
||||
|
||||
|
||||
###############################################################################
|
||||
# NOTES
|
||||
###############################################################################
|
||||
#
|
||||
# Flow Deletion Cascade (per api-refactoring-final.md):
|
||||
# - Flow record → DELETE
|
||||
# - All generations → DELETE
|
||||
# - Images without alias → DELETE (with MinIO cleanup)
|
||||
# - Images with project alias → KEEP (unlink: flowId = NULL)
|
||||
#
|
||||
# Known Issues:
|
||||
# 1. Project alias on flow images may not resolve properly
|
||||
# 2. Flow deletion may not properly cascade deletions
|
||||
# 3. Aliased images may not have flowId set to null
|
||||
#
|
||||
# These tests document expected behavior that may not be implemented yet.
|
||||
#
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
@base = http://localhost:3000
|
||||
@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d
|
||||
|
||||
###############################################################################
|
||||
# AUTO-ENHANCE TESTS
|
||||
# Tests: Prompt auto-enhancement feature
|
||||
#
|
||||
# Test Coverage:
|
||||
# 1. Generate without autoEnhance param (defaults to true)
|
||||
# 2. Generate with autoEnhance: false
|
||||
# 3. Generate with autoEnhance: true
|
||||
# 4. Verify enhancement quality
|
||||
# 5. List generations with autoEnhance field
|
||||
# 6. Verify response structure
|
||||
###############################################################################
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 1: Generate Without autoEnhance Parameter
|
||||
# Should default to true (enhancement enabled)
|
||||
###############################################################################
|
||||
|
||||
### Step 1.1: Create generation without autoEnhance param
|
||||
# @name genDefaultEnhance
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "a simple test image",
|
||||
"aspectRatio": "1:1"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@genDefaultId = {{genDefaultEnhance.response.body.$.data.id}}
|
||||
|
||||
### Step 1.2: Poll generation
|
||||
# @name checkGenDefault
|
||||
GET {{base}}/api/v1/generations/{{genDefaultId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - autoEnhance = true
|
||||
# - originalPrompt = "a simple test image"
|
||||
# - prompt != originalPrompt (was enhanced)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 2: Generate with autoEnhance: false
|
||||
# Should NOT enhance the prompt
|
||||
###############################################################################
|
||||
|
||||
### Step 2.1: Create generation with autoEnhance: false
|
||||
# @name genNoEnhance
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "another test image",
|
||||
"aspectRatio": "1:1",
|
||||
"autoEnhance": false
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@genNoEnhanceId = {{genNoEnhance.response.body.$.data.id}}
|
||||
|
||||
### Step 2.2: Poll generation
|
||||
# @name checkGenNoEnhance
|
||||
GET {{base}}/api/v1/generations/{{genNoEnhanceId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - autoEnhance = false
|
||||
# - originalPrompt = "another test image"
|
||||
# - prompt = "another test image" (same, NOT enhanced)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 3: Generate with autoEnhance: true
|
||||
# Should enhance the prompt
|
||||
###############################################################################
|
||||
|
||||
### Step 3.1: Create generation with explicit autoEnhance: true
|
||||
# @name genExplicitEnhance
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "third test image",
|
||||
"aspectRatio": "1:1",
|
||||
"autoEnhance": true
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@genExplicitId = {{genExplicitEnhance.response.body.$.data.id}}
|
||||
|
||||
### Step 3.2: Poll generation
|
||||
# @name checkGenExplicit
|
||||
GET {{base}}/api/v1/generations/{{genExplicitId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - autoEnhance = true
|
||||
# - originalPrompt = "third test image"
|
||||
# - prompt != originalPrompt (was enhanced)
|
||||
# - prompt is longer and more descriptive
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 4: Verify Enhancement Quality
|
||||
# Enhanced prompt should be longer and more descriptive
|
||||
###############################################################################
|
||||
|
||||
### Step 4.1: Get enhanced generation
|
||||
# @name getEnhancedGen
|
||||
GET {{base}}/api/v1/generations/{{genDefaultId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Enhanced prompt is longer than original
|
||||
# - Enhanced prompt may contain: "photorealistic", "detailed", "scene", etc.
|
||||
# - Compare: prompt.length > originalPrompt.length
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 5: List Generations with autoEnhance Field
|
||||
###############################################################################
|
||||
|
||||
### Step 5.1: List all generations
|
||||
# @name listGens
|
||||
GET {{base}}/api/v1/generations
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify:
|
||||
# - Each generation has autoEnhance field (boolean)
|
||||
# - Some generations have autoEnhance = true
|
||||
# - Some generations have autoEnhance = false
|
||||
|
||||
|
||||
### Step 5.2: Filter by status to see recent ones
|
||||
# @name listSuccessGens
|
||||
GET {{base}}/api/v1/generations?status=success&limit=10
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TEST 6: Verify Response Structure
|
||||
###############################################################################
|
||||
|
||||
### Step 6.1: Get generation and check fields
|
||||
# @name verifyStructure
|
||||
GET {{base}}/api/v1/generations/{{genDefaultId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Expected fields:
|
||||
# - prompt: string (final prompt, possibly enhanced)
|
||||
# - originalPrompt: string (original input prompt)
|
||||
# - autoEnhance: boolean (whether enhancement was applied)
|
||||
# - status: string
|
||||
# - outputImageId: string (on success)
|
||||
# - processingTimeMs: number (on completion)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# ADDITIONAL TEST CASES
|
||||
###############################################################################
|
||||
|
||||
### Complex prompt that might be enhanced differently
|
||||
# @name complexPrompt
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "a cat sitting on a windowsill",
|
||||
"aspectRatio": "16:9"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@complexId = {{complexPrompt.response.body.$.data.id}}
|
||||
|
||||
### Check complex prompt enhancement
|
||||
# @name checkComplexPrompt
|
||||
GET {{base}}/api/v1/generations/{{complexId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Enhanced prompt should add details like lighting, perspective, style, etc.
|
||||
|
||||
|
||||
### Short prompt enhancement
|
||||
# @name shortPrompt
|
||||
POST {{base}}/api/v1/generations
|
||||
Content-Type: application/json
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
{
|
||||
"prompt": "sunset",
|
||||
"aspectRatio": "21:9"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
@shortId = {{shortPrompt.response.body.$.data.id}}
|
||||
|
||||
### Check short prompt enhancement
|
||||
# @name checkShortPrompt
|
||||
GET {{base}}/api/v1/generations/{{shortId}}
|
||||
X-API-Key: {{apiKey}}
|
||||
|
||||
###
|
||||
# Verify: Very short prompts should be significantly enhanced
|
||||
|
||||
|
||||
###############################################################################
|
||||
# NOTES
|
||||
###############################################################################
|
||||
#
|
||||
# Auto-Enhance Feature:
|
||||
# - Default: autoEnhance = true (prompts are enhanced by AI)
|
||||
# - Set autoEnhance: false to disable enhancement
|
||||
# - Enhanced prompts are more detailed and descriptive
|
||||
#
|
||||
# Response Fields:
|
||||
# - prompt: The final prompt (enhanced if autoEnhance was true)
|
||||
# - originalPrompt: The user's original input
|
||||
# - autoEnhance: Boolean flag indicating if enhancement was applied
|
||||
#
|
||||
# Enhancement adds:
|
||||
# - Descriptive adjectives
|
||||
# - Lighting and atmosphere details
|
||||
# - Perspective and composition hints
|
||||
# - Style and rendering suggestions
|
||||
#
|
||||
Loading…
Reference in New Issue