用服务端 test/_rpc.js + class.desk.js 脚手架跑一局(发牌→叫分→上庄→选主→ 埋牌→28轮出牌→结算),把真实下发包按座位导出成 JSON 供前端回放。不手写假 包——测的是真实契约,协议文档写错或前端理解偏差都会在这里暴露。 _rpc.js 的 setup() 只接受已构造好的 o_paiju,不走 class.desk.js,产不出 fapai;改用 test_flow.js/test_endgame.js 的 mkRoom() 模式(D.new + 全局 youle_erqiwang.app/import 与 mod.app/mod.import 同指一个 sent 数组),两条 既有用法缺一不可。 夹具自检守住质量:覆盖 fapai/jiaofen/shangzhuang/xuanzhu/maipai/ chupai1/2/3/jiesuan 全部阶段、保留座位差异(底牌只发给部分座位)、每个下发 包都带 success。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
34 lines
1.4 KiB
JavaScript
34 lines
1.4 KiB
JavaScript
// 夹具自检:真包必须覆盖全部阶段,且保留座位差异
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { ROOT } = require('./_load');
|
||
const t = require('./_assert')();
|
||
|
||
const fx = JSON.parse(fs.readFileSync(path.join(ROOT, 'client/tests/fixtures/packets.json'), 'utf8'));
|
||
|
||
t.eq('三个座位', fx.seats.length, 3);
|
||
|
||
const rpcsOf = seat => fx.seats[seat].map(p => p.rpc);
|
||
const allRpcs = [].concat(rpcsOf(0), rpcsOf(1), rpcsOf(2));
|
||
|
||
['fapai','jiaofen','shangzhuang','xuanzhu','maipai','chupai1','chupai2','chupai3','jiesuan']
|
||
.forEach(rpc => t.eq('夹具覆盖 ' + rpc, allRpcs.indexOf(rpc) >= 0, true));
|
||
|
||
// 差异化下发确实存在:bottomcards 不该三家都有
|
||
const withBottom = [0,1,2].filter(s =>
|
||
fx.seats[s].some(p => p.rpc === 'shangzhuang' && p.data.hasOwnProperty('bottomcards')));
|
||
t.eq('底牌只发给部分座位', withBottom.length < 3, true);
|
||
|
||
// deskinfo 快照覆盖多个阶段
|
||
const steps = fx.deskinfo.map(d => d.step).filter((v, i, a) => a.indexOf(v) === i).sort();
|
||
t.eq('deskinfo 覆盖 ≥3 个阶段', steps.length >= 3, true);
|
||
|
||
// 每个包都有 success(协议 §0.1:每个下发包的 data 必带 success)
|
||
const noSuccess = [];
|
||
[0,1,2].forEach(s => fx.seats[s].forEach(p => {
|
||
if (!p.data || !p.data.hasOwnProperty('success')) { noSuccess.push(s + ':' + p.rpc); }
|
||
}));
|
||
t.eq('每个下发包都带 success', noSuccess, []);
|
||
|
||
process.exit(t.done('fixture') ? 0 : 1);
|