import { PNG_BASE64, imageFormRequest, readSseEvents, startHttpConnectProxy, startImagesJsonUpstream, startResponsesImageUpstream, startStreamingImageUpstream, startStreamingResponsesImageUpstream } from './route-test-helpers'; import { registerRouteTestLifecycle } from './route-test-setup'; import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; registerRouteTestLifecycle(); describe('POST /api/images backend defaults and security boundaries', { concurrency: false }, () => { it('normalizes non-streamed page image bytes to the requested output format', async () => { const { POST } = await import('./route'); const upstream = await startImagesJsonUpstream(async () => ({ data: [{ b64_json: PNG_BASE64 }] })); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', stream: false, streamMode: 'non_stream', outputFormat: 'webp' }) ); assert.equal(response.status, 200); const body = (await response.json()) as { images?: Array> }; assert.equal(body.images?.[0]?.output_format, 'webp'); assert.equal( Buffer.from(String(body.images?.[0]?.b64_json || ''), 'base64').toString('ascii', 8, 12), 'WEBP' ); } finally { await upstream.close(); } }); it('uses IMAGE_GENERATION_BACKEND as the route default when the request omits imageBackend', async () => { process.env.ENABLE_RESPONSES_IMAGE_BACKEND = 'true'; process.env.IMAGE_GENERATION_BACKEND = 'responses'; process.env.OPENAI_RESPONSES_API_MODEL = 'gpt-4.1'; const { POST } = await import('./route'); let upstreamBody = ''; const upstream = await startResponsesImageUpstream(async (body) => { upstreamBody = body; return { output: [ { type: 'image_generation_call', status: 'completed', result: PNG_BASE64 } ] }; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', stream: false, streamMode: 'non_stream' }) ); assert.equal(response.status, 200); const body = (await response.json()) as { images?: Array> }; assert.equal(body.images?.[0]?.b64_json, PNG_BASE64); const upstreamJson = JSON.parse(upstreamBody) as Record; assert.equal(upstreamJson.model, 'gpt-4.1'); assert.equal(upstreamJson.stream, false); } finally { await upstream.close(); } }); it('downloads same-origin GPT2Image URL results before persisting Images API JSON responses', async () => { const { POST } = await import('./route'); let imageDownloadCount = 0; const upstream = await startImagesJsonUpstream(async (_body, url) => { if (url === '/generated/final.png') { imageDownloadCount += 1; return Buffer.from(PNG_BASE64, 'base64'); } return { data: [{ url: '/generated/final.png' }] }; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', stream: false, streamMode: 'non_stream' }) ); assert.equal(response.status, 200); const body = (await response.json()) as { images?: Array> }; assert.equal(body.images?.[0]?.b64_json, PNG_BASE64); assert.equal(imageDownloadCount, 1); } finally { await upstream.close(); } }); it('sends page API requests through the configured global upstream proxy', async () => { const { POST } = await import('./route'); const upstream = await startImagesJsonUpstream(async () => ({ data: [{ b64_json: PNG_BASE64 }] })); const proxy = await startHttpConnectProxy(); process.env.OPENAI_UPSTREAM_PROXY_URL = proxy.url; try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', stream: false, streamMode: 'non_stream' }) ); assert.equal(response.status, 200); assert.ok(proxy.connectTargets.length > 0); assert.ok(proxy.connectTargets.every((target) => target === new URL(upstream.baseUrl).host)); } finally { await proxy.close(); await upstream.close(); } }); it('uses a channel proxy instead of the global upstream proxy', async () => { const { POST } = await import('./route'); const upstream = await startImagesJsonUpstream(async () => ({ data: [{ b64_json: PNG_BASE64 }] })); const globalProxy = await startHttpConnectProxy(); const channelProxy = await startHttpConnectProxy(); process.env.OPENAI_UPSTREAM_PROXY_URL = globalProxy.url; process.env.OPENAI_CHANNEL_1_ID = 'proxied-channel'; process.env.OPENAI_CHANNEL_1_BASE_URL = upstream.baseUrl; process.env.OPENAI_CHANNEL_1_API_KEYS = 'channel-key'; process.env.OPENAI_CHANNEL_1_PROXY_URL = channelProxy.url; process.env.OPENAI_CHANNEL_1_REQUEST_MODES = 'images-non-stream'; try { const response = await POST( imageFormRequest({ stream: false, streamMode: 'non_stream' }) ); assert.equal(response.status, 200); assert.ok(channelProxy.connectTargets.length > 0); assert.ok(channelProxy.connectTargets.every((target) => target === new URL(upstream.baseUrl).host)); assert.deepEqual(globalProxy.connectTargets, []); } finally { await channelProxy.close(); await globalProxy.close(); await upstream.close(); } }); it('rejects cross-origin GPT2Image URL results before downloading them', async () => { const { POST } = await import('./route'); const upstream = await startImagesJsonUpstream(async () => { return { data: [{ url: 'https://other.example.test/generated/final.png' }] }; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', stream: false, streamMode: 'non_stream' }) ); assert.equal(response.status, 502); const body = (await response.json()) as Record; assert.match(String(body.error), /同源/); } finally { await upstream.close(); } }); it('applies the image backend env default to edit requests', async () => { process.env.ENABLE_RESPONSES_IMAGE_BACKEND = 'true'; process.env.OPENAI_RESPONSES_API_MODEL = 'gpt-4.1'; process.env.IMAGE_GENERATION_BACKEND = 'responses'; const { POST } = await import('./route'); let upstreamBody = ''; const upstream = await startResponsesImageUpstream(async (body) => { upstreamBody = body; return { output: [ { type: 'image_generation_call', status: 'completed', result: PNG_BASE64 } ] }; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', mode: 'edit', stream: false, streamMode: 'non_stream' }) ); assert.equal(response.status, 200); const body = (await response.json()) as { images?: Array> }; assert.equal(body.images?.[0]?.b64_json, PNG_BASE64); const upstreamJson = JSON.parse(upstreamBody) as { tools?: Array>; input?: Array<{ content?: Array> }>; }; assert.equal(upstreamJson.tools?.[0]?.type, 'image_generation'); assert.equal(upstreamJson.tools?.[0]?.action, 'edit'); assert.equal( upstreamJson.input?.[0]?.content?.some((item) => item.type === 'input_image'), true ); } finally { await upstream.close(); } }); it('applies Responses streaming strategy env defaults to edit streams', async () => { process.env.ENABLE_RESPONSES_IMAGE_BACKEND = 'true'; process.env.OPENAI_RESPONSES_API_MODEL = 'gpt-4.1'; process.env.IMAGE_GENERATION_BACKEND = 'responses'; process.env.IMAGE_STREAMING_STRATEGY = 'responses-sse'; const { POST } = await import('./route'); let upstreamBody = ''; const upstream = await startStreamingResponsesImageUpstream(async (body) => { upstreamBody = body; return [ { event: 'response.output_item.done', data: { type: 'response.output_item.done', item: { type: 'image_generation_call', status: 'completed', result: PNG_BASE64 } } } ]; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', mode: 'edit', stream: true }) ); assert.equal(response.status, 200); assert.equal(response.headers.get('content-type'), 'text/event-stream'); const events = await readSseEvents(response); assert.deepEqual( events.map((event) => event.type), ['completed', 'done'] ); const upstreamJson = JSON.parse(upstreamBody) as Record; assert.equal(upstreamJson.stream, true); assert.equal( upstreamJson.tool_choice && (upstreamJson.tool_choice as Record).type, 'image_generation' ); assert.equal(Array.isArray(upstreamJson.tools), true); } finally { await upstream.close(); } }); it('accepts snake-case Responses backend controls on edit page SSE requests', async () => { process.env.ENABLE_RESPONSES_IMAGE_BACKEND = 'true'; process.env.OPENAI_RESPONSES_API_MODEL = 'gpt-4.1'; const { POST } = await import('./route'); let upstreamBody = ''; const upstream = await startStreamingResponsesImageUpstream(async (body) => { upstreamBody = body; return [ { event: 'response.output_item.done', data: { type: 'response.output_item.done', item: { type: 'image_generation_call', status: 'completed', result: PNG_BASE64 } } } ]; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', mode: 'edit', stream: true, imageBackend: 'responses-image-generation', imageBackendField: 'image_backend', imageStreamingStrategy: 'responses-sse', imageStreamingStrategyField: 'image_streaming_strategy', partialImages: '1' }) ); assert.equal(response.status, 200); assert.equal(response.headers.get('content-type'), 'text/event-stream'); const events = await readSseEvents(response); assert.deepEqual( events.map((event) => event.type), ['completed', 'done'] ); const upstreamJson = JSON.parse(upstreamBody) as Record; assert.equal(upstreamJson.stream, true); assert.equal( upstreamJson.tool_choice && (upstreamJson.tool_choice as Record).type, 'image_generation' ); } finally { await upstream.close(); } }); it('lets explicit edit backend and streaming strategy override env defaults', async () => { process.env.ENABLE_RESPONSES_IMAGE_BACKEND = 'true'; process.env.OPENAI_RESPONSES_API_MODEL = 'gpt-4.1'; process.env.IMAGE_GENERATION_BACKEND = 'responses'; process.env.IMAGE_STREAMING_STRATEGY = 'responses-sse'; const { POST } = await import('./route'); let upstreamUrl = ''; let upstreamBody = ''; const upstream = await startStreamingImageUpstream(async (body, url) => { upstreamUrl = url; upstreamBody = body; return [ { event: 'image_edit.completed', data: { type: 'image_edit.completed', b64_json: PNG_BASE64 } } ]; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', mode: 'edit', stream: true, imageBackend: 'images-api', imageStreamingStrategy: 'auto' }) ); assert.equal(response.status, 200); assert.equal(response.headers.get('content-type'), 'text/event-stream'); const events = await readSseEvents(response); assert.deepEqual( events.map((event) => event.type), ['completed', 'done'] ); assert.equal(upstreamUrl, '/v1/images/edits'); assert.match(upstreamBody, /name="stream"/); assert.match(upstreamBody, /name="partial_images"/); } finally { await upstream.close(); } }); it('rejects incompatible edit backend and streaming strategy defaults before contacting upstream', async () => { process.env.ENABLE_RESPONSES_IMAGE_BACKEND = 'true'; process.env.OPENAI_RESPONSES_API_MODEL = 'gpt-4.1'; process.env.IMAGE_GENERATION_BACKEND = 'responses'; process.env.IMAGE_STREAMING_STRATEGY = 'newapi-keepalive-sse'; const { POST } = await import('./route'); let upstreamCalls = 0; const upstream = await startStreamingImageUpstream(async () => { upstreamCalls += 1; return [ { event: 'image_edit.completed', data: { type: 'image_edit.completed', b64_json: PNG_BASE64 } } ]; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', mode: 'edit', stream: true }) ); assert.equal(response.status, 400); const body = (await response.json()) as Record; assert.match(String(body.error), /Responses image_generation/); assert.equal(upstreamCalls, 0); } finally { await upstream.close(); } }); it('keeps Images API edit streams available when env defaults stay on Images API', async () => { process.env.IMAGE_GENERATION_BACKEND = 'images-api'; process.env.IMAGE_STREAMING_STRATEGY = 'auto'; const { POST } = await import('./route'); let upstreamUrl = ''; let upstreamBody = ''; const upstream = await startStreamingImageUpstream(async (body, url) => { upstreamUrl = url; upstreamBody = body; return [ { event: 'image_edit.completed', data: { type: 'image_edit.completed', b64_json: PNG_BASE64 } } ]; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', mode: 'edit', stream: true }) ); assert.equal(response.status, 200); assert.equal(response.headers.get('content-type'), 'text/event-stream'); const events = await readSseEvents(response); assert.deepEqual( events.map((event) => event.type), ['completed', 'done'] ); assert.equal(upstreamUrl, '/v1/images/edits'); assert.match(upstreamBody, /name="stream"/); assert.match(upstreamBody, /name="partial_images"/); } finally { await upstream.close(); } }); it('reports invalid image upstream env configuration as a server error before contacting upstream', async () => { process.env.IMAGE_STREAMING_STRATEGY = 'not-a-strategy'; const { POST } = await import('./route'); let upstreamCalls = 0; const upstream = await startStreamingImageUpstream(async () => { upstreamCalls += 1; return []; }); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', stream: false }) ); assert.equal(response.status, 500); const body = (await response.json()) as Record; assert.match(String(body.error), /IMAGE_STREAMING_STRATEGY/); assert.equal(upstreamCalls, 0); } finally { await upstream.close(); } }); it('rejects remote plain-http API base URLs before forwarding API keys', async () => { const { POST } = await import('./route'); const response = await POST( imageFormRequest({ apiBaseUrl: 'http://api.example.com/v1', apiKey: 'test-key', stream: false }) ); assert.equal(response.status, 400); const body = (await response.json()) as Record; assert.match(String(body.error), /远程 HTTP API URL/); }); it('treats blank APP_PASSWORD as disabled for page SSE auth', async () => { process.env.APP_PASSWORD = ' '; const { POST } = await import('./route'); const upstream = await startStreamingImageUpstream(async () => [ { event: 'image_generation.completed', data: { type: 'image_generation.completed', b64_json: PNG_BASE64 } } ]); try { const response = await POST( imageFormRequest({ apiBaseUrl: upstream.baseUrl, apiKey: 'test-key', stream: true }) ); assert.equal(response.status, 200); const events = await readSseEvents(response); assert.deepEqual( events.map((event) => event.type), ['completed', 'done'] ); } finally { await upstream.close(); } }); });