返回 DeepSeek-Reasonix
remote_legacy_cleanup_test.go
根目录 / desktop / remote_legacy_cleanup_test.go
1 package main
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "reasonix/internal/config"
9 )
10
11 func seedLegacyWorkbenchData(t *testing.T, trust bool) {
12 t.Helper()
13 dir := config.MemoryUserDir()
14 if dir == "" {
15 t.Fatal("test MemoryUserDir is empty")
16 }
17 if err := os.MkdirAll(filepath.Join(dir, "remote-mirrors", "host-a"), 0o700); err != nil {
18 t.Fatal(err)
19 }
20 for _, name := range []string{"a.jsonl", "b.jsonl"} {
21 if err := os.WriteFile(filepath.Join(dir, "remote-mirrors", "host-a", name), make([]byte, 128), 0o600); err != nil {
22 t.Fatal(err)
23 }
24 }
25 if trust {
26 if err := os.WriteFile(filepath.Join(dir, "remote-provider-trust.json"), []byte(`{"v":1}`), 0o600); err != nil {
27 t.Fatal(err)
28 }
29 }
30 }
31
32 func TestScanRemoteLegacyWorkbenchDataReadsCountsOnly(t *testing.T) {
33
34 t.Setenv("REASONIX_STATE_HOME", t.TempDir())
35 seedLegacyWorkbenchData(t, true)
36 a := &App{}
37 view := a.ScanRemoteLegacyWorkbenchData()
38 if view.MirrorCount != 2 {
39 t.Fatalf("mirror count = %d, want 2", view.MirrorCount)
40 }
41 if view.MirrorBytes != 256 {
42 t.Fatalf("mirror bytes = %d, want 256", view.MirrorBytes)
43 }
44 if !view.TrustFile {
45 t.Fatal("trust file not detected")
46 }
47
48 a2 := &App{}
49 view2 := a2.ScanRemoteLegacyWorkbenchData()
50 if view2.MirrorCount != 2 || !view2.TrustFile {
51 t.Fatalf("scan mutated the data it reads: rescan = %+v", view2)
52 }
53 }
54
55 func TestScanRemoteLegacyWorkbenchDataEmptyWhenAbsent(t *testing.T) {
56
57 t.Setenv("REASONIX_STATE_HOME", t.TempDir())
58 a := &App{}
59 view := a.ScanRemoteLegacyWorkbenchData()
60 if view.MirrorCount != 0 || view.MirrorBytes != 0 || view.TrustFile {
61 t.Fatalf("empty scan = %+v", view)
62 }
63 }
64
65 func TestCleanRemoteLegacyWorkbenchDataRemovesMirrorsOnly(t *testing.T) {
66
67 t.Setenv("REASONIX_STATE_HOME", t.TempDir())
68 seedLegacyWorkbenchData(t, true)
69 a := &App{}
70 if err := a.CleanRemoteLegacyWorkbenchData("mirrors"); err != nil {
71 t.Fatal(err)
72 }
73 view := a.ScanRemoteLegacyWorkbenchData()
74 if view.MirrorCount != 0 {
75 t.Fatalf("mirrors survived cleanup: %+v", view)
76 }
77 if !view.TrustFile {
78 t.Fatal("trust file removed by the mirrors cleanup")
79 }
80 if err := a.CleanRemoteLegacyWorkbenchData("trust"); err != nil {
81 t.Fatal(err)
82 }
83 if a.ScanRemoteLegacyWorkbenchData().TrustFile {
84 t.Fatal("trust file survived cleanup")
85 }
86 }
87
88 func TestCleanRemoteLegacyWorkbenchDataRejectsUnknownAndEscapeTargets(t *testing.T) {
89
90 t.Setenv("REASONIX_STATE_HOME", t.TempDir())
91 a := &App{}
92 if err := a.CleanRemoteLegacyWorkbenchData("everything"); err == nil {
93 t.Fatal("unknown target accepted")
94 }
95 if err := a.CleanRemoteLegacyWorkbenchData("../remote-mirrors"); err == nil {
96 t.Fatal("path traversal target accepted")
97 }
98 if err := a.CleanRemoteLegacyWorkbenchData(""); err == nil {
99 t.Fatal("empty target accepted")
100 }
101 }
102
103 func TestCleanRemoteLegacyWorkbenchDataRejectsSymlinkedMirrorDir(t *testing.T) {
104
105 t.Setenv("REASONIX_STATE_HOME", t.TempDir())
106 if os.Getenv("REASONIX_LEGACY_CLEANUP_NO_SYMLINK") != "" {
107 t.Skip("environment does not allow symlinks")
108 }
109 dir := config.MemoryUserDir()
110 if dir == "" {
111 t.Fatal("test MemoryUserDir is empty")
112 }
113 outside := t.TempDir()
114 link := filepath.Join(dir, "remote-mirrors")
115 _ = os.RemoveAll(link)
116 if err := os.Symlink(outside, link); err != nil {
117 t.Skipf("cannot create symlink: %v", err)
118 }
119 t.Cleanup(func() { _ = os.Remove(link) })
120 a := &App{}
121 if err := a.CleanRemoteLegacyWorkbenchData("mirrors"); err == nil {
122 t.Fatal("symlinked mirror dir was accepted for cleanup")
123 }
124 if _, err := os.Stat(outside); err != nil {
125 t.Fatalf("symlink target was touched: %v", err)
126 }
127 }
128
129 func TestCleanRemoteLegacyWorkbenchDataIdempotentWhenAbsent(t *testing.T) {
130
131 t.Setenv("REASONIX_STATE_HOME", t.TempDir())
132 a := &App{}
133 if err := a.CleanRemoteLegacyWorkbenchData("mirrors"); err != nil {
134 t.Fatalf("cleanup of absent mirrors = %v", err)
135 }
136 if err := a.CleanRemoteLegacyWorkbenchData("trust"); err != nil {
137 t.Fatalf("cleanup of absent trust = %v", err)
138 }
139 }
140
141 func TestWithinReasonixPrivateDirRejectsEscapes(t *testing.T) {
142
143 t.Setenv("REASONIX_STATE_HOME", t.TempDir())
144 dir := config.MemoryUserDir()
145 for _, path := range []string{
146 filepath.Join(dir, "remote-mirrors"),
147 filepath.Join(dir, "nested", "remote-provider-trust.json"),
148 } {
149 if !withinReasonixPrivateDir(path) {
150 t.Fatalf("legitimate path rejected: %q", path)
151 }
152 }
153 for _, path := range []string{
154 filepath.Join(dir, "..", "elsewhere"),
155 filepath.Join(dir, ".."),
156 filepath.Join(dir, "remote-mirrors", "..", "..", "escape"),
157 "/etc/passwd",
158 } {
159 if withinReasonixPrivateDir(path) {
160 t.Fatalf("escape path accepted: %q", path)
161 }
162 }
163 }
164
164 lines GO