|
|
@@ -45,6 +45,11 @@ func TestNestingInvariants_ValidFixtureIsClean(t *testing.T) {
|
|
|
// ⇒ 只要作用域丢了 root,I4 会一次报 100+ 条、I5 差 −130。**这个量级不可能是实现错**,
|
|
|
// 一眼就能定位到"作用域"。这组数字比合成 fixture 更能说明问题,所以写在这里。
|
|
|
//
|
|
|
+// ⚠ **不要"优化"回 `Σ(right−left+1)/2 == 节点数`**(team-lead 原式,已确认有误):
|
|
|
+// 反例是一棵完全合法的两节点树 `a1[1,4] / a2[2,3]` ⇒ `(4+2)/2 = 3 ≠ 2`。
|
|
|
+// 原因:嵌套节点的跨度**互相包含**,求和会重复计数;原式只在 n=1 时偶然成立。
|
|
|
+// 正确恒等式是"每个节点占 2 个槽位"⇒ 整棵树宽度 == 2n。见到有人改回原式,把这两个数摆给他算。
|
|
|
+//
|
|
|
// 合成 fixture(两棵树同形)上的变异探针(本轮真跑过,改 `nestKey` 去掉 root 后):
|
|
|
//
|
|
|
// I4 报 1 条:left_val=1 重复(a1 与 b1)
|
|
|
@@ -109,3 +114,42 @@ func TestNestingInvariants_I5DetectsMissingShift(t *testing.T) {
|
|
|
t.Fatalf("区间总量不守恒(Σ/2 ≠ 节点数)必须被 I5 抓到,实得 0 条:%v", vs)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// TestNestingInvariants_I6CatchesGrandparentParent 负向:parent 指成**祖父**。
|
|
|
+//
|
|
|
+// 形态:r[1,6] → p[2,5] → c[3,4],但 c.parent 写成 r(祖父)。
|
|
|
+// 区间三条件(I1 包含、I2 层级、I3 同 root)里,I1/I3 **都能过**;
|
|
|
+// 唯一能抓的是 I6 —— 因为 p 才是"包含 c 的最窄区间"。
|
|
|
+// 这一条正是"区间完全合法但 parent_userid 指错"的典型。
|
|
|
+func TestNestingInvariants_I6CatchesGrandparentParent(t *testing.T) {
|
|
|
+ nodes := []DivideNode{
|
|
|
+ {UserID: "r", RootID: "A", ParentID: "", LeftVal: 1, RightVal: 6, Layer: 1},
|
|
|
+ {UserID: "p", RootID: "A", ParentID: "r", LeftVal: 2, RightVal: 5, Layer: 2},
|
|
|
+ {UserID: "c", RootID: "A", ParentID: "r", LeftVal: 3, RightVal: 4, Layer: 3}, // ← 指向祖父
|
|
|
+ }
|
|
|
+ vs := CheckNestingInvariants(nodes)
|
|
|
+ if CountViolationsByInvariant(vs)["I6"] == 0 {
|
|
|
+ t.Fatalf("parent 指成祖父必须被 I6 抓到(区间合法性全过),实得:%v", vs)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// TestNestingInvariants_I6CatchesCrossTreeParent 负向:parent 指到**另一棵树**。
|
|
|
+func TestNestingInvariants_I6CatchesCrossTreeParent(t *testing.T) {
|
|
|
+ nodes := []DivideNode{
|
|
|
+ {UserID: "a_root", RootID: "A", ParentID: "", LeftVal: 1, RightVal: 6, Layer: 1},
|
|
|
+ {UserID: "b_root", RootID: "B", ParentID: "", LeftVal: 1, RightVal: 2, Layer: 1},
|
|
|
+ // 区间上 a_root 确实包含 c,但 c 属于 B 树 ⇒ root 不一致
|
|
|
+ {UserID: "c", RootID: "B", ParentID: "a_root", LeftVal: 3, RightVal: 4, Layer: 2},
|
|
|
+ }
|
|
|
+ vs := CheckNestingInvariants(nodes)
|
|
|
+ if CountViolationsByInvariant(vs)["I6"] == 0 {
|
|
|
+ t.Fatalf("parent 指向另一棵树必须被 I6 抓到,实得:%v", vs)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// TestNestingInvariants_I6AcceptsCorrectParent 正向:父子关系正确时 I6 不报。
|
|
|
+func TestNestingInvariants_I6AcceptsCorrectParent(t *testing.T) {
|
|
|
+ if vs := CheckNestingInvariants(twoRootFixture()); len(vs) != 0 {
|
|
|
+ t.Fatalf("两棵合法树的父子关系应零违规,实得 %d 条:%v", len(vs), vs)
|
|
|
+ }
|
|
|
+}
|