11
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-16 21:47:20 +08:00
parent 2456c10c63
commit 09d4c0c31b
79 changed files with 11873 additions and 2341 deletions

View File

@@ -1,12 +1,12 @@
import assert from 'node:assert/strict';
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import fs from 'node:fs';
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import type { AppContext } from '../context.js';
import { type AppConfig } from '../config.js';
import type { AppContext } from '../context.js';
import { generateSceneImage } from './sceneImageService.js';
const PNG_BUFFER = Buffer.from(
@@ -24,7 +24,7 @@ function createTestConfig(
dashScope: {
baseUrl: dashScopeBaseUrl,
apiKey: 'test-dashscope-key',
imageModel: 'wan2.7-image',
imageModel: 'wan2.2-t2i-flash',
requestTimeoutMs: 5_000,
},
} as AppConfig;
@@ -92,11 +92,8 @@ async function withHttpServer<T>(
}
}
test('generateSceneImage uploads a public reference image as a data url and saves the generated scene', async () => {
test('generateSceneImage uses wan2.2-t2i-flash text-to-image payload and saves the generated scene', async () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genarrative-scene-image-'));
const publicDir = path.join(tempRoot, 'public');
fs.mkdirSync(path.join(publicDir, 'scene_bg'), { recursive: true });
fs.writeFileSync(path.join(publicDir, 'scene_bg', 'reference-layout.png'), PNG_BUFFER);
const capturedRequests: Array<{
pathname: string;
@@ -164,7 +161,6 @@ test('generateSceneImage uploads a public reference image as a data url and save
profileId: 'world-1',
landmarkName: '旧港灯塔',
landmarkId: 'landmark-1',
referenceImageSrc: '/scene_bg/reference-layout.png',
});
assert.equal(result.ok, true);
@@ -177,6 +173,7 @@ test('generateSceneImage uploads a public reference image as a data url and save
assert.ok(createRequest?.bodyText);
const createPayload = JSON.parse(createRequest.bodyText) as {
model: string;
input: {
messages: Array<{
content: Array<{ text?: string; image?: string }>;
@@ -188,8 +185,9 @@ test('generateSceneImage uploads a public reference image as a data url and save
};
const content = createPayload.input.messages[0]?.content ?? [];
assert.equal(createPayload.model, 'wan2.2-t2i-flash');
assert.equal(content[0]?.text, '海雾港口像素风场景');
assert.match(content[1]?.image ?? '', /^data:image\/png;base64,/u);
assert.equal(content.length, 1);
assert.equal(createPayload.parameters.negative_prompt, '模糊');
const savedImagePath = path.join(tempRoot, 'public', result.imageSrc.slice(1));
@@ -197,3 +195,105 @@ test('generateSceneImage uploads a public reference image as a data url and save
},
);
});
test('generateSceneImage uses qwen-image-2.0 edit flow when a reference image is provided', async () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genarrative-scene-image-'));
const publicDir = path.join(tempRoot, 'public');
fs.mkdirSync(path.join(publicDir, 'scene_bg'), { recursive: true });
fs.writeFileSync(path.join(publicDir, 'scene_bg', 'reference-layout.png'), PNG_BUFFER);
const capturedRequests: Array<{
pathname: string;
bodyText?: string;
}> = [];
await withHttpServer(
(baseUrl) => async (req, res) => {
const url = new URL(req.url || '/', baseUrl);
const bodyText =
req.method === 'POST' ? (await readRequestBody(req)).toString('utf8') : undefined;
capturedRequests.push({
pathname: url.pathname,
bodyText,
});
if (
req.method === 'POST' &&
url.pathname === '/api/v1/services/aigc/multimodal-generation/generation'
) {
sendJson(res, {
output: {
choices: [
{
message: {
content: [
{
image: `${baseUrl}/downloads/reference-scene.png`,
},
],
},
},
],
},
});
return;
}
if (req.method === 'GET' && url.pathname === '/downloads/reference-scene.png') {
res.statusCode = 200;
res.setHeader('Content-Type', 'image/png');
res.end(PNG_BUFFER);
return;
}
res.statusCode = 404;
res.end('not found');
},
async (dashScopeBaseUrl) => {
const context = {
config: createTestConfig(tempRoot, `${dashScopeBaseUrl}/api/v1`),
} as AppContext;
const result = await generateSceneImage(context, {
prompt: '废墟月台像素风场景',
negativePrompt: '模糊',
size: '1280*720',
worldName: '碎轨边境',
profileId: 'world-2',
landmarkName: '裂轨月台',
landmarkId: 'landmark-2',
referenceImageSrc: '/scene_bg/reference-layout.png',
});
assert.equal(result.ok, true);
assert.equal(result.model, 'qwen-image-2.0');
assert.match(result.taskId, /^scene-edit-/u);
assert.equal(
capturedRequests.some(
(entry) => entry.pathname === '/api/v1/tasks/scene-task-1',
),
false,
);
const createRequest = capturedRequests.find(
(entry) =>
entry.pathname === '/api/v1/services/aigc/multimodal-generation/generation',
);
assert.ok(createRequest?.bodyText);
const createPayload = JSON.parse(createRequest.bodyText) as {
model: string;
input: {
messages: Array<{
content: Array<{ text?: string; image?: string }>;
}>;
};
};
const content = createPayload.input.messages[0]?.content ?? [];
assert.equal(createPayload.model, 'qwen-image-2.0');
assert.match(content[0]?.image ?? '', /^data:image\/png;base64,/u);
assert.equal(content[1]?.text, '废墟月台像素风场景');
},
);
});