From fba243cfbdb8e6b145c2914d04c05a49602dd45c Mon Sep 17 00:00:00 2001 From: Oleg Proskurin Date: Sun, 23 Nov 2025 23:01:46 +0700 Subject: [PATCH] feat: add test --- docs/api/image-generation.md | 95 ++++++++++ tests/api/02-basic.rest | 332 +++++++++++++++++++++++++++++++++++ 2 files changed, 427 insertions(+) create mode 100644 tests/api/02-basic.rest diff --git a/docs/api/image-generation.md b/docs/api/image-generation.md index 5795d38..9f8060a 100644 --- a/docs/api/image-generation.md +++ b/docs/api/image-generation.md @@ -672,6 +672,101 @@ The v1 API supports a 3-tier alias resolution system for referencing images. --- +## Response Formats + +### Generation Response + +Generation responses include fields for prompt enhancement tracking: + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "projectId": "57c7f7f4d-47de-4d70-9ebd-3807a0b63746", + "prompt": "A photorealistic establishing shot of a luxurious motor yacht...", + "originalPrompt": "шикарная моторная яхта движется по живописному озеру...", + "autoEnhance": true, + "aspectRatio": "16:9", + "status": "success", + "outputImageId": "7c4ccf47-41ce-4718-afbc-8c553b2c631a", + "outputImage": { ... }, + "referenceImages": [], + "flowId": null, + "processingTimeMs": 8980, + "cost": null, + "retryCount": 0, + "errorMessage": null, + "meta": {}, + "createdAt": "2025-11-23T13:47:00.127Z", + "updatedAt": "2025-11-23T13:47:09.107Z" +} +``` + +**Prompt Enhancement Fields:** + +- `prompt` - The actual prompt used for generation (enhanced if `autoEnhance: true`, original if `autoEnhance: false`) +- `originalPrompt` - The user's original input prompt (always populated for transparency) +- `autoEnhance` - Boolean indicating if prompt enhancement was applied + +**Semantics:** +- When `autoEnhance: true`: + - `originalPrompt` = user's original input + - `prompt` = AI-enhanced version used for generation + - Example: Original "a cat" → Enhanced "A photorealistic close-up portrait of a cat..." + +- When `autoEnhance: false`: + - `originalPrompt` = user's original input + - `prompt` = same as originalPrompt (no enhancement) + - Both fields contain identical values + +**Default Behavior:** `autoEnhance` defaults to `true` if not explicitly set to `false`. + +--- + +### Image Response + +Image responses include actual dimensions and storage access: + +```json +{ + "id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a", + "projectId": "57c7f7f4d-47de-4d70-9ebd-3807a0b63746", + "flowId": null, + "storageKey": "default/57c7f7f4d-47de-4d70-9ebd-3807a0b63746/generated/2025-11/gen_fd14839b.png", + "storageUrl": "http://localhost:9000/banatie/default/57c7f7f4d-47de-4d70-9ebd-3807a0b63746/generated/gen_fd14839b.png", + "mimeType": "image/jpeg", + "fileSize": 1909246, + "width": 1792, + "height": 1024, + "source": "generated", + "alias": null, + "focalPoint": null, + "fileHash": null, + "generationId": "fd14839b-d42e-4be9-93b3-e2fb67f7af0d", + "apiKeyId": "988cb71e-0021-4217-a536-734b097a87b3", + "meta": {}, + "createdAt": "2025-11-23T13:47:00.127Z", + "updatedAt": "2025-11-23T13:47:00.127Z", + "deletedAt": null +} +``` + +**Key Fields:** + +- `width` / `height` - Actual image dimensions in pixels (automatically extracted from generated images) +- `storageUrl` - Direct URL to access the image file (use this for image display) +- `storageKey` - Internal MinIO storage path +- `source` - Image origin: "generated" (AI-generated) or "uploaded" (user upload) +- `alias` - Project-scoped alias (e.g., "@logo"), null if not assigned +- `focalPoint` - Cropping focal point {x: 0.0-1.0, y: 0.0-1.0}, null if not set + +**Image Access:** + +To display an image, use the `storageUrl` field which provides direct access to the image file in MinIO storage. For public CDN access, use the `/cdn/:orgSlug/:projectSlug/img/:alias` endpoint. + +**Note:** Previous versions included a `url` field that pointed to a non-existent `/download` endpoint. This field has been removed. Always use `storageUrl` for direct image access. + +--- + ## Error Codes | Code | Description | diff --git a/tests/api/02-basic.rest b/tests/api/02-basic.rest new file mode 100644 index 0000000..4279529 --- /dev/null +++ b/tests/api/02-basic.rest @@ -0,0 +1,332 @@ +@base = http://localhost:3000 +@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d + +############################################################################### +# IMAGE UPLOAD & CRUD TESTS +# Tests: Upload, list, filter, pagination, metadata updates, alias management +############################################################################### + +### Test 1.1: Upload image with project-scoped alias +# @name uploadWithAlias +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" + +@test-logo +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="description" + +Test logo image for CRUD tests +------WebKitFormBoundary7MA4YWxkTrZu0gW-- + +### + +@uploadedImageId = {{uploadWithAlias.response.body.$.data.id}} +@uploadedImageAlias = {{uploadWithAlias.response.body.$.data.alias}} +@uploadedImageSource = {{uploadWithAlias.response.body.$.data.source}} + +### Test 1.2: Verify uploaded image details +# Expected: alias = @test-logo, source = uploaded +GET {{base}}/api/v1/images/{{uploadedImageId}} +X-API-Key: {{apiKey}} + +### + +### Test 2.1: Upload image without alias +# @name uploadWithoutAlias +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="description" + +Image without alias +------WebKitFormBoundary7MA4YWxkTrZu0gW-- + +### + +@uploadedImageId2 = {{uploadWithoutAlias.response.body.$.data.id}} + +### Test 2.2: Verify image has no alias +# Expected: alias = null +GET {{base}}/api/v1/images/{{uploadedImageId2}} +X-API-Key: {{apiKey}} + +### + +### Test 3: List all images +# Expected: Returns array with pagination +GET {{base}}/api/v1/images +X-API-Key: {{apiKey}} + +### + +### Test 4: List images - filter by source=uploaded +# Expected: All results have source="uploaded" +GET {{base}}/api/v1/images?source=uploaded +X-API-Key: {{apiKey}} + +### + +### Test 5: List images with pagination +# Expected: limit=3, offset=0, hasMore=true/false +GET {{base}}/api/v1/images?limit=3&offset=0 +X-API-Key: {{apiKey}} + +### + +### Test 6: Get image by ID +# Expected: Returns full image details +GET {{base}}/api/v1/images/{{uploadedImageId}} +X-API-Key: {{apiKey}} + +### + +### Test 7: Resolve project-scoped alias +# Expected: Resolves to uploadedImageId, scope=project +GET {{base}}/api/v1/images/resolve/@test-logo +X-API-Key: {{apiKey}} + +### + +### Test 8.1: Update image metadata (focal point + meta) +# @name updateMetadata +PUT {{base}}/api/v1/images/{{uploadedImageId}} +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "focalPoint": { + "x": 0.5, + "y": 0.3 + }, + "meta": { + "description": "Updated description", + "tags": ["test", "logo", "updated"] + } +} + +### + +### Test 8.2: Verify metadata update +# Expected: focalPoint x=0.5, y=0.3, meta has tags +GET {{base}}/api/v1/images/{{uploadedImageId}} +X-API-Key: {{apiKey}} + +### + +### Test 9.1: Update image alias (dedicated endpoint) +# @name updateAlias +PUT {{base}}/api/v1/images/{{uploadedImageId}}/alias +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "alias": "@new-test-logo" +} + +### + +### Test 9.2: Verify new alias works +# Expected: Resolves to same uploadedImageId +GET {{base}}/api/v1/images/resolve/@new-test-logo +X-API-Key: {{apiKey}} + +### + +### Test 10: Verify old alias doesn't work after update +# Expected: 404 - Alias not found +GET {{base}}/api/v1/images/resolve/@test-logo +X-API-Key: {{apiKey}} + +### + +### Test 11.1: Remove image alias +# @name removeAlias +PUT {{base}}/api/v1/images/{{uploadedImageId}}/alias +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "alias": null +} + +### + +### Test 11.2: Verify image exists but has no alias +# Expected: alias = null +GET {{base}}/api/v1/images/{{uploadedImageId}} +X-API-Key: {{apiKey}} + +### + +### Test 11.3: Verify alias resolution fails +# Expected: 404 - Alias not found +GET {{base}}/api/v1/images/resolve/@new-test-logo +X-API-Key: {{apiKey}} + +### + +### Test 12.1: Reassign alias for reference image test +# @name reassignAlias +PUT {{base}}/api/v1/images/{{uploadedImageId}}/alias +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "alias": "@reference-logo" +} + +### + +### Test 12.2: Generate with manual reference image +# @name genWithReference +POST {{base}}/api/v1/generations +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "prompt": "A product photo with the logo in corner", + "aspectRatio": "1:1", + "referenceImages": ["@reference-logo"] +} + +### + +@genWithReferenceId = {{genWithReference.response.body.$.data.id}} + +### Test 12.3: Poll generation status +# Run this multiple times until status = success +GET {{base}}/api/v1/generations/{{genWithReferenceId}} +X-API-Key: {{apiKey}} + +### + +### Test 12.4: Verify referenced images tracked +# Expected: referencedImages array contains @reference-logo +GET {{base}}/api/v1/generations/{{genWithReferenceId}} +X-API-Key: {{apiKey}} + +### + +### Test 13.1: Generate with auto-detected reference in prompt +# @name genAutoDetect +POST {{base}}/api/v1/generations +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "prompt": "Create banner using @reference-logo with blue background", + "aspectRatio": "16:9" +} + +### + +@genAutoDetectId = {{genAutoDetect.response.body.$.data.id}} + +### Test 13.2: Poll until complete +GET {{base}}/api/v1/generations/{{genAutoDetectId}} +X-API-Key: {{apiKey}} + +### + +### Test 13.3: Verify auto-detection worked +# Expected: referencedImages contains @reference-logo +GET {{base}}/api/v1/generations/{{genAutoDetectId}} +X-API-Key: {{apiKey}} + +### + +### Test 14.1: Generate with project alias assignment +# @name genWithAlias +POST {{base}}/api/v1/generations +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "prompt": "A hero banner image", + "aspectRatio": "21:9", + "alias": "@hero-banner" +} + +### + +@genWithAliasId = {{genWithAlias.response.body.$.data.id}} + +### Test 14.2: Poll until complete +GET {{base}}/api/v1/generations/{{genWithAliasId}} +X-API-Key: {{apiKey}} + +### + +@heroImageId = {{genWithAlias.response.body.$.data.outputImageId}} + +### Test 14.3: Verify alias assigned to output image +# Expected: alias = @hero-banner +GET {{base}}/api/v1/images/{{heroImageId}} +X-API-Key: {{apiKey}} + +### + +### Test 14.4: Verify alias resolution works +# Expected: Resolves to heroImageId +GET {{base}}/api/v1/images/resolve/@hero-banner +X-API-Key: {{apiKey}} + +### + +### Test 15.1: Alias conflict - create second generation with same alias +# @name genConflict +POST {{base}}/api/v1/generations +X-API-Key: {{apiKey}} +Content-Type: application/json + +{ + "prompt": "A different hero image", + "aspectRatio": "21:9", + "alias": "@hero-banner" +} + +### + +@genConflictId = {{genConflict.response.body.$.data.id}} + +### Test 15.2: Poll until complete +GET {{base}}/api/v1/generations/{{genConflictId}} +X-API-Key: {{apiKey}} + +### + +@secondHeroImageId = {{genConflict.response.body.$.data.outputImageId}} + +### Test 15.3: Verify second image has the alias +# Expected: Resolves to secondHeroImageId (not heroImageId) +GET {{base}}/api/v1/images/resolve/@hero-banner +X-API-Key: {{apiKey}} + +### + +### Test 15.4: Verify first image lost the alias but still exists +# Expected: alias = null, image still exists +GET {{base}}/api/v1/images/{{heroImageId}} +X-API-Key: {{apiKey}} + +### + +############################################################################### +# END OF IMAGE UPLOAD & CRUD TESTS +###############################################################################