turbo-editors/turbo-corepublic Fork 0
v1.0.1
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

config_test.go · 290 lines · 7.7 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 14h ago1package acp_test
2
3import (
4 "errors"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9
📦 Turbo Core f3ade8d k33g 7h ago10 "rickub.com/turbo-editors/turbo-core/acp"
11 "rickub.com/turbo-editors/turbo-core/profile"
🛟 Updated. 28d5985 k33g 14h ago12)
13
14// testProfile is an editor whose user directory is a temporary one, so a test
15// never reads the agents of whoever is running it.
16func testProfile(t *testing.T) profile.Profile {
17 t.Helper()
18
19 p := profile.Profile{Name: "Turbo Test", Slug: "turbo-test", Language: "Test", ToolsMenu: "~T~est"}
20 t.Setenv(p.DirEnvVar(), t.TempDir())
21 return p
22}
23
24// writeProject puts an agents file into a project directory and returns it.
25func writeProject(t *testing.T, p profile.Profile, contents string) string {
26 t.Helper()
27
28 dir := t.TempDir()
29 path := acp.ProjectPath(p, dir)
30 if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
31 t.Fatalf("making %s: %v", filepath.Dir(path), err)
32 }
33 if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
34 t.Fatalf("writing %s: %v", path, err)
35 }
36 return dir
37}
38
39// writeUser puts an agents file where the user's own would be.
40func writeUser(t *testing.T, p profile.Profile, contents string) {
41 t.Helper()
42
43 path := acp.UserPath(p)
44 if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
45 t.Fatalf("making %s: %v", filepath.Dir(path), err)
46 }
47 if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
48 t.Fatalf("writing %s: %v", path, err)
49 }
50}
51
52func TestAnAgentIsReadWithEveryKey(t *testing.T) {
53 p := testProfile(t)
54 dir := writeProject(t, p, `
55[[agent]]
56name = "Bob"
57command = "docker"
58args = ["agent", "serve", "acp", "./agent.yaml"]
59env = { TELEMETRY_ENABLED = "false" }
60cwd = "backend"
61`)
62
63 list, err := acp.Load(p, dir)
64 if err != nil {
65 t.Fatalf("Load() error = %v", err)
66 }
67 if list.Len() != 1 {
68 t.Fatalf("read %d agents, want 1", list.Len())
69 }
70
71 agent := list.Agents()[0]
72 switch {
73 case agent.Name != "Bob":
74 t.Errorf("name is %q", agent.Name)
75 case agent.Command != "docker":
76 t.Errorf("command is %q", agent.Command)
77 case strings.Join(agent.Args, " ") != "agent serve acp ./agent.yaml":
78 t.Errorf("args are %v", agent.Args)
79 case agent.Env["TELEMETRY_ENABLED"] != "false":
80 t.Errorf("env is %v", agent.Env)
81 case agent.Cwd != "backend":
82 t.Errorf("cwd is %q", agent.Cwd)
83 }
84}
85
86func TestEnvMayAlsoBeWrittenAsASubTable(t *testing.T) {
87 // Both spellings are ordinary TOML and the documentation offers both, so a
88 // reader who picks the one this test does not cover must not be wrong.
89 p := testProfile(t)
90 dir := writeProject(t, p, `
91[[agent]]
92name = "Bob"
93command = "docker"
94
95[agent.env]
96TOKEN = "secret"
97`)
98
99 list, err := acp.Load(p, dir)
100 if err != nil {
101 t.Fatalf("Load() error = %v", err)
102 }
103 if got := list.Agents()[0].Env["TOKEN"]; got != "secret" {
104 t.Errorf("env is %v", list.Agents()[0].Env)
105 }
106}
107
108func TestAProjectsAgentReplacesOneOfYours(t *testing.T) {
109 // The user's file is your habits and the project's is this repository's, so
110 // the project's is the more specific statement of the two. The rule is the
111 // one snippets already follow.
112 p := testProfile(t)
113 writeUser(t, p, `
114[[agent]]
115name = "Bob"
116command = "yours"
117
118[[agent]]
119name = "Everywhere"
120command = "kept"
121`)
122 dir := writeProject(t, p, `
123[[agent]]
124name = "Bob"
125command = "theirs"
126
127[[agent]]
128name = "Only here"
129command = "added"
130`)
131
132 list, err := acp.Load(p, dir)
133 if err != nil {
134 t.Fatalf("Load() error = %v", err)
135 }
136 if list.Len() != 3 {
137 t.Fatalf("read %d agents, want 3: %v", list.Len(), list.Agents())
138 }
139
140 bob, _ := list.ByName("Bob")
141 if bob.Command != "theirs" {
142 t.Errorf("Bob runs %q, want the project's %q", bob.Command, "theirs")
143 }
144 if _, ok := list.ByName("Everywhere"); !ok {
145 t.Error("the user's other agent was lost")
146 }
147 if _, ok := list.ByName("Only here"); !ok {
148 t.Error("the project's own agent was lost")
149 }
150}
151
152func TestTheOrderIsTheFilesOrder(t *testing.T) {
153 // Somebody reordering the file expects the menu to reorder.
154 p := testProfile(t)
155 dir := writeProject(t, p, `
156[[agent]]
157name = "Third"
158command = "c"
159
160[[agent]]
161name = "First"
162command = "a"
163`)
164
165 list, err := acp.Load(p, dir)
166 if err != nil {
167 t.Fatalf("Load() error = %v", err)
168 }
169 if got := list.Agents()[0].Name; got != "Third" {
170 t.Errorf("the first agent is %q, want the file's own first", got)
171 }
172}
173
174func TestAMissingFileIsNotAnError(t *testing.T) {
175 p := testProfile(t)
176
177 list, err := acp.Load(p, t.TempDir())
178 if err != nil {
179 t.Fatalf("Load() error = %v", err)
180 }
181 if list.Len() != 0 {
182 t.Errorf("read %d agents from nothing", list.Len())
183 }
184}
185
186func TestABrokenFileIsRefusedWholesale(t *testing.T) {
187 // A half-loaded menu offering three of your five agents is worse than an
188 // error saying which line is wrong.
189 p := testProfile(t)
190
191 for _, bad := range []struct {
192 name string
193 contents string
194 says string
195 }{
196 {"no name", "[[agent]]\ncommand = \"docker\"\n", "has no name"},
197 {"no command", "[[agent]]\nname = \"Bob\"\n", "has no command"},
198 {"two of a name", "[[agent]]\nname=\"Bob\"\ncommand=\"a\"\n\n[[agent]]\nname=\"Bob\"\ncommand=\"b\"\n", "two agents are called"},
199 {"a key that is not one", "[[agent]]\nname=\"Bob\"\ncommand=\"a\"\ncomand=\"typo\"\n", "not a key"},
200 {"not TOML at all", "[[agent}}\n", "reading"},
201 } {
202 t.Run(bad.name, func(t *testing.T) {
203 dir := writeProject(t, p, bad.contents)
204
205 list, err := acp.Load(p, dir)
206 if err == nil {
207 t.Fatalf("Load() accepted it and read %v", list.Agents())
208 }
209 if !strings.Contains(err.Error(), bad.says) {
210 t.Errorf("Load() error = %q, want %q in it", err, bad.says)
211 }
212 if list.Len() != 0 {
213 t.Errorf("Load() returned %d agents alongside the error", list.Len())
214 }
215 })
216 }
217}
218
219func TestAMisspeltKeyIsRefusedRatherThanIgnored(t *testing.T) {
220 // This is the one that matters most in practice: `comand` silently ignored
221 // leaves an agent that cannot start and a file that looks right.
222 p := testProfile(t)
223 dir := writeProject(t, p, "[[agent]]\nname=\"Bob\"\ncommand=\"a\"\nenviron={X=\"1\"}\n")
224
225 _, err := acp.Load(p, dir)
226 if err == nil {
227 t.Fatal("Load() accepted a key the format does not define")
228 }
229 if !strings.Contains(err.Error(), "environ") {
230 t.Errorf("Load() error = %q, want the offending key named", err)
231 }
232}
233
234func TestExistsSeesOnlyARegularFile(t *testing.T) {
235 p := testProfile(t)
236
237 empty := t.TempDir()
238 if acp.Exists(p, empty) {
239 t.Error("Exists() is true with no file")
240 }
241
242 dir := writeProject(t, p, "")
243 if !acp.Exists(p, dir) {
244 t.Error("Exists() is false with a file")
245 }
246
247 // A directory where the file should be is not something Load could read.
248 inTheWay := t.TempDir()
249 if err := os.MkdirAll(acp.ProjectPath(p, inTheWay), 0o755); err != nil {
250 t.Fatalf("making the directory: %v", err)
251 }
252 if acp.Exists(p, inTheWay) {
253 t.Error("Exists() counted a directory as the file")
254 }
255}
256
257func TestCreateRefusesToOverwrite(t *testing.T) {
258 p := testProfile(t)
259 p.Templates.Agents = "# %[1]s and %[2]s\n"
260 dir := t.TempDir()
261
262 path, err := acp.Create(p, dir)
263 if err != nil {
264 t.Fatalf("Create() error = %v", err)
265 }
266 if _, err := acp.Create(p, dir); !errors.Is(err, acp.ErrExists) {
267 t.Errorf("Create() error = %v, want ErrExists", err)
268 }
269
270 data, err := os.ReadFile(path)
271 if err != nil {
272 t.Fatalf("reading %s: %v", path, err)
273 }
274 if !strings.Contains(string(data), p.ProjectDir()) {
275 t.Errorf("the created file is %q, want the project directory in it", data)
276 }
277 if !strings.Contains(string(data), acp.UserPath(p)) {
278 t.Errorf("the created file is %q, want the user's path in it", data)
279 }
280}
281
282func TestACommandLineReadsAsOne(t *testing.T) {
283 agent := acp.Agent{Command: "docker", Args: []string{"agent", "serve", "acp", "a.yaml"}}
284 if got, want := agent.CommandLine(), "docker agent serve acp a.yaml"; got != want {
285 t.Errorf("CommandLine() = %q, want %q", got, want)
286 }
287 if got, want := (acp.Agent{Command: "solo"}).CommandLine(), "solo"; got != want {
288 t.Errorf("CommandLine() = %q, want %q", got, want)
289 }
290}