cangku/backend/test/selectionFissionAiSettings.test.js

202 lines
9.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const {
DEFAULT_MAIN_IMAGE_PROMPT,
DEFAULT_MODEL,
DEFAULT_PRODUCT_TITLE_PROMPT,
DEFAULT_SKU_IMAGE_PROMPT,
buildSkuNameRepairInstruction,
normalizeMainImagePrompt,
normalizeSkuImagePrompt,
normalizeProductTitleCategoryTerm,
normalizeProductTitleCandidatesPayload,
normalizeSkuNameCandidatesPayload,
} = require('../src/services/selectionFissionAiSettings');
test('selection AI reduces taxonomy paths to a usable product title category', () => {
assert.equal(normalizeProductTitleCategoryTerm('书柜 > 单个书柜'), '书柜');
assert.equal(normalizeProductTitleCategoryTerm('住宅家具 / 柜类 / 书柜'), '书柜');
assert.equal(normalizeProductTitleCategoryTerm('单个鞋柜'), '鞋柜');
assert.equal(normalizeProductTitleCategoryTerm('书柜'), '书柜');
});
test('selection AI uses the verified Qwen multimodal snapshot by default', () => {
assert.equal(DEFAULT_MODEL, 'qwen3.7-max-2026-06-08');
assert.match(DEFAULT_PRODUCT_TITLE_PROMPT, /28至30个汉字/);
assert.match(DEFAULT_PRODUCT_TITLE_PROMPT, /优先写满30字/);
assert.match(DEFAULT_PRODUCT_TITLE_PROMPT, /第三方品牌/);
assert.match(DEFAULT_PRODUCT_TITLE_PROMPT, /forbiddenBrandTerms/);
});
test('main image prompt requires styled props without changing the product', () => {
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /装饰品/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /不能输出光秃秃的裸产品/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /不能.*改变|不得改变/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /不得.*赠送|不能.*赠送/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /不得出现任何字符、符号或伪文字/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /刚性整体/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /部件位置与精确数量/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /优先保持目标商品外观结构不变/);
});
test('SKU image prompt treats the first image as the unique SKU identity', () => {
assert.match(DEFAULT_SKU_IMAGE_PROMPT, /第一张图是当前SKU唯一商品身份图/);
assert.match(DEFAULT_SKU_IMAGE_PROMPT, /只有在参考市场图生成模式下/);
assert.match(DEFAULT_SKU_IMAGE_PROMPT, /其他SKU/);
assert.match(DEFAULT_SKU_IMAGE_PROMPT, /刚性整体/);
assert.match(DEFAULT_SKU_IMAGE_PROMPT, /部件位置与精确数量/);
assert.match(DEFAULT_SKU_IMAGE_PROMPT, /优先保持SKU外观结构不变/);
});
test('legacy SKU scene prompt is migrated to SKU-first reference order', () => {
const legacyPrompt = [
'第一张图是已完成产品替换的市场主图场景,第二张图是公司SKU产品图。',
'把第二张图的SKU商品自然放入第一张图相同的场景。',
].join('\n');
assert.equal(normalizeSkuImagePrompt(legacyPrompt), legacyPrompt);
assert.equal(
normalizeSkuImagePrompt('参考已替换完成的市场主图场景,旧默认'),
DEFAULT_SKU_IMAGE_PROMPT
);
});
test('main image prompt treats the target image as the only product identity', () => {
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /第一张图是目标商品图/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /唯一商品身份来源/);
assert.match(DEFAULT_MAIN_IMAGE_PROMPT, /禁止拆分重绘或复制市场商品结构/);
});
test('legacy main image prompt is migrated to the company-first reference order', () => {
const legacyPrompt = [
'第一张图是市场参考图,第二张图是公司商品白底图。',
'彻底移除市场参考图中的原商品、文字、图标、标签、角标、价格、水印和品牌标识,只保留干净的场景、构图、视角、光线与真实阴影。',
'将第二张图中的公司商品完整替换到市场参考图原商品位置,保持商品结构、颜色和比例准确,边缘自然,不新增任何文字或图标。',
'参考市场图的陈设风格,在商品上或周围布置与品类和场景协调的书籍、摆件、收纳盒、绿植等生活化装饰品,画面饱满但不过度堆砌,不能输出光秃秃的裸产品。',
'所有装饰品必须采用无文字、无Logo设计,书籍封面和书脊只保留纯色或自然纹理,不得出现任何字符、符号或伪文字。',
'装饰品仅作为场景陈设,不得改变、遮挡或虚构商品的层板、柜门、五金、尺寸和其他主体结构,也不得表现为随商品赠送的配件。',
'输出一张可直接用于电商主图的正方形高清图片。',
].join('\n');
assert.equal(normalizeMainImagePrompt(legacyPrompt), DEFAULT_MAIN_IMAGE_PROMPT);
assert.equal(normalizeMainImagePrompt('自定义主图提示词'), '自定义主图提示词');
});
test('selection AI normalizes exactly five unique names for every SKU', () => {
const rows = normalizeSkuNameCandidatesPayload({
items: [{
skuCode: 'SKU-1',
names: ['1. 名称甲', '名称乙', '名称丙', '名称丁', '名称戊'],
}],
}, [{ skuCode: 'SKU-1', originalSkuName: '1688原始SKU名' }]);
assert.deepEqual(rows, [{
skuCode: 'SKU-1',
originalSkuName: '1688原始SKU名',
names: ['名称甲', '名称乙', '名称丙', '名称丁', '名称戊'],
}]);
});
test('selection AI rejects incomplete or duplicate candidate sets', () => {
assert.throws(
() => normalizeSkuNameCandidatesPayload({
items: [{
skuCode: 'SKU-1',
names: ['名称甲', '名称甲', '名称乙', '名称丙', '名称丁'],
}],
}, [{ skuCode: 'SKU-1' }]),
/恰好5个与原名不同的有效名称/
);
assert.throws(
() => normalizeSkuNameCandidatesPayload({
items: [{
skuCode: 'SKU-1',
names: ['原始名', '名称甲', '名称乙', '名称丙', '名称丁'],
}],
}, [{ skuCode: 'SKU-1', originalSkuName: '原始名' }]),
/恰好5个与原名不同的有效名称/
);
});
test('selection AI retry instruction requires every SKU and exactly five names', () => {
const instruction = buildSkuNameRepairInstruction([
{ skuCode: 'SKU-1' },
{ skuCode: 'SKU-2' },
], 1);
assert.match(instruction, /第2次生成/);
assert.match(instruction, /SKU-1、SKU-2/);
assert.match(instruction, /恰好5个名称/);
assert.match(instruction, /不能遗漏任何SKU/);
});
test('selection AI requires exactly five unique product title candidates', () => {
const titles = [
'现代简约家用多层落地收纳书柜客厅卧室大容量置物储物柜组合',
'家用客厅卧室多层落地收纳书柜简约大容量置物储物展示柜组合',
'简约现代客厅卧室落地收纳书柜家用多层大容量置物储物柜组合',
'客厅卧室家用多层书柜落地收纳简约现代大容量置物储物柜组合',
'多层落地收纳书柜家用客厅卧室简约现代大容量置物储物柜组合',
];
assert.deepEqual(
normalizeProductTitleCandidatesPayload({
titles: titles.map((title, index) => `${index + 1}. ${title}`),
}),
titles
);
assert.throws(
() => normalizeProductTitleCandidatesPayload({
titles: [titles[0], titles[0], ...titles.slice(1, 4)],
}),
/恰好5个有效商品标题/
);
});
test('selection AI enforces title length, category and all surging keywords', () => {
const strategy = { category: '书柜', surgingKeywords: ['落地', '收纳'] };
const validTitles = [
'现代简约家用多层落地收纳书柜客厅卧室大容量置物储物柜组合',
'家用客厅卧室多层落地收纳书柜简约大容量置物储物展示柜组合',
'简约现代客厅卧室落地收纳书柜家用多层大容量置物储物柜组合',
'客厅卧室家用多层书柜落地收纳简约现代大容量置物储物柜组合',
'多层落地收纳书柜家用客厅卧室简约现代大容量置物储物柜组合',
];
assert.deepEqual(normalizeProductTitleCandidatesPayload({ titles: validTitles }, strategy), validTitles);
assert.throws(
() => normalizeProductTitleCandidatesPayload({
titles: [...validTitles.slice(0, 4), '家用落地收纳书柜'],
}, strategy),
/28至30字/
);
assert.throws(
() => normalizeProductTitleCandidatesPayload({
titles: [...validTitles.slice(0, 4), '现代简约家用多层落地收纳置物架客厅卧室大容量储物展示柜组合'],
}, strategy),
/品类词/
);
assert.throws(
() => normalizeProductTitleCandidatesPayload({
titles: [...validTitles.slice(0, 4), '现代简约家用多层落地书柜客厅卧室大容量置物储物展示柜组合款'],
}, strategy),
/飙升词/
);
});
test('selection AI rejects titles that copy brands or models from reference links', () => {
const titles = [
'现代简约家用多层落地收纳书柜客厅卧室大容量置物储物柜组合',
'家用客厅卧室多层落地收纳书柜简约大容量置物储物展示柜组合',
'简约现代客厅卧室落地收纳书柜家用多层大容量置物储物柜组合',
'客厅卧室家用多层书柜落地收纳简约现代大容量置物储物柜组合',
'钛载S3多层落地收纳书柜家用客厅卧室大容量置物储物柜组合款',
];
assert.throws(
() => normalizeProductTitleCandidatesPayload({ titles }, {
category: '书柜',
surgingKeywords: ['落地', '收纳'],
forbiddenBrandTerms: ['钛载S3'],
}),
/参考商品品牌或型号“钛载S3”/
);
});