二七王:补 9 处 design 规则的正/反/边界用例(均通过变异检验)
对 design §1~§12 重做「每条规则是否三类用例齐全」的审计。此前的覆盖矩阵按 「被测函数」组织,容易漏掉跨函数才体现的规则,本轮补齐 9 处: - G1/G2 §4.2 叫分:下限5与非首家不叫接受,负数/缺call/步进外/同分拒; 「不叫即退出本局叫分」——反悔再叫被 SEAT 拒、callproc 不变、仍轮到下一家 - G3 §4.5 埋牌:非庄家 SEAT、step2/step5 STEP、7张/9张/空数组 PARAM - G4 §5.1 每轮由上一轮牌面最大的一方先出(闲1赢/闲2赢两种) - G5 §6.2 闲家赢计入台面全部分牌(含庄家自己打出的)、庄家赢作废、 两个闲家谁赢结果一致 - G6 §8 算奖快照:庄家埋后28(已埋不在内)、闲家28、投降36、打出后不缩水 - G7 §3 对子只认同花色同点数两副:跨花色副7/副2、正副7、大小王均不成对 - G8 §6.3 赢末轮的牌不全是主牌就不扣底(副牌拖拉机/混合出牌/顺序颠倒) - G9 §11 无超时托管守卫:对局阶段 min_ontimeout 调用次数必须为 0, 将来有人加超时代打就会转红 每条都在副本上注入违反该规则的改动确认转红(7 组变异全部被抓住)。 顺带修一个测试自身的问题:mkBury 桩缺 do_burycard,导致「埋牌张数校验被 改松」这类变异表现为整个文件崩溃而非某条断言转红,后续断言全不执行。桩已 补完整到能走完成功路径。 全套单测 462 → 511 项全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -284,4 +284,70 @@ t.eq('提示 非法tip=9 拒(回失败包PARAM)', rej(tp, ERR.PARAM), true);
|
||||
tp = setup("00000", mkTip(5, 0)); mod.tishi(pack(1, {}));
|
||||
t.eq('提示 缺tip 拒(回失败包PARAM)', rej(tp, ERR.PARAM), true);
|
||||
|
||||
// ===================== §4.2 叫分:边界与非法入参补全 =====================
|
||||
// design §4.2:范围 5~70、步进 5;暂定庄家必须叫(不能"不叫"),其后才可以"叫更低"或"不叫"
|
||||
t.eq('叫分 边界 下限5 接受', jf(0, 5), 5);
|
||||
t.eq('叫分 正 非首家可以不叫(call=0) 接受', jf(65, 0), 0);
|
||||
t.eq('叫分 反 负数 拒', jf(0, -5), null);
|
||||
t.eq('叫分 反 缺 call 字段(NaN) 拒', jf(0, undefined), null);
|
||||
t.eq('叫分 反 步进外的小值1 拒', jf(0, 1), null);
|
||||
t.eq('叫分 反 与当前叫分相同(不更低) 拒', jf(55, 55), null);
|
||||
|
||||
// ===================== §4.2 已"不叫"的玩家不得再叫 =====================
|
||||
// design §4.2:不叫即退出本局叫分,轮次直接跳过他;他再发 jiaofen 时已不是当前叫分位,应被拒。
|
||||
const bd = P.new({ paiju_list: [] }, 0); // 真实牌局,暂定庄家 = 0 号座位
|
||||
const bdc = setup("00000", bd);
|
||||
mod.jiaofen(pack(0, { call: 65 }));
|
||||
t.eq('叫分 正 首家叫65后轮到 seat1', bd.method.get_callgrade_seat(), 1);
|
||||
mod.jiaofen(pack(1, { call: 0 })); // seat1 不叫
|
||||
t.eq('叫分 正 seat1不叫后跳到 seat2', bd.method.get_callgrade_seat(), 2);
|
||||
const bdLen = bd.callproc.length;
|
||||
bdc.sent.length = 0;
|
||||
mod.jiaofen(pack(1, { call: 60 })); // seat1 反悔想再叫
|
||||
t.eq('叫分 反 已不叫者再叫 → 拒(SEAT)', rej(bdc, ERR.SEAT), true);
|
||||
t.eq('叫分 反 已不叫者再叫 不改变叫分过程', bd.callproc.length, bdLen);
|
||||
t.eq('叫分 反 已不叫者再叫 仍轮到 seat2', bd.method.get_callgrade_seat(), 2);
|
||||
t.eq('叫分 反 已不叫者再叫 未产生庄家', bd.banker, -1);
|
||||
|
||||
// ===================== §4.5 埋牌 maipai:庄家 / 阶段 / 张数 =====================
|
||||
// design §4.5:仅"选主/打牌"路径、由庄家从 36 张里选出 **8 张** 重新扣下
|
||||
// 桩要"完整到能走完成功路径":否则一旦校验被改松,这里会**抛异常打断整个文件**,
|
||||
// 后面的断言全不执行——看到的是崩溃而不是某条断言转红,定位与信号都变差。
|
||||
const mkBury = (step, banker) => ({
|
||||
step, banker, cards: make108(), playproc: { currseat: banker },
|
||||
method: {
|
||||
check_cards_valid: c => P.check_cards_valid({ cards: make108() }, c),
|
||||
check_cards_inhand: () => true,
|
||||
do_burycard: () => { },
|
||||
get_seat_cards: () => [],
|
||||
get_liangpai: () => null
|
||||
}
|
||||
});
|
||||
let bc = setup("00000", mkBury(3, 1)); mod.maipai(pack(0, { cards: [0, 1, 2, 3, 4, 5, 6, 7] }));
|
||||
t.eq('埋牌 反 非庄家 → 拒(SEAT)', rej(bc, ERR.SEAT), true);
|
||||
bc = setup("00000", mkBury(2, 0)); mod.maipai(pack(0, { cards: [0, 1, 2, 3, 4, 5, 6, 7] }));
|
||||
t.eq('埋牌 反 选主阶段(step2)就埋 → 拒(STEP)', rej(bc, ERR.STEP), true);
|
||||
bc = setup("00000", mkBury(5, 0)); mod.maipai(pack(0, { cards: [0, 1, 2, 3, 4, 5, 6, 7] }));
|
||||
t.eq('埋牌 反 出牌阶段(step5)再埋 → 拒(STEP)', rej(bc, ERR.STEP), true);
|
||||
bc = setup("00000", mkBury(3, 0)); mod.maipai(pack(0, { cards: [0, 1, 2, 3, 4, 5, 6] }));
|
||||
t.eq('埋牌 边界 只埋7张 → 拒(PARAM)', rej(bc, ERR.PARAM), true);
|
||||
bc = setup("00000", mkBury(3, 0)); mod.maipai(pack(0, { cards: [0, 1, 2, 3, 4, 5, 6, 7, 8] }));
|
||||
t.eq('埋牌 边界 埋9张 → 拒(PARAM)', rej(bc, ERR.PARAM), true);
|
||||
bc = setup("00000", mkBury(3, 0)); mod.maipai(pack(0, { cards: [] }));
|
||||
t.eq('埋牌 反 空数组 → 拒(PARAM)', rej(bc, ERR.PARAM), true);
|
||||
|
||||
// ===================== §11 无超时托管:对局阶段不得注册任何定时器 =====================
|
||||
// design §11 已确认「倒计时只作展示,服务端不做任何超时动作」。这不是缺口,而是规则——
|
||||
// 所以这里放的是**守卫用例**:若将来有人给服务端加「超时自动出牌 / AI 代打」,必然要注册
|
||||
// 定时器来推进对局,这条就会转红,从而把违反 §11 的改动拦在提交前。
|
||||
// (min_ontimeout 在**结算**阶段另有正当用途:延迟扣房卡与存战绩,属平台事务、不推进对局,
|
||||
// 故本守卫只覆盖到出牌阶段为止。)
|
||||
const realTO = global.min_ontimeout;
|
||||
let timerCount = 0;
|
||||
global.min_ontimeout = function () { timerCount++; };
|
||||
const gd = driveToPlay("00000");
|
||||
mod.chupai(pack(0, { cards: [P.get_seat_cards(gd.pj, 0)[0]] }));
|
||||
global.min_ontimeout = realTO;
|
||||
t.eq('§11 叫分→选主→埋牌→出牌 全程未注册任何定时器', timerCount, 0);
|
||||
|
||||
process.exit(t.done('rpc') ? 0 : 1);
|
||||
|
||||
Reference in New Issue
Block a user