package skills import ( "strings" "testing" ) // 本文件专测 design/09 §6.4-3 的能力收缩:**只收不放**。 func TestResolveToolSurface_表驱动(t *testing.T) { t.Parallel() registered := []string{"alpha", "beta", "gamma"} cases := []struct { name string allowed []string disallowed []string gate Gate want string }{ { name: "未声明上限时不额外收窄(仍受安全闸约束)", gate: NewNameSet([]string{"alpha", "beta", "gamma"}), want: "alpha,beta,gamma", }, { name: "allowed 是上限:列表外的工具不可见", allowed: []string{"alpha"}, gate: NewNameSet([]string{"alpha", "beta", "gamma"}), want: "alpha", }, { name: "disallowed 从上限里剔除", allowed: []string{"alpha", "beta"}, disallowed: []string{"beta"}, gate: NewNameSet([]string{"alpha", "beta", "gamma"}), want: "alpha", }, { name: "安全闸拒绝的工具即使被技能允许也不可用", allowed: []string{"alpha", "beta"}, gate: NewNameSet([]string{"alpha"}), want: "alpha", }, { name: "allowed 里的幽灵工具不会凭空出现(∩ 注册表)", allowed: []string{"alpha", "ghost"}, gate: NewNameSet([]string{"alpha", "ghost"}), want: "alpha", }, { name: "缺安全闸 ⇒ 全拒(fail-closed)", gate: nil, want: "", }, { name: "空注册表 ⇒ 空面", gate: NewNameSet([]string{"alpha"}), want: "", }, } for _, tc := range cases { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() tools := registered if tc.name == "空注册表 ⇒ 空面" { tools = nil } got := ResolveToolSurface(tools, tc.allowed, tc.disallowed, tc.gate) if strings.Join(got, ",") != tc.want { t.Fatalf("ResolveToolSurface = %q, want %q", strings.Join(got, ","), tc.want) } }) } } // TestResolveToolSurface_只收不放 是本片的核心不变式:无论技能怎么声明,结果永远 // 是「注册表 ∩ 安全闸」的子集 —— 技能**加不进**任何东西。 func TestResolveToolSurface_只收不放(t *testing.T) { t.Parallel() registered := []string{"alpha", "beta"} gate := NewNameSet([]string{"alpha", "beta"}) // 技能把 allowed-tools 写成注册表之外的、或安全闸之外的,都不该出现在结果里。 got := ResolveToolSurface(registered, []string{"ghost", "secret"}, nil, gate) if len(got) != 0 { t.Fatalf("ResolveToolSurface = %v, want 空(技能不得放宽任何工具)", got) } // 反过来:技能把*所有*注册表工具都写进 allowed,也只能拿到安全闸允许的那些。 got = ResolveToolSurface(registered, registered, nil, NewNameSet([]string{"beta"})) if strings.Join(got, ",") != "beta" { t.Fatalf("ResolveToolSurface = %v, want [beta]", got) } } func TestResolveToolSurface_去重与保序(t *testing.T) { t.Parallel() got := ResolveToolSurface([]string{"b", "a", "b", " ", "c"}, nil, nil, NewNameSet([]string{"a", "b", "c"})) if strings.Join(got, ",") != "b,a,c" { t.Fatalf("ResolveToolSurface = %q, want b,a,c(保注册表序 + 去重 + 跳空白)", strings.Join(got, ",")) } } func TestAllOf_全允许才允许(t *testing.T) { t.Parallel() allowAll := NewNameSet([]string{"a", "b"}) onlyA := NewNameSet([]string{"a"}) if !AllOf(allowAll, onlyA).Allow("a") { t.Fatal("AllOf(allowAll, onlyA).Allow(a) = false, want true") } if AllOf(allowAll, onlyA).Allow("b") { t.Fatal("AllOf(allowAll, onlyA).Allow(b) = true, want false") } if AllOf(allowAll, GateFunc(nil)).Allow("a") { t.Fatal("含 nil 函数闸时应当全拒") } // 没有任何有效闸 ⇒ 全拒(绝不给出无人看守的工具面)。 if AllOf(nil, nil).Allow("a") { t.Fatal("AllOf() 全 nil 时应当全拒") } if AllOf().Allow("a") { t.Fatal("AllOf() 无参时应当全拒") } } func TestNameSet_语义(t *testing.T) { t.Parallel() set := NewNameSet([]string{" a ", "", "b"}) if !set.Allow("a") || !set.Allow("b") { t.Fatalf("NameSet = %v, want 含 a/b(并去掉空白)", set) } if set.Allow("") || set.Allow("c") { t.Fatal("NameSet 不该放行未登记的名字") } var nilSet NameSet if nilSet.Allow("a") { t.Fatal("nil NameSet 必须全拒") } } func TestSortedCopy_不改原切片(t *testing.T) { t.Parallel() in := []string{"b", "a"} got := SortedCopy(in) if strings.Join(got, ",") != "a,b" { t.Fatalf("SortedCopy = %v, want [a b]", got) } if in[0] != "b" { t.Fatalf("SortedCopy 改动了入参:%v", in) } }