Parcourir la source

docs(ac15): 批B 变异实验 M1/M2/M3 实测记录(spend扣balance / 206同向 / baddebt单增)

Q il y a 8 heures
Parent
commit
ce1a5de666
1 fichiers modifiés avec 22 ajouts et 1 suppressions
  1. 22 1
      internal/academic/dualrun/stage2/README.md

+ 22 - 1
internal/academic/dualrun/stage2/README.md

@@ -249,7 +249,9 @@ SP11 的签名只有 4 个参数(`inputUserid, inputSchoolid, inputCampusid, i
 
 > `t_user_expense_log.id` 是自增,回滚后**不会回退**;重跑时 id 会跳号。断言里一律不用固定 id。
 
-## 6. 变异实验(3 组,都已还原)
+## 6. 变异实验(都已还原)
+
+### 批一 / 批 A
 
 |#|改错什么|结果|
 |---|---|---|
@@ -260,3 +262,22 @@ SP11 的签名只有 4 个参数(`inputUserid, inputSchoolid, inputCampusid, i
 
 > M2 的说明:种子 SQL 每次都把账户 `UPDATE` 回 finance_bk 基线,所以"第二次跑基线不等"这个症状不会出现;
 > 真正抓住它的是**残留断言**(并且在截图里能看到 target 侧多出 1 条已提交的流水)。
+
+### 批 B(三支各打一处,全部"红成应红的那一条")
+
+|#|改错什么|靶子断言|实测输出|
+|---|---|---|---|
+|**M1**|`spend` 也扣 `balance`(全局+校区)|`Δbalance == 0`|`✘ 累计消费(spend):Δbalance 必须为 0` → `全局 0.00/-25.00;转出校区 0.00/-25.00`;FAIL 3 项|
+|**M2**|`transfer` 的 206 写成 `+m`(同向)|`SUM(money) == 0`|`✘ 转结:… SUM(money)==0` → `target 2 条 types=106,206 SUM=100.00(期望 0)`;FAIL 2 项|
+|**M3**|`baddebt` 只加 `special_expenses`、不加 `balance`|双增断言|`✘ 坏账:balance 与 special_expenses 必须同时 +金额` → `target 全局 Δbal=0.00 Δspec=80.00`;FAIL 4 项|
+
+M2 有一个值得记的细节:**"两校区反号"那条仍然是绿的**。
+`206` 写成 `+m` 时两个校区的 `total_expenditure` 依然是 −m/+m(账户更新是对的,只有流水符号错了)——
+所以 `SUM(money)==0` 和"两校区反号"**不能互相替代**,必须两条都在。
+这正是"一条断言只钉住一件事"的价值:如果把它们合成一条"转结语义正确",
+M2 会让整条一起红,但看不出到底是账户错了还是流水错了。
+
+M3 还顺带被**第三条**断言抓到:`target.balance_after=180.00 ≠ 调用后全局余额 100.00`。
+`balance_after` 是 Go 独有的列,旧库无从对照,但它自己必须与"调用后的余额"自洽 ——
+这条自洽性检查在这次变异里起到了冗余防线的作用。
+