# 二七王服务端 · 合规测试计划 > 目标:用 Node 单测/集成测**系统覆盖 `design.md` 的全部服务端可验证规则**,做到"规则 → 用例"可追溯,作为"代码是否符合手册"的**自动化证据**(与 `01-design合规逐节核对.md` 的人工审计互补)。 > > 运行:`node server/games/erqiwang/test/run.js`。测试原理与双运行时见 `test/README.md`。测试纪律(正/反/边界、不软化断言、失败先裁根因)见 `docs/server/development-guide/04` §10。 --- ## 0. 分层与测试类型 | 层 | 含义 | 依赖 | 现状 | | --- | --- | --- | --- | | **L1 单元** | 纯函数直接调用(arith/config/paiju 静态计算) | 仅 `_shim` 的平台工具 | 已有 `test_arith`/`test_config`/`test_paiju`(部分) | | **L2 局内集成** | `cls_youle_erqiwang_paiju.new(o_desk,firstseat)` 造牌局,驱动 `do_callgrade→do_choiceflower→do_burycard→do_playcard…→get_paiju_account` 验证状态/结算 | mock `o_desk`/`o_room` + shim;**不经 mod.js** | 仅 `get_paiju_account` 造了极简对象,**无完整一局驱动** | | **L3 RPC/广播** | `mod.js` 收包入口 → 校验 → 广播 | mock `check_player`/`SendPack`/`sendpack_toother`,捕获下发包 | **完全未覆盖** | **发牌随机性处理**:`do_dealpai` 用 `min_random`。L2 需要**可控发牌**——测试 `_shim` 提供可注入的 `min_random`(预置序列或指定发牌结果),使一局可复现。这是 L2 的前置基建。 --- ## 1. 覆盖矩阵(按 design 章节) 状态:✅ 已覆盖 · 🟡 部分 · ❌ 待补。用例列标注 正/反/边界 三类(`docs/server/development-guide/04` §10 要求三类齐全)。 ### §2 牌局构成 | 规则 | 被测 | 用例(正/反/边界) | 层 | 状态 | | --- | --- | --- | --- | --- | | 92 张、去两副 3/4 | `init_cards`/`do_dealpai` | 正:发完 3 家各 28 + 底 8 = 92;反:牌堆不含 number 3/4;边界:两副各花色计数 | L2 | ❌ | | 分值 5→5、10→10、K→10、余 0 | `init_cards` | 正:各面值 score;边界:非分牌 score=0 | L1/L2 | 🟡(扣底/结算间接) | ### §3 主牌顺序 / 编码 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | 主牌 code 降序=设计顺序(大王>小王>正7>副7>正2>副2>主A…主5>副牌) | `id_to_code`/`order_cards` | 正:排序结果逐位;边界:正/副 7、正/副 2 的相对大小;主A vs 副2 | L1 | ❌(间接用到,无专测) | | 相邻链 + 6/8 可连、7 不与 6/8 连 | `is_continuous` | 正:全相邻段逐对;反:7-6/8-7/9-7 不连;边界:8-6 连、大王-小王连 | L1 | 🟡(甩牌/算奖间接) | | 副7/副2 跨花色比大小归一 | `trump_rank`/`can_followcard` | 正:副7 vs 副7 不可压;反:正7 压副7 | L1 | 🟡(甩牌间接) | ### §4 开局与坐庄 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | 叫分 5~70、步进5、暂定庄必叫、后叫更低/不叫 | `mod.jiaofen` 校验 + `do_callgrade` | 正:合法叫分推进;反:>70/非5倍/首家0/叫≥当前;边界:叫70、叫5 | L3(校验)+L1(do_callgrade) | ❌ | | 叫5立即上庄、两家不叫上庄 | `do_callgrade` | 正:叫5→banker;正:一家叫另两家不叫→banker;边界:三家叫分序列 | L1 | ❌ | | 坐庄轮换:庄赢连庄、庄输/投降下家、首局庄=0号 | `desk.do_prepare`/`makewar` | 正:result=0 连庄;正:result=1/2 顺延;边界:首局 firstseat=0 | L2/L3 | ❌ | | 阶段机 1→2→3→5→6,无 step4 | `do_up_banker`/`do_choiceflower`/`do_burycard` | 正:各 handler 后 step 值;反:错误 step 调用被拒 | L2/L3 | ❌ | | 投降仅70分/step2/庄家/不埋牌 | `mod.touxiang` | 正:70分 step2 投降→结算;反:≠70/step≠2/非庄;边界:投降后 step=6 | L3 | ❌ | | 70分暗牌向所有玩家亮3秒(+ancard3s)、非70只发庄 | `mod.jiaofen`(shangzhuang) | 正:70→闲家有 bottomcards+ancard3s;反:非70 闲家无 bottomcards | L3 | ❌ | | 选主后先选主后埋牌 | `do_choiceflower`/`do_burycard` | 正:flower 记录、step 推进;边界:埋牌须8张且在手 | L2 | 🟡(check_cards_inhand 未测) | ### §5 出牌规则 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | **§5.1/5.2 跟牌/毙牌/垫牌/混合出牌/拖拉机覆盖度** | `get_followcard`/`can_followcard` | 正:同花色跟、有对必出对、有拖必出拖、两对不连必出、尽量长拖优先;反:缺牌型被拒、放长拖只出散对被拒;毙牌(须完全缺门):副单→主单、副对→主对、副N连→主N连;垫牌/混合出牌:cardvalue=0 不争权 | L1 | ✅(`test_follow` 50 例,含 else 欠约束修复回归) | | §5.3 拖拉机相邻 | `is_continuous`/`get_tuolaji_list` | 见 §3 | L1 | 🟡 | | §5.4 甩牌:副禁甩/最大性/甩错 | `can_playcard`/`opp_can_beat_flush`/`decompose_trump` | 已覆盖(合法/甩错含smallest/副禁甩/单张/对子/拖拉机) | L1 | ✅ | | §5.4.4 跟甩牌强制分量拆解 | `flush_follow_ok` | 已覆盖(有对必打对/有拖必打拖/退化/主牌最大化/无主全垫) | L1 | ✅ | ### §6 捡分与扣底 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | §6.1 分值 | `init_cards` | 见 §2 | L1 | 🟡 | | §6.2 闲家赢归闲、庄赢作废、两闲谁赢都算闲 | `do_playcard`/`get_jian_grade` | 正:闲赢累计分;反:庄赢不计;边界:两闲各赢 | L2 | 🟡(1 集成例) | | §6.3 扣底倍数 单1/对2/N连2N、全主牌守卫、取最高规格 | `get_bottom_multiple` | 已覆盖 | L1 | ✅ | | §6.3 触发:闲家用主牌赢末轮才扣底 | `get_bottom_account` | 正:闲家主牌赢末轮翻倍;反:非主牌赢不扣、庄家赢不扣 | L2 | ❌ | ### §7 结算:子数与升级 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | 常规算子 基础子数 + 大光×3/小光×2/过庄×1/升N级、Q=40 | `get_base_bycall`/`get_qvalue`/`get_upgrade` | 已覆盖各档代表值 | L1 | ✅(可补每档过庄/小光分界与升3级) | | 爬坡 基础子数梯度 + 分段Q | 同上(climb=true) | 已覆盖各档 | L1 | ✅ | | X=基础×倍率、庄赢/闲赢符号、按房间爬坡切换 | `get_paiju_account` | 正:大光/过庄/升级/爬坡局 seatlist;边界:banker=-1 | L2 | 🟡(仅65小光+投降) | ### §8 算奖 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | §8.1 常规算奖只庄家、三/四王+连对链、6-8个7/2、无≥10老主/无作废 | `get_chongguan` | 已覆盖 | L1 | ✅ | | §8.2 亮牌阈值/统计、只亮结构 | `get_liangpai` | 已覆盖 4 例 | L1 | 🟡(补 王≥3/7≥6 组合、边界9/10) | | §8.3 傍王按位开关、庄闲每王1奖 | `get_paiju_account`(bangwang) | 正:傍王局 grade_aw 含王奖;反:未勾选不计 | L2 | ❌ | | §8.4 算奖并入 X×(2Ni−Nj−Nk)、含闲-闲、投降X=1 | `get_paiju_account` | 正:庄1奖/闲傍王2奖的三家净额;边界:三家同时有奖 | L2 | ❌(公式单测有,集成未测) | | §8 快照:庄埋后28/闲发后28;投降庄36无主 | `get_seat_cards_award` | 正:庄排除已埋;正:投降含底36;边界:无主花色无连对链 | L2 | 🟡(投降 count 间接) | ### §9 查牌 / 明牌 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | 报无主统计 info/baozhu 门控 | `mod.chupai` | 正:可查牌下发 info/baozhu;反:不查牌 info 缺失、baozhu=0 | L3 | ❌ | | 亮牌/PushCards.seatlist 门控 | `mod.maipai`/`get_deskinfo` | 正:可查牌闲家有 liangpai;反:不查牌无 | L3 | ❌ | | 明牌 mingpai:可查牌+已报无主+出牌阶段 | `mod.mingpai` | 正:条件满足返回他家主牌;反:不查牌/未报无主/非出牌阶段被拒 | L3 | ❌ | ### §10 房间设置 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | 位串解析、局数6/12、扣卡房主2·4/AA1·2 | `config.parse`/`export.get_asetcount`/`get_needroomcard` | 已覆盖(config 复刻映射) | L1 | ✅(可改为直接调 export 函数) | | 傍王/爬坡/查牌三开关生效 | `get_paiju_account`/`mod.*` | 傍王/爬坡见 §7/§8;查牌见 §9 | L2/L3 | 🟡 | ### §12 完整流程 | 规则 | 被测 | 用例 | 层 | 状态 | | --- | --- | --- | --- | --- | | 一局端到端:发牌→叫分坐庄→选主→埋牌→逐轮出牌→末轮扣底→结算→轮庄 | 全链路 | 正:一局可控发牌打到结算,各家总分正确;含连庄/下家轮转 | L2 | ❌ | > §11 牌局交互提示中,选主对子数/亮底牌等随对应阶段包下发;闲家三提示(踩/没分/有分)为服务端转发项(`mod.tishi`,见 P1)。 --- ## 2. 待补用例清单(按优先级) ### P0 · 规则/金额关键 —— ✅ 已完成 1. ✅ **§5.1/5.2 正常跟牌/毙牌/垫牌/混合出牌/拖拉机覆盖度**(`can_followcard`,`test_follow.js` 50 例)——单张/对子/拖拉机的必出、毙牌数量对应(副单→主单/副对→主对/副N连→主N连、且毙牌须完全缺门)、垫牌与"混合出牌"(有该花色不够+补主牌→`cardvalue=0` 不争权)、缺门/同花色不足/对子不够各分支、多候选拖拉机(D1 崩溃回归)、**降级递归 else 欠约束回归**(三组等长 2 连对时出孤立对→拒),正反边界齐全。 2. ✅ **§7/§8 结算集成扩充**(`get_paiju_account`,`test_paiju.js`)——大光/过庄/升2级、爬坡局、傍王局(庄3王,N=4)、不傍王对照、投降,逐档验证 seatlist 各家 grade。 3. ✅ **§4.2 叫分坐庄**(`do_callgrade`,`test_callgrade.js` 6 例)——叫5立即上庄、两家不叫上庄、后叫更低→最低者上庄、不同首家。 4. ✅ **§6.3 扣底触发**(`get_bottom_account`,`test_paiju.js`)——闲家主对/两连对赢末轮才扣底(×2/×4);副牌赢、庄家赢均不扣。 > 注:§4.2 中 `mod.jiaofen` 的**入参校验**(>70/非5倍/首家必叫/后叫更低)属 L3,随投降/暗牌/查牌门控一起在 P1 补。 ### P1 · 规则关键、需 RPC 脚手架 —— ✅ 已完成(`_rpc.js` + `test_rpc.js` / `test_desk.js`) 5. ✅ **§9 查牌门控 + mingpai**——chupai 的 info/baozhu 按 `nocheck` 门控(可查有/不查无);mingpai 合法下发他家主牌,不查牌/未报无主/非出牌阶段均拒。 6. ✅ **§4 投降 + 暗牌亮牌**——touxiang 仅 70分/step2/庄家;真实 jiaofen 驱动到 70分上庄,验证 shangzhuang 闲家有 bottomcards+ancard3s、叫5上庄闲家无。 7. ✅ **§4.2 `mod.jiaofen` 入参校验**——>70/非5倍/首家必叫/后叫更低 全部拒,合法接受。 8. ✅ **§4.6 坐庄轮换 `do_prepare`**——庄赢连庄、闲赢/投降下家。 ### P2 · 补强既有 —— ✅ 已完成 9. ✅ §2 牌局构成专测(`test_deal`)、§3 编码/排序专测(`test_arith`:order_cards + is_continuous 链)。 10. ✅ §9 `mod.maipai` 亮牌门控 / `get_deskinfo` PushCards 门控(`test_rpc` 端到端驱动后验证)。 11. ✅ §8.2 亮牌更多阈值(6个7/固定主9-10边界)、§7 算子每档分界+升3级(`test_arith`)。 12. ✅ §12 端到端(`test_rpc` 真实驱动到出牌就绪)。 13. ✅ §10 直接调 `export.get_asetcount`/`get_needroomcard`/`get_needroomcard_joinroom`(`test_config`)。 14. ✅ **§11 闲家三提示 `mod.tishi`**——正:闲家 step5 发 踩/没分/有分,只转发给对家(`3-banker-seat`,验证 fromid/seat/tip);反:庄家发/非出牌阶段/非法 tip/缺 tip 均拒(`test_rpc` 11 例)。 --- ## 3. 集成脚手架需求(实现 L2/L3 的前置) - **L2 局内驱动器 `test/_harness.js`**: - 可注入发牌的 `min_random`(或直接构造 `o_paiju.cards` 指定各家手牌),使牌局可复现。 - mock `o_desk`(seatlist 累积、`get_desk_account`、`o_room.roomtype/asetcount`)与 `o_room`。 - 提供"驱动一局到某阶段"的辅助:叫分序列 → 选主 → 埋牌 → 出牌序列。 - **L3 RPC 捕获器**:mock `youle_erqiwang.import.check_player`(返回构造的 `o_room`)、`o_room.method.sendpack_toother`/`youle_erqiwang.app.SendPack`(把下发包收集进数组),断言包字段(含 §9 门控、暗牌、投降、chupai1/2/3、jiesuan)。 --- ## 4. 完成定义(DoD)—— ✅ 已达成 - ✅ 覆盖矩阵中所有 ❌/🟡 项补到 ✅(详见 §5 现状),每条规则至少含正/反/边界; - `node test/run.js` 全绿、退出码 0; - 新增用例遵守测试纪律:不改正式代码去迁就测试、不软化断言、失败先用证据裁根因; - 本计划与 `01-design合规逐节核对.md` 的结论一致(自动化测试成为人工审计的可回归证据)。 --- ## 5. 当前状态小结 —— ✅ DoD 达成,P0/P1/P2 全部完成 **共 511 项断言全绿**(`test_arith` 122 / `test_calc` 49 / `test_callgrade` 6 / `test_config` 19 / `test_deal` 12 / `test_desk` 6 / `test_endgame` 25 / `test_follow` 58 / `test_followdiff` 12 / `test_input` 34 / `test_paiju` 50 / `test_rpc` 89 / `test_success` 29)。其中 `test_calc`/`test_followdiff` 见 §7、G1~G9 见 §8。 design 全部服务端可验证章节均有正/反/边界单测: - **§2 构成**:`test_deal` 92张/去3-4/三家28+底8/分值。 - **§3 编码/排序**:`test_arith` order_cards 降序=设计顺序、is_continuous 相邻链逐段+反例。 - **§4**:叫分坐庄 do_callgrade、`mod.jiaofen` 入参校验、投降 touxiang 条件、70分上庄暗牌亮牌下发、坐庄轮换 do_prepare。 - **§5**:**正常跟牌全面覆盖(§5.1/5.2)**——按跟牌方手牌各分支穷举:主牌首出与副牌首出、单张/对子/拖拉机各牌型、有同花必出对应牌型、同花不足则出仅有的+补、缺门毙牌(数量对应)与垫牌(不压)、拖拉机的对子不够/同花无对/**多候选拖拉机(D1崩溃回归)**/3连对/富余对子必出拖、手牌数==首出张数全部必出;**甩牌全面覆盖(§5.4)**——合法(纯单/纯对/单+拖/单+对+拖三混)、甩错(单张/主对/拖拉机分量各自被压、双对之一被压)、副牌禁甩(对手缺门与非缺门)、甩错惩罚**实际执行**(do_playcard 只打最小一张、其余留手);**跟甩牌全面覆盖(§5.4.4)**——对子必出、拖拉机必出/退化对子/退化到单张、多组拖拉机、混合demand逐分量对位、主牌最大化、无主全垫,以及"首出甩牌→跟牌违规→do_playcard 拒"的集成。 - **§6**:捡分(结算集成间接)、扣底倍数+触发。 - **§7**:算子常规+爬坡逐档、每档分界+升3级、结算集成(大光/过庄/升级/爬坡/傍王/投降)。 - **§8**:算奖 get_chongguan、亮牌 get_liangpai(含 6个7/固定主9-10边界)、算奖并入 X×N(傍王对照)。 - **§9**:查牌门控(chupai info/baozhu、maipai liangpai、deskinfo PushCards seatlist)、明牌 mingpai。 - **§10**:位串解析、局数/扣卡(直接调 export 接口)。 - **§12**:端到端(叫分→上庄→选主→埋牌→出牌ready,真实 RPC 驱动)。 > 说明:§12 覆盖到"出牌准备就绪"(各阶段串接正确);完整 28 墩逐墩对打需一个合法出牌选择器,属后续增强,非规则合规缺口(逐墩规则已由 §5/§6 单测覆盖)。 --- ## 6. 第三轮补充:design 之外的可验证维度 > 前两轮的覆盖矩阵完全按 design 章节组织,因此**平台接入、客户端入参、下发面一致性、协议红线**这几类根本不在矩阵里——270 项全绿却漏掉了 1 项阻断级与 3 项严重缺陷(见 `01-design合规逐节核对.md`「第三轮核对」)。本节把这些维度补进测试计划,后续复核必须一并跑。 | 维度 | 被测 | 用例(正/反/边界) | 文件 | 状态 | | --- | --- | --- | --- | --- | | 客户端入参合法性 | `check_cards_valid` / `check_cards_inhand` | 正:单张/多张/边界 id 0 与 107;反:非数组(undefined/null/字符串/伪数组)、空数组、重复 id、越界、负数、小数、数字字符串;反:不在手上/已出/已埋 | `test_input` | ✅ 21 | | 重复 id 不得伪造牌型 | `mod.maipai` / `mod.chupai` | 反:8 个重复 id 埋牌被拒且一张未埋;反:重复 id 出牌被拒;正:8 张不同牌埋牌成功且实埋 8 张;反:越界/非数组入参不抛异常且不产生出牌 | `test_input` | ✅ 7 | | §9 报无主即时刷新 | `do_playcard` | 正:打空瞬间三家统计与主花色标志同时刷新;边界:报无主前三家均为 `[-1,-1]` | `test_input` | ✅ 6 | | 红线 `data.success`(成功包) | `class.desk`/`mod.js`/`get_paiju_account` | 正:fapai/zhunbei/jiaofen/shangzhuang/xuanzhu/maipai/chupai1/mingpai/tishi 均带 `success:true`;正:jiesuan 的正常/投降/解散三种 type 都带 | `test_success` | ✅ 12 | | 红线 `data.success`(失败回包) | 各 handler 失败分支 | 反:每个 handler 取代表性失败路径,断言"恰好回 1 包 + `success:false` + errcode 正确 + 回到请求者 fromid",覆盖 STEP/SEAT/PARAM/RULE/PLAYER 五类 | `test_success` | ✅ 17 | | §7.3 下发面 multiple 与结算同源 | `mod.jiaofen` / `get_deskinfo` / `get_paiju_account` | 正:叫 45 分在常规房/爬坡房分别得 6/7;正:上庄包与重连 ChooseMain 一致;边界:与结算包 `aset.multiple` 逐一对齐 | `test_rpc` | ✅ 7 | | §5.4 甩牌下发面 | `chupai1` | 正:合法甩牌带 `shuai` 分量;反:非甩牌不带;反:甩错只打最小一张且不带 `shuai` | `test_rpc` | ✅ 8 | | 重连 pushlist 结构 | `get_deskinfo` | 正:轮数正确、内层恒 3 个座位、无 undefined;正:按本局主牌花色排序(主5 排在副A 前) | `test_rpc` | ✅ 4 | | §9 出牌历史按查牌模式门控 | `get_deskinfo` | 反:不查牌房无 `pushlist`;正:可查牌房有 `pushlist`;边界:两种模式下当前轮桌面牌 `playproc.cards` 都必须恢复 | `test_rpc` | ✅ 4 | | §3 相邻链花色 | `is_continuous` | 正:同花色 8-6 连;反:三组跨花色 8-6 不连;反:差 2 但非 8/6 不连 | `test_arith` | ✅ 5 | **关于超时**:`countdown` 只作展示、服务端不做任何超时动作,这是 design §11 确认的规则(并非缺口),因此**没有也不需要「超时自动操作」的用例**;反过来,若将来有人给服务端加了超时代打,那才是违反 design §11——现已由 `test_rpc` 的**守卫用例**拦下(见 §8 G9)。 --- ## 7. 第五轮:按 design 原文另写参考实现的差分测试 > 前六节的用例都是**手写的场景枚举**——覆盖的是"我想到的情况"。第五轮补两组**差分测试**:把 design 的规则整条转写成参考实现,与代码大面积对拍,覆盖手写用例想不到的组合。 | 文件 | 覆盖 | 规模 | | --- | --- | --- | | `test_calc.js` | §7 算子全量对拍:参考实现逐字转写自 §7.1/§7.2/§7.3 判定表,比 base / Q / 判定倍率 / 最终子数 | 2 模式 × 14 档 × grade 0~260 = **7308 格**,另按 design 的 17 张表逐格抽查 **141 个写死的最终子数** | | `test_followdiff.js` | §5.2 跟牌强制层级穷举差分:参考裁定按原文写,对每手牌**枚举全部 C 张出牌组合**逐个对拍 | 约 **11 万组** | **差分测试的强度取决于造牌器,不取决于对拍逻辑。** 第一版 `test_followdiff` 用纯随机抽牌跑 7.8 万组全绿,但把 `follow_tractor_cover_ok` 整个关掉**依然全绿**——因为唯一能区分"只出零散对子"与"先凑最长拖拉机"的形状是「同花色三组互不相邻的两连对」(12 张),随机抽根本抽不出来。故: - 随机源必须是**固定种子 PRNG**(不用 `Math.random`),保证用例集可复现; - 关键结构必须用**确定性手牌**钉死(`structHands()`),不能指望随机覆盖到; - 注意别让掺入的其他花色把手牌撑过枚举上限而被**静默跳过**(踩过一次)。 --- ## 8. 第五轮:逐规则的正/反/边界补全 > 对 design §1~§12 重新做了一次"每条规则是否三类用例齐全"的审计。覆盖面本来就广,但仍有 9 处 design 明文规则缺用例(多是**规则本身**而非入参校验,前几轮的矩阵按"被测函数"组织,容易漏掉这类"跨函数才体现"的规则)。 | # | design | 规则 | 补的用例(正/反/边界) | 文件 | | --- | --- | --- | --- | --- | | G1 | §4.2 | 暂定庄家必须叫、其后才可"更低或不叫" | 边界:下限 5 接受;正:非首家 `call=0` 接受;反:负数 / 缺 `call`(NaN) / 步进外小值 1 / 与当前叫分相同 均拒 | `test_rpc` | | G2 | §4.2 | **"不叫"即退出本局叫分,轮次跳过他** | 正:首家叫 65 后轮到 seat1;正:seat1 不叫后跳到 seat2;反:seat1 反悔再叫 → SEAT 拒、`callproc` 不变、仍轮到 seat2、未产生庄家 | `test_rpc` | | G3 | §4.5 | 埋牌仅庄家、仅埋牌阶段、恰 8 张 | 反:非庄家 → SEAT;反:step2/step5 埋 → STEP;边界:7 张 / 9 张 / 空数组 → PARAM | `test_rpc` | | G4 | §5.1 | **每轮由上一轮牌面最大的一方先出** | 正:闲 1 赢 → 下一轮 `start/currseat` 都是 1;边界:闲 2 赢 → 改由闲 2 先出 | `test_paiju` | | G5 | §6.2 | 闲家赢 → 台面**全部**分牌计入;庄家赢 → 分牌作废 | 反:庄赢 → 下发面无 `grade`、闲家捡分 0;正:闲 1 赢 → 15 分(**含庄家自己打出的 ♥5**);边界:闲 2 赢结果与闲 1 赢一致("两个闲家谁大不影响") | `test_paiju` | | G6 | §8 | 算奖快照的时间点与静态性 | 正:庄家 = 埋牌后 28 张、已埋 8 张不在内;正:闲家 = 发牌后 28 张;边界:投降局庄家 = 发牌+暗牌 36 张;边界:之后打出 10 张,快照仍是同样的 28 张 | `test_paiju` | | G7 | §3 | **对子只认同花色同点数的两副** | 正:同花色两副 ♥7/♥2/大王成对;反:♥7+♣7、♥2+♣2、正7+副7、正2+副2、大王+小王 均不成对;边界:四张跨花色 7/2 组不出拖拉机,而正7对+副7对可以 | `test_arith` | | G8 | §6.3 | 赢末轮的牌不全是主牌就不扣底 | 反:副牌两连对赢 → 0;反:混合出牌(主K+副K)→ 0;边界:主副提交顺序颠倒仍判 0 | `test_arith` | | G9 | §11 | **无超时托管**(守卫) | 守卫:叫分→选主→埋牌→出牌全程 `min_ontimeout` 调用次数为 0。将来若有人加"超时自动出牌/代打",必然要注册定时器,这条即转红 | `test_rpc` | **每条都做了变异检验**(在副本上注入违反该规则的改动,确认对应断言转红): | 注入的缺陷 | 转红的断言 | | --- | --- | | 去掉 `mod.jiaofen` 的"非当前叫分位"校验 | G2 三条 | | 埋牌张数 `!= 8` 放宽为 `< 1` | G3 两条边界 | | §6.2 改成庄家赢也累计闲家捡分 | G5 一条 | | 下一轮改为固定庄家先出 | G4 两条 | | 算奖快照不排除已埋的牌 | G6 两条 + 亮牌快照一条 | | `get_pairlist` 放宽为"只看点数" | G7 五条 | | 在 `mod.chupai` 里注册一个定时器 | G9 一条 | > 过程中的一个教训:M2(埋牌张数)第一次跑出来是**整个文件崩溃**而不是干净的 FAIL——因为 `mkBury` 桩缺 `do_burycard`,校验一被改松就走到成功路径抛异常,后面的断言全不执行。**桩要完整到能走完成功路径**,否则"校验被改松"这类变异只会表现为崩溃,定位与信号都变差。已修。 > 原列在此处的两项「待外部输入」均已由规则设计者拍板:「不查牌模式是否屏蔽出牌历史」确认屏蔽,已改 design §9 + 代码 + 用例(见上表);「超时托管默认动作」确认维持现状、不做超时动作,已写入 design §11。至此测试计划无待外部输入项。