156 lines
6.3 KiB
JavaScript
156 lines
6.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { createAssetGatewayConfigService } from './asset-gateway.mjs';
|
|
|
|
function createPool() {
|
|
let global = null;
|
|
const plugins = new Map();
|
|
return {
|
|
async query(sql, params = []) {
|
|
const compact = sql.replace(/\s+/g, ' ').trim();
|
|
if (compact.startsWith('SELECT * FROM h5_asset_gateway_config')) return [[global].filter(Boolean)];
|
|
if (compact === 'SELECT * FROM h5_asset_plugin_configs ORDER BY plugin_id') {
|
|
return [[...plugins.values()].sort((a, b) => a.plugin_id.localeCompare(b.plugin_id))];
|
|
}
|
|
if (compact.startsWith('SELECT * FROM h5_asset_plugin_configs WHERE plugin_id')) {
|
|
return [[plugins.get(params[0])].filter(Boolean)];
|
|
}
|
|
if (compact.startsWith('INSERT INTO h5_asset_gateway_config')) {
|
|
global = { config_key: params[0], enabled: params[1], updated_by: params[2], updated_at: params[3] };
|
|
return [{}];
|
|
}
|
|
if (compact.startsWith('UPDATE h5_asset_gateway_config')) {
|
|
global = { ...global, enabled: params[0], updated_by: params[1], updated_at: params[2] };
|
|
return [{}];
|
|
}
|
|
if (compact.startsWith('INSERT INTO h5_asset_plugin_configs')) {
|
|
plugins.set(params[1], {
|
|
id: params[0], plugin_id: params[1], enabled: params[2], provider: params[3],
|
|
llm_provider_key_id: params[4], llm_model: params[5], config_json: params[6],
|
|
updated_by: params[7], created_at: params[8], updated_at: params[9],
|
|
});
|
|
return [{}];
|
|
}
|
|
if (compact.startsWith('UPDATE h5_asset_plugin_configs')) {
|
|
const pluginId = params[7];
|
|
plugins.set(pluginId, {
|
|
...plugins.get(pluginId), enabled: params[0], provider: params[1],
|
|
llm_provider_key_id: params[2], llm_model: params[3], config_json: params[4],
|
|
updated_by: params[5], updated_at: params[6],
|
|
});
|
|
return [{}];
|
|
}
|
|
throw new Error(`Unexpected SQL: ${compact}`);
|
|
},
|
|
};
|
|
}
|
|
|
|
const llmProviderService = {
|
|
async listKeys() {
|
|
return [{
|
|
id: 'provider-key-1', name: 'Primary', providerId: 'custom', status: 'active', models: ['fast-model'],
|
|
}];
|
|
},
|
|
};
|
|
|
|
test('asset gateway stays disabled by default and never blocks callers that choose to fall back', async () => {
|
|
const service = createAssetGatewayConfigService(createPool(), { llmProviderService });
|
|
const config = await service.getConfig();
|
|
assert.equal(config.enabled, false);
|
|
assert.equal(config.plugins.length, 4);
|
|
assert.deepEqual(await service.resolvePlugin('asset-generate'), {
|
|
ok: false,
|
|
code: 'gateway_disabled',
|
|
message: '资产能力未启用,可安全降级到原有流程',
|
|
});
|
|
});
|
|
|
|
test('asset plugins no longer bind per-plugin LLM models', async () => {
|
|
const service = createAssetGatewayConfigService(createPool(), { llmProviderService });
|
|
await service.updateGlobalConfig({ enabled: true }, { updatedBy: 'admin-1' });
|
|
const result = await service.updatePluginConfig('asset-generate', {
|
|
enabled: true,
|
|
provider: 'flux-schnell',
|
|
llmProviderKeyId: 'provider-key-1',
|
|
llmModel: 'fast-model',
|
|
}, { updatedBy: 'admin-1' });
|
|
|
|
assert.equal(result.ok, true);
|
|
const plugin = result.config.plugins.find((item) => item.pluginId === 'asset-generate');
|
|
assert.equal(plugin.enabled, true);
|
|
assert.equal(plugin.provider, 'flux-schnell');
|
|
assert.equal(plugin.llmProviderKeyId, null);
|
|
assert.equal(plugin.llmModel, null);
|
|
assert.equal((await service.resolvePlugin('asset-generate')).ok, true);
|
|
});
|
|
|
|
test('asset plugins reject unknown providers', async () => {
|
|
const service = createAssetGatewayConfigService(createPool(), { llmProviderService });
|
|
const invalidProvider = await service.updatePluginConfig('asset-generate', {
|
|
enabled: true,
|
|
provider: 'unknown-provider',
|
|
});
|
|
assert.equal(invalidProvider.ok, false);
|
|
assert.match(invalidProvider.message, /Provider/);
|
|
|
|
const transform = await service.updatePluginConfig('asset-transform', {
|
|
enabled: true,
|
|
provider: 'sharp',
|
|
llmProviderKeyId: 'provider-key-1',
|
|
llmModel: 'fast-model',
|
|
});
|
|
assert.equal(transform.ok, true);
|
|
const plugin = transform.config.plugins.find((item) => item.pluginId === 'asset-transform');
|
|
assert.equal(plugin.llmProviderKeyId, null);
|
|
assert.equal(plugin.llmModel, null);
|
|
});
|
|
|
|
test('image_make purposes are disabled by default and resolved independently', async () => {
|
|
const service = createAssetGatewayConfigService(createPool(), { llmProviderService });
|
|
await service.updateGlobalConfig({ enabled: true }, { updatedBy: 'admin-1' });
|
|
const result = await service.updatePluginConfig('asset-generate', {
|
|
enabled: true,
|
|
provider: 'image_make',
|
|
purposes: { hero: true, feed_cover: true },
|
|
}, { updatedBy: 'admin-1' });
|
|
|
|
assert.equal(result.ok, true);
|
|
const plugin = result.config.plugins.find((item) => item.pluginId === 'asset-generate');
|
|
assert.deepEqual(plugin.purposes, {
|
|
inline_image: false,
|
|
hero: true,
|
|
card_cover: false,
|
|
feed_cover: true,
|
|
});
|
|
assert.equal((await service.resolveImageGenerationPurpose('hero')).ok, true);
|
|
assert.deepEqual(await service.resolveImageGenerationPurpose('feed_cover'), {
|
|
ok: false,
|
|
code: 'reuse_hero_required',
|
|
message: '页面头图已启用:请只生成一次 hero,并从该主图派生卡片封面和信息流缩略图',
|
|
sourcePurpose: 'hero',
|
|
});
|
|
assert.equal(plugin.llmProviderKeyId, null);
|
|
assert.equal(plugin.llmModel, null);
|
|
assert.deepEqual(await service.resolveImageGenerationPurpose('card_cover'), {
|
|
ok: false,
|
|
code: 'purpose_disabled',
|
|
message: '该图片用途未启用,可安全降级到原有流程',
|
|
});
|
|
});
|
|
|
|
test('derived cover purpose can generate one source when hero output is disabled', async () => {
|
|
const service = createAssetGatewayConfigService(createPool(), { llmProviderService });
|
|
await service.updateGlobalConfig({ enabled: true }, { updatedBy: 'admin-1' });
|
|
await service.updatePluginConfig('asset-generate', {
|
|
enabled: true,
|
|
provider: 'image_make',
|
|
purposes: { hero: false, card_cover: true },
|
|
}, { updatedBy: 'admin-1' });
|
|
|
|
const resolved = await service.resolveImageGenerationPurpose('card_cover');
|
|
assert.equal(resolved.ok, true);
|
|
assert.equal(resolved.strategy, 'derive');
|
|
assert.equal(resolved.sourcePurpose, 'hero');
|
|
assert.equal(resolved.presetId, 'memind_card_cover');
|
|
});
|