Files
erqiwang_youle/server/games/erqiwang/docs/compliance/02-测试计划.md
T
joywayerandClaude Opus 5 b6aeba412f 二七王:把「无超时托管」确认为规则,写入 design §11
服务端四个阶段只下发 countdown、没有任何定时器,此前被本轮核对记
为待办 D3(等规则设计者定义超时默认动作)。规则设计者已确认:
**维持现状,不需要超时动作**。

这属于规则本身如此、而非实现缺口。若不固化下来,下一轮核对会再次
把它当缺陷提出、甚至有人去补一套「超时自动出牌」,反而违背规则。
故写进 design.md 作为权威约定:

- design §11 新增一条:倒计时只作展示、不触发任何自动操作——不自
  动叫分/选主/埋牌/出牌,也不判负、不跳过;轮到谁而谁不操作,牌局
  就停在这一步等待。并加醒目说明:这是确认过的规则、不是待实现的
  缺口,牌局停住由玩家走房间解散流程收场(按当前累计分结算,
  §12.2),后续核对不要为它补超时托管。
  (解散路径已核实:平台 server_room 侧有 freeroom 解散倒计时,并
  回调子游戏 export.get_disbandRoom → get_paiju_account(2) 出解散
  结算包,与 §12.2 一致。)
- 协议文档新增 0.3「countdown 只是展示用的秒数」:说明服务端无定
  时器、归零不做任何事,并要求客户端不得在归零时做乐观界面推进
  (原 0.3 顺延为 0.4)。
- 合规 01:D3 由「🟧 中等/未修」改为「✅ 规则如此」,并说明 server
  dev-guide「服务端自动操作复用真人链路」约束的是「若要做代打就必
  须复用真人链路」,本局不做代打故不适用;「尚未处置」小节改为
  「无」,11 项全部收口。
- 合规 02:撤掉「仍无自动化覆盖」中的超时项,改为说明不需要超时用
  例,并反向标注:若将来有人给服务端加了超时代打,那才是违反
  design §11、应由核对拦下。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 19:05:24 +08:00

18 KiB
Raw Blame History

二七王服务端 · 合规测试计划

目标:用 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)

  1. ✅ §9 查牌门控 + mingpai——chupai 的 info/baozhu 按 nocheck 门控(可查有/不查无);mingpai 合法下发他家主牌,不查牌/未报无主/非出牌阶段均拒。
  2. ✅ §4 投降 + 暗牌亮牌——touxiang 仅 70分/step2/庄家;真实 jiaofen 驱动到 70分上庄,验证 shangzhuang 闲家有 bottomcards+ancard3s、叫5上庄闲家无。
  3. ✅ §4.2 mod.jiaofen 入参校验——>70/非5倍/首家必叫/后叫更低 全部拒,合法接受。
  4. ✅ §4.6 坐庄轮换 do_prepare——庄赢连庄、闲赢/投降下家。

P2 · 补强既有 —— ✅ 已完成

  1. ✅ §2 牌局构成专测(test_deal)、§3 编码/排序专测(test_arith:order_cards + is_continuous 链)。
  2. ✅ §9 mod.maipai 亮牌门控 / get_deskinfo PushCards 门控(test_rpc 端到端驱动后验证)。
  3. ✅ §8.2 亮牌更多阈值(6个7/固定主9-10边界)、§7 算子每档分界+升3级(test_arith)。
  4. ✅ §12 端到端(test_rpc 真实驱动到出牌就绪)。
  5. ✅ §10 直接调 export.get_asetcount/get_needroomcard/get_needroomcard_joinroom(test_config)。
  6. ✅ §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 全部完成

共 361 项断言全绿(test_arith 106 / test_callgrade 6 / test_config 19 / test_deal 12 / test_desk 6 / test_follow 50 / test_input 34 / test_paiju 31 / test_rpc 68 / test_success 29)。

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,应由核对拦下。

原列在此处的两项「待外部输入」均已由规则设计者拍板:「不查牌模式是否屏蔽出牌历史」确认屏蔽,已改 design §9 + 代码 + 用例(见上表);「超时托管默认动作」确认维持现状、不做超时动作,已写入 design §11。至此测试计划无待外部输入项。