返回 DeepSeek-Reasonix
writedoc_test.go
根目录 / internal / memory / writedoc_test.go
1 package memory
2
3 import (
4 "os"
5 "path/filepath"
6 "runtime"
7 "strings"
8 "testing"
9 )
10
11 // TestWriteDocAllowsRecognizedFiles verifies WriteDoc overwrites a canonical
12 // scope file (project REASONIX.md) and that it round-trips.
13 func TestWriteDocAllowsRecognizedFiles(t *testing.T) {
14 proj := t.TempDir()
15 mustMkdir(t, filepath.Join(proj, ".git"))
16 set := Load(Options{CWD: proj})
17
18 path := set.DocPath(ScopeProject)
19 if _, err := set.WriteDoc(path, "# New project memory\n\nUse spaces."); err != nil {
20 t.Fatalf("WriteDoc: %v", err)
21 }
22 b, err := os.ReadFile(path)
23 if err != nil {
24 t.Fatal(err)
25 }
26 if !strings.Contains(string(b), "Use spaces.") {
27 t.Fatalf("body not written: %q", b)
28 }
29 }
30
31 // TestWriteDocRejectsArbitraryPaths is the security guard: the panel must not be
32 // able to overwrite files that aren't recognized memory docs.
33 func TestWriteDocRejectsArbitraryPaths(t *testing.T) {
34 proj := t.TempDir()
35 mustMkdir(t, filepath.Join(proj, ".git"))
36 set := Load(Options{CWD: proj})
37
38 evil := filepath.Join(proj, "..", "escape.txt")
39 if _, err := set.WriteDoc(evil, "pwned"); err == nil {
40 t.Fatal("WriteDoc accepted an arbitrary path; it must reject non-memory files")
41 }
42 if _, err := os.Stat(evil); err == nil {
43 t.Fatal("WriteDoc wrote a file it should have refused")
44 }
45 }
46
47 // TestWriteDocAllowsDiscoveredDoc verifies an already-discovered doc (e.g. an
48 // AGENTS.md the user is editing) stays writable even though it isn't a canonical
49 // REASONIX.md scope target.
50 func TestWriteDocAllowsDiscoveredDoc(t *testing.T) {
51 proj := t.TempDir()
52 mustMkdir(t, filepath.Join(proj, ".git"))
53 agents := filepath.Join(proj, "AGENTS.md")
54 mustWrite(t, agents, "original")
55 set := Load(Options{CWD: proj})
56
57 if _, err := set.WriteDoc(agents, "edited"); err != nil {
58 t.Fatalf("WriteDoc on discovered AGENTS.md: %v", err)
59 }
60 b, _ := os.ReadFile(agents)
61 if strings.TrimSpace(string(b)) != "edited" {
62 t.Fatalf("AGENTS.md not updated: %q", b)
63 }
64 }
65
66 func TestWriteDocRejectsInstructionSymlinkOutsideWorkspace(t *testing.T) {
67 if runtime.GOOS == "windows" {
68 t.Skip("symlink creation requires elevated privileges on common Windows setups")
69 }
70 proj := t.TempDir()
71 mustMkdir(t, filepath.Join(proj, ".git"))
72 outside := filepath.Join(t.TempDir(), "outside.md")
73 mustWrite(t, outside, "keep me")
74 agents := filepath.Join(proj, "AGENTS.md")
75 if err := os.Symlink(outside, agents); err != nil {
76 t.Fatal(err)
77 }
78
79 set := Load(Options{CWD: proj})
80 if _, err := set.WriteDoc(agents, "overwritten"); err == nil {
81 t.Fatal("WriteDoc followed an instruction symlink outside the workspace")
82 }
83 body, err := os.ReadFile(outside)
84 if err != nil {
85 t.Fatal(err)
86 }
87 if string(body) != "keep me" {
88 t.Fatalf("outside target changed to %q", body)
89 }
90 }
91
91 lines GO