返回 DeepSeek-Reasonix
write_access_test.go
根目录 / internal / agent / write_access_test.go
1 package agent
2
3 import (
4 "context"
5 "encoding/json"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10
11 "reasonix/internal/sandbox"
12 "reasonix/internal/tool"
13 "reasonix/internal/tool/builtin"
14 )
15
16 func TestBindChildWriteRootsSnapshotsParentGrants(t *testing.T) {
17 work := t.TempDir()
18 extra := t.TempDir()
19 parent := sandbox.NewWritableRootSet([]string{work})
20 parent.GrantSession([]string{extra})
21 reg := tool.NewRegistry()
22 for _, tl := range (builtin.Workspace{Dir: work, WriteRoots: []string{work}, WriteRootSet: parent}).Tools("write_file") {
23 if tl.Name() == "write_file" {
24 reg.Add(tl)
25 }
26 }
27 _, child := BindChildWriteRoots(reg, parent, WritePathSet{})
28 later := t.TempDir()
29 parent.GrantSession([]string{later})
30 if child.Covers(later) {
31 t.Fatal("child must not inherit later parent grants")
32 }
33 if !child.Covers(extra) {
34 t.Fatal("child should inherit the snapshot that existed at spawn")
35 }
36
37 pkg := filepath.Join(work, "pkg")
38 if err := os.MkdirAll(pkg, 0o755); err != nil {
39 t.Fatal(err)
40 }
41 claims, err := NormalizeWritePaths(work, []string{"pkg"})
42 if err != nil {
43 t.Fatal(err)
44 }
45 _, restricted := BindChildWriteRoots(reg, parent, claims)
46 if restricted.Covers(extra) {
47 t.Fatal("explicit write_paths must drop unrelated session grants")
48 }
49 if !restricted.Covers(pkg) {
50 t.Fatal("explicit write_paths should keep the intersection")
51 }
52
53 whole, err := WholeWorkspaceWriteClaim(work)
54 if err != nil {
55 t.Fatal(err)
56 }
57 _, inherited := BindChildWriteRoots(reg, parent, whole)
58 if !inherited.Covers(extra) {
59 t.Fatal("omitted write_paths still inherit the existing session snapshot")
60 }
61 }
62
63 func TestApplyWriteAccessSubagentUsesStructuredHint(t *testing.T) {
64 work := t.TempDir()
65 outside := t.TempDir()
66 set := sandbox.NewWritableRootSet([]string{work})
67 a := &Agent{svc: agentServices{
68 writeRoots: set,
69 writeAccessExpandable: false,
70 workspaceRoot: work,
71 homeDir: work,
72 stateRoot: t.TempDir(),
73 }}
74 var write tool.Tool
75 for _, tl := range (builtin.Workspace{Dir: work, WriteRoots: []string{work}, WriteRootSet: set}).Tools("write_file") {
76 if tl.Name() == "write_file" {
77 write = tl
78 }
79 }
80 if write == nil {
81 t.Fatal("write_file missing")
82 }
83 args, err := json.Marshal(map[string]string{"path": filepath.Join(outside, "x.go"), "content": "x"})
84 if err != nil {
85 t.Fatal(err)
86 }
87 out, early := a.applyWriteAccess(context.Background(), &toolCallPlan{
88 execTool: write,
89 permName: "write_file",
90 permArgs: args,
91 })
92 if !early || !out.blocked {
93 t.Fatalf("expected blocked write access, got %+v early=%v", out, early)
94 }
95 if !strings.Contains(out.output, "parent agent") {
96 t.Fatalf("sub-agent hint missing: %s", out.output)
97 }
98 }
99
99 lines GO