数字我 Digital Me:全量入库(大文件走 Git LFS)
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s

This commit is contained in:
2026-10-10 16:24:43 +08:00
commit 0593ff7e62
1893 changed files with 361723 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { ZHANZHEN, matchWei, roleOf, zhanzhen, listZhanzhen, zhanzhenByDyn } from '../src/zhanzhen.js';
// 引擎纯逻辑用 mock 战役测(真战役数据由双证考据子代理陆续装入 ZHANZHEN)。
const MOCK = {
id: 'mock', dyn: 'tang', chao: 'tang', name: '模拟之战', year: 621,
side: 'A vs B', stake: '赌注', intro: '引子', situ: '处境',
weiTiers: { '主帅': 5, '列将': 4, '裨将': 3, '甲士': 1, '民夫': 0 },
narr: { '主帅': '你居中军', '甲士': '你在阵列里' },
nodes: [{ label: '关键节点', cite: '《旧唐书》:「……」' }],
choices: {
fengkou: { prompt: '身处风口——', options: [{ id: 'go', text: '战', outcome: '胜', end: { tone: 'sheng', word: '受赏' } }] },
juzhong: { prompt: '身在局中——', options: [{ id: 'go', text: '冲', outcome: '死', end: { tone: 'wang', word: '身死' } }] },
boji: { prompt: '被波及——', options: [{ id: 'go', text: '躲', outcome: '幸存', end: { tone: 'xian', word: '未定' } }] },
},
real: '战果', cite: '《旧唐书》:「……」', note: null,
};
test('matchWei:取不高于你 tier 的最高一档;全低于则 null', () => {
assert.equal(matchWei(MOCK.weiTiers, 5), '主帅');
assert.equal(matchWei(MOCK.weiTiers, 3), '裨将');
assert.equal(matchWei(MOCK.weiTiers, 2), '甲士');
assert.equal(matchWei(MOCK.weiTiers, null), null);
});
test('roleOf:顶档=风口,次档=局中,再低=波及,无=旁观', () => {
assert.equal(roleOf('主帅', MOCK.weiTiers).key, 'fengkou');
assert.equal(roleOf('列将', MOCK.weiTiers).key, 'juzhong');
assert.equal(roleOf('甲士', MOCK.weiTiers).key, 'boji');
assert.equal(roleOf(null, MOCK.weiTiers).key, 'pangguan');
});
test('zhanzhen:按 tier 组装处境+对应抉择+双证战果', () => {
ZHANZHEN.mock = MOCK;
const top = zhanzhen('mock', 5);
assert.equal(top.role.key, 'fengkou');
assert.equal(top.you.wei, '主帅');
assert.equal(top.choice.options[0].end.word, '受赏');
assert.ok(/《.+》/.test(top.cite));
const low = zhanzhen('mock', 1);
assert.equal(low.role.key, 'boji');
const out = zhanzhen('mock', null);
assert.equal(out.role.key, 'pangguan');
assert.equal(out.choice, null, '旁观无抉择');
assert.equal(zhanzhenByDyn('tang').includes('mock'), true);
assert.ok(listZhanzhen().some((b) => b.id === 'mock'));
delete ZHANZHEN.mock;
});
test('未知战役→null', () => {
assert.equal(zhanzhen('nope', 5), null);
});
// ── 真实双证战役数据校验(11场·zhanzhenData.js)──
test('11场战役全部装入,字段完整、cite可查', () => {
const list = listZhanzhen();
assert.ok(list.length >= 11, '至少11场战役');
const ids = new Set(list.map((b) => b.id));
for (const id of ['julu', 'gaixia', 'baideng', 'kunyang', 'guandu', 'chibi', 'feishui', 'hulao', 'chanyuan', 'poyang', 'tumu']) {
assert.ok(ids.has(id), `应含 ${id}`);
}
for (const id of ids) {
const b = zhanzhen(id, 5);
assert.ok(b.name && b.side && b.stake && b.intro && b.situ, `${id} 元信息完整`);
assert.ok(/《.+》/.test(b.cite), `${id} 战果 cite 可查`);
assert.ok(b.real, `${id} 有真实战果`);
assert.ok(Object.keys(b.zaichang).length >= 5, `${id} 军职档≥5`);
// 节点≥3;cite 须么带《篇名》双证、么为 null(诚实存疑节点·如"水灌鄢城非见史记")
assert.ok(b.nodes.length >= 3 && b.nodes.every((n) => n.cite == null || /《.+》/.test(n.cite) || n.cite.length > 6), `${id} 节点≥3且双证/存疑标注`);
// 抉择:至少一档;存在的每档每个选项都要有 outcome + 合法 end.tone(部分战役只给单一军职档的抉择,自选身份走该档)
const choiceRoles = Object.keys(ZHANZHEN[id].choices || {});
assert.ok(choiceRoles.length >= 1, `${id} 至少一档抉择`);
for (const roleKey of choiceRoles) {
const ch = ZHANZHEN[id].choices[roleKey];
assert.ok(ch && ch.options.length >= 2, `${id}.${roleKey} 应有≥2选项`);
assert.ok(ch.options.every((o) => o.outcome && ['sheng', 'wang', 'xian'].includes(o.end.tone)), `${id}.${roleKey} 选项须有outcome+合法end.tone`);
}
}
});
test('引擎朝映射:能算tier的战役 dyn 落在8引擎朝内', () => {
const ENGINE = new Set(['tang', 'xihan', 'han', 'weijin', 'song', 'ming', 'liao', 'nanchao']);
for (const b of Object.values(ZHANZHEN)) {
if (b.dyn != null) assert.ok(ENGINE.has(b.dyn), `${b.id} 的 dyn=${b.dyn} 应是引擎朝`);
}
// 引擎朝各有对应战役(扩容后数量增多,校验包含关系)
for (const id of ['poyang', 'tumu']) assert.ok(zhanzhenByDyn('ming').includes(id), `ming 应含 ${id}`);
for (const id of ['chibi', 'guandu']) assert.ok(zhanzhenByDyn('han').includes(id), `han 应含 ${id}`);
for (const id of ['gaixia', 'jingxing', 'mobei']) assert.ok(zhanzhenByDyn('xihan').includes(id), `xihan 应含 ${id}`);
for (const id of ['hulao', 'yinshan', 'suiyang']) assert.ok(zhanzhenByDyn('tang').includes(id), `tang 应含 ${id}`);
});