Files
Agent-Squad/adapters/native/register_test.go
T
weichen aa15a5d164 Version resolver configs and invalidate executor caches
Why: F06 - the wrapped-executor cache keyed only backend/role/model, so
allow->deny permission tightening and factory replacement never took
effect on subsequent resolves.
Evidence: squad and adapters/native tests pass (verify-u4.json); config
identity helper covers permission/tool/limiter changes.
Impact: stale executors can no longer widen capabilities; in-flight
tasks are not rolled back (documented).
Refs: feature/agent-stack-audit-remediation-b1 U4
2026-10-21 15:51:53 +00:00

132 lines
4.3 KiB
Go

package native
import (
"testing"
"time"
"agent-native/runtime/llm"
"agent-native/runtime/tools"
"agent-squad/squad"
)
func TestBuildOptionsBashPermissionGating(t *testing.T) {
base := Config{
Client: &llm.Client{BaseURL: "http://x", APIKey: "k"},
DefaultModel: "m1", MaxTurns: 5, Timeout: time.Minute,
Exec: &tools.ExecConfig{Bash: "/bin/bash"},
SkillsRoots: []string{"/skills"},
}
deny, err := buildOptions(base, squad.RoleSpec{Role: "r", BashPermission: squad.BashPermissionDeny})
if err != nil {
t.Fatal(err)
}
if deny.Exec != nil {
t.Fatal("deny role must not get exec tools even when config provides them")
}
ask, _ := buildOptions(base, squad.RoleSpec{Role: "r", BashPermission: squad.BashPermissionAsk})
if ask.Exec == nil {
t.Fatal("ask role gets exec tools")
}
allow, _ := buildOptions(base, squad.RoleSpec{Role: "r", BashPermission: squad.BashPermissionAllow})
if allow.Exec == nil {
t.Fatal("allow role gets exec tools")
}
empty, _ := buildOptions(base, squad.RoleSpec{Role: "r"})
if empty.Exec != nil {
t.Fatal("unset bash_permission defaults to deny")
}
none, _ := buildOptions(Config{Client: base.Client, DefaultModel: "m"}, squad.RoleSpec{Role: "r", BashPermission: squad.BashPermissionAllow})
if none.Exec != nil {
t.Fatal("no Exec config = no exec tools")
}
roleModel, _ := buildOptions(base, squad.RoleSpec{Role: "r", Model: "big"})
if roleModel.Model != "big" {
t.Fatalf("role model must win, got %s", roleModel.Model)
}
if _, err := buildOptions(Config{Client: base.Client}, squad.RoleSpec{Role: "r"}); err == nil {
t.Fatal("missing model must fail-closed")
}
}
func TestConfigIdentityReflectsToolAndClient(t *testing.T) {
base := Config{
Client: &llm.Client{BaseURL: "http://x", APIKey: "k"},
DefaultModel: "m1", MaxTurns: 5, Timeout: time.Minute,
Exec: &tools.ExecConfig{Bash: "/bin/bash"},
}
otherExec := base
otherExec.Exec = &tools.ExecConfig{Bash: "/bin/zsh"}
if base.Identity() == otherExec.Identity() {
t.Fatal("Exec tool-config change must change Config.Identity")
}
otherClient := base
otherClient.Client = &llm.Client{BaseURL: "http://y", APIKey: "k"}
if base.Identity() == otherClient.Identity() {
t.Fatal("client change must change Config.Identity")
}
same := base
if base.Identity() != same.Identity() {
t.Fatal("identical config must keep Identity stable")
}
}
func TestRegisterFullAllowDenyTakesEffectOnNextResolve(t *testing.T) {
r := squad.NewBackendResolver()
cfg := Config{
Client: &llm.Client{BaseURL: "http://x", APIKey: "k"},
DefaultModel: "m1", MaxTurns: 1, Timeout: time.Second,
Exec: &tools.ExecConfig{Bash: "/bin/bash"},
}
RegisterFull(r, cfg)
allowRole := squad.RoleSpec{Role: "eng", Backend: "agent-native", Model: "m1", BashPermission: squad.BashPermissionAllow}
denyRole := squad.RoleSpec{Role: "eng", Backend: "agent-native", Model: "m1", BashPermission: squad.BashPermissionDeny}
eAllow, _, err := r.Resolve(allowRole, "")
if err != nil {
t.Fatal(err)
}
eDeny, _, err := r.Resolve(denyRole, "")
if err != nil {
t.Fatal(err)
}
if eAllow == eDeny {
t.Fatal("allow→deny must not reuse cached executor on next Resolve")
}
// Identity of role configs must diverge so cache keys differ.
if squad.ConfigIdentity(allowRole) == squad.ConfigIdentity(denyRole) {
t.Fatal("role ConfigIdentity must include BashPermission")
}
}
func TestRegisterFullToolConfigReplacementTakesEffect(t *testing.T) {
r := squad.NewBackendResolver()
cfg1 := Config{
Client: &llm.Client{BaseURL: "http://x", APIKey: "k"},
DefaultModel: "m1", MaxTurns: 1, Timeout: time.Second,
Exec: &tools.ExecConfig{Bash: "/bin/bash"},
}
cfg2 := Config{
Client: &llm.Client{BaseURL: "http://x", APIKey: "k"},
DefaultModel: "m1", MaxTurns: 1, Timeout: time.Second,
Exec: &tools.ExecConfig{Bash: "/bin/zsh"},
}
if cfg1.Identity() == cfg2.Identity() {
t.Fatal("precondition: tool configs must differ in Identity")
}
RegisterFull(r, cfg1)
role := squad.RoleSpec{Role: "eng", Backend: "agent-native", Model: "m1", BashPermission: squad.BashPermissionAllow}
e1, _, err := r.Resolve(role, "")
if err != nil {
t.Fatal(err)
}
RegisterFull(r, cfg2)
e2, _, err := r.Resolve(role, "")
if err != nil {
t.Fatal(err)
}
if e1 == e2 {
t.Fatal("RegisterFull tool-config replacement must invalidate cache")
}
}