Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements. Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate. Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable. Refs: Versioned dependencies U1/U3; feature/dependency-workspace
91 lines
3.3 KiB
Go
91 lines
3.3 KiB
Go
package runmgr
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
|
||
agentllm "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm"
|
||
agentpolicy "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/policy"
|
||
|
||
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
|
||
)
|
||
|
||
func TestPlanModeRejectsWriteAndKeepsRead(t *testing.T) {
|
||
c, st := newExecEnv(t)
|
||
if err := st.SetPermissionMode("alice", c.run.ConversationID, "plan"); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
c.permissionMode = "plan"
|
||
res := c.ExecuteTool(core.ToolCallRequest{CallID: "c1", Name: "write_artifact", Arguments: []byte(`{"name":"a.md","content":"x"}`)})
|
||
if res.Status != "error" || res.Output == "" {
|
||
t.Fatalf("write in plan mode: %+v", res)
|
||
}
|
||
ok := c.ExecuteTool(core.ToolCallRequest{CallID: "c2", Name: "time_now", Arguments: []byte(`{}`)})
|
||
if ok.Status != "ok" {
|
||
t.Fatalf("read in plan mode: %+v", ok)
|
||
}
|
||
names := map[string]bool{}
|
||
for _, tool := range c.Tools() {
|
||
names[tool.Name] = true
|
||
}
|
||
if names["write_artifact"] || !names["time_now"] || !names["ask_user"] || !names["exit_plan_mode"] {
|
||
t.Fatalf("plan tool list: %v", names)
|
||
}
|
||
}
|
||
|
||
func TestDefaultModeStillRunsWrite(t *testing.T) {
|
||
c, _ := newExecEnv(t)
|
||
res := c.ExecuteTool(core.ToolCallRequest{CallID: "c1", Name: "write_artifact", Arguments: []byte(`{"name":"a.md","content":"x"}`)})
|
||
if res.Status != "ok" {
|
||
t.Fatalf("default write: %+v", res)
|
||
}
|
||
}
|
||
|
||
// TestConvAllowStoreKeysByName:SessionAllows 端口的键控口径保持「会话 × 工具名」,
|
||
// 不区分参数——与旧 sessionAllows 布尔参数的行为一致。
|
||
func TestConvAllowStoreKeysByName(t *testing.T) {
|
||
c, _ := newExecEnv(t)
|
||
conv := c.run.ConversationID
|
||
store := convAllowStore{mgr: c.mgr, conv: conv}
|
||
pol := agentpolicy.Policy{
|
||
Rules: []agentpolicy.Rule{{Effect: agentpolicy.EffectAsk, Tool: "run_shell"}},
|
||
SessionAllows: store,
|
||
}
|
||
if got := pol.Decide("run_shell", `{"command":"ls"}`, agentpolicy.Traits{}); got != agentpolicy.EffectAsk {
|
||
t.Fatalf("before remember: %q, want ask", got)
|
||
}
|
||
store.Remember("run_shell", `{"command":"ls"}`)
|
||
if got := pol.Decide("run_shell", `{"command":"rm -rf /tmp/x"}`, agentpolicy.Traits{}); got != agentpolicy.EffectAllow {
|
||
t.Fatalf("after remember, other args: %q, want allow(按名放行)", got)
|
||
}
|
||
if !c.mgr.sessionAllows(conv, "run_shell") {
|
||
t.Fatal("remember did not reach the manager table")
|
||
}
|
||
}
|
||
|
||
// TestPermissionAskRoutesToHITL:内核 ask 决策经 HITL 确认卡裁决;
|
||
// session 选项落会话放行,取消给出可读理由。
|
||
func TestPermissionAskRoutesToHITL(t *testing.T) {
|
||
c, _ := newExecEnv(t)
|
||
c.h = &runHandle{hitlCh: make(chan hitlAnswer, 1)}
|
||
call := agentllm.ToolCall{ID: "c1", Name: "run_shell", Arguments: `{"command":"ls"}`}
|
||
|
||
c.h.hitlCh <- hitlAnswer{resp: core.HITLResponse{ChoiceID: "yes"}}
|
||
if v := c.PermissionAsk(context.Background(), call); !v.Allow {
|
||
t.Fatalf("yes verdict = %+v", v)
|
||
}
|
||
|
||
c.h.hitlCh <- hitlAnswer{resp: core.HITLResponse{ChoiceID: "session"}}
|
||
if v := c.PermissionAsk(context.Background(), call); !v.Allow {
|
||
t.Fatalf("session verdict = %+v", v)
|
||
}
|
||
if !c.mgr.sessionAllows(c.run.ConversationID, "run_shell") {
|
||
t.Fatal("session choice did not remember the allow")
|
||
}
|
||
|
||
c.h.hitlCh <- hitlAnswer{resp: core.HITLResponse{ChoiceID: "no"}}
|
||
if v := c.PermissionAsk(context.Background(), call); v.Allow || v.Reason == "" {
|
||
t.Fatalf("no verdict = %+v", v)
|
||
}
|
||
}
|