Files
digital-me-history/digital-me-backend/test/erchuang.test.js
T
admin 0593ff7e62
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s
数字我 Digital Me:全量入库(大文件走 Git LFS)
2026-10-10 16:24:43 +08:00

57 lines
3.7 KiB
JavaScript
Raw 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.
import test from 'node:test';
import assert from 'node:assert/strict';
import { STYLES, listErchuangAnchors, buildErchuangPrompt, buildSegmentPrompt, parseSegments } from '../src/erchuang.js';
import { buildPack } from '../src/sucaibao.js';
test('锚点名录:战役 + 她传人物 + 体裁', () => {
const a = listErchuangAnchors();
assert.ok(a.zhanzhen.length >= 11, '应含全部战役');
assert.ok(a.nvxing.length >= 18, '应含全部她传人物');
assert.ok(a.zhanzhen.every((x) => x.type === 'zhanzhen' && x.title && x.sub));
assert.ok(a.nvxing.every((x) => x.type === 'nvxing' && x.title && x.sub));
for (const k of ['ci', 'fu', 'ji', 'juben', 'shi', 'weishichen', 'duitan', 'gongzhi', 'huimu', 'gongxu']) {
assert.ok(a.styles.some((s) => s.key === k), `体裁应含 ${k}`);
}
// 每体裁有 layer 标
for (const v of Object.values(STYLES)) assert.ok(['work', 'segment'].includes(v.layer), `${v.label} 须有 layer`);
});
test('buildSegmentPrompt:骨架注入 + 出处自标铁律 + 黑名单/断代守卫', () => {
const pack = buildPack('zhanzhen', 'chibi');
const { messages } = buildSegmentPrompt(pack, 'weishichen', {});
const sys = messages[0].content, user = messages[1].content;
assert.ok(/\[\[f:key\|/.test(sys) && /\[\[y:子类\|/.test(sys), 'system 须给出 f/y 自标语法');
assert.ok(/对话永远是 y/.test(sys), '对话恒演绎铁律');
assert.ok(/演义黑名单/.test(sys) && /借东风/.test(sys), '须注入黑名单');
assert.ok(/断代=donghan/.test(sys), '须注入断代守卫');
assert.ok(user.includes('史实骨架') && /\[c\d\]/.test(user), 'user 须带编号骨架');
});
test('parseSegments:f挂cite/表外key降y/对话保子类/黑名单剔除/标点并段', () => {
const table = { c1: '《三国志·周瑜传》:操军方连船舰' };
const r = parseSegments('[[f:c1|曹军船舰首尾相接]]。[[y:对话|黄盖低声:今夜可成]]。[[f:c9|某表外句]]。[[y:叙事|借东风起了]]。', table, { blacklist: ['借东风'], era: 'donghan' });
const f = r.segs.find((s) => s.text.includes('船舰'));
assert.equal(f.t, 'f'); assert.ok(f.cite.includes('周瑜传'));
assert.ok(f.text.endsWith('。'), '标点应并进史实段');
const dia = r.segs.find((s) => s.text.includes('黄盖'));
assert.equal(dia.t, 'y'); assert.equal(dia.kind, '对话'); assert.ok(!dia.cite, '对话永不挂cite');
const wild = r.segs.find((s) => s.text.includes('表外'));
assert.equal(wild.t, 'y', '表外key必须降为演绎(护城河)');
assert.ok(!r.segs.some((s) => s.text.includes('借东风')), '演义黑名单段被剔除');
});
test('buildErchuangPrompt:把双证史实卡拼进 prompt,含红线约束', () => {
const fact = { title: '赤壁之战', sub: '孙刘联军 vs 曹军 · 208年', facts: ['火攻破曹'], cites: ['《三國志·吳書·周瑜傳》:「……」'] };
const { messages, styleLabel } = buildErchuangPrompt(fact, 'ci', { seed: '写周瑜的意气', role: '周瑜部将' });
assert.equal(styleLabel, '词');
assert.equal(messages[0].role, 'system');
assert.ok(/文学演绎/.test(messages[0].content) && /相悖的硬事实/.test(messages[0].content), 'system 须含创作≠史料红线');
assert.ok(messages[1].content.includes('赤壁之战') && messages[1].content.includes('《三國志'), 'user 须带真实事件+正史依据');
assert.ok(messages[1].content.includes('周瑜部将') && messages[1].content.includes('写周瑜的意气'), 'user 须带身份+方向');
});
test('未知体裁回落古体诗', () => {
const { styleLabel } = buildErchuangPrompt({ title: 't', sub: 's', facts: [], cites: [] }, 'nope', {});
assert.equal(styleLabel, STYLES.shi.label);
});