turbo-editors/turbo-corepublic Fork 0
main
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.

configrepo_test.go · 314 lines · 11.9 KBGo Blame HistoryRaw
📦 Turbo Core — files rewritten outside reload into their windows; the agent window wraps what you type and keeps a long paste aside as a token (#28); configrepo fetches a shared .turbo-<slug> from a forge URL (#25) fe23288 k33g 9h ago1package configrepo
2
3import (
4 "context"
5 "errors"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "reflect"
10 "strings"
11 "testing"
12
13 "rickub.com/turbo-editors/turbo-core/profile"
14)
15
16func TestParseReadsTheURLsTheForgesShow(t *testing.T) {
17 cases := []struct {
18 url string
19 want Source
20 }{
21 {"https://rickub.com/turbo-editors/configs/tree/main/golang-init",
22 Source{Host: "rickub.com", Owner: "turbo-editors", Repo: "configs", Ref: "main", Path: "golang-init"}},
23 {"https://github.com/acme/configs/tree/main/go/service",
24 Source{Host: "github.com", Owner: "acme", Repo: "configs", Ref: "main", Path: "go/service"}},
25 {"https://gitlab.com/acme/platform/configs/-/tree/main/go-service",
26 Source{Host: "gitlab.com", Owner: "acme/platform", Repo: "configs", Ref: "main", Path: "go-service"}},
27 {"https://codeberg.org/acme/configs/src/branch/main/go-service",
28 Source{Host: "codeberg.org", Owner: "acme", Repo: "configs", Ref: "main", Path: "go-service"}},
29 {"https://codeberg.org/acme/configs/src/tag/v1.0.0/go-service",
30 Source{Host: "codeberg.org", Owner: "acme", Repo: "configs", Ref: "v1.0.0", Path: "go-service"}},
31 {"https://github.com/acme/configs",
32 Source{Host: "github.com", Owner: "acme", Repo: "configs"}},
33 {"https://github.com/acme/configs.git",
34 Source{Host: "github.com", Owner: "acme", Repo: "configs"}},
35 {"https://github.com/acme/configs/tree/main",
36 Source{Host: "github.com", Owner: "acme", Repo: "configs", Ref: "main"}},
37 {"https://github.com/acme/configs/tree/main/go-service/",
38 Source{Host: "github.com", Owner: "acme", Repo: "configs", Ref: "main", Path: "go-service"}},
39 {"https://github.com/acme/configs/blob/main/go-service/.turbo-go/settings.toml",
40 Source{Host: "github.com", Owner: "acme", Repo: "configs", Ref: "main", Path: "go-service/.turbo-go/settings.toml"}},
41 {" https://rickub.com/turbo-editors/configs/tree/main/golang-init-with-agents\n",
42 Source{Host: "rickub.com", Owner: "turbo-editors", Repo: "configs", Ref: "main", Path: "golang-init-with-agents"}},
43 }
44 for _, c := range cases {
45 got, err := Parse(c.url)
46 if err != nil {
47 t.Errorf("Parse(%q) error = %v", c.url, err)
48 continue
49 }
50 if got != c.want {
51 t.Errorf("Parse(%q) = %+v, want %+v", c.url, got, c.want)
52 }
53 }
54}
55
56func TestParseRefusesWhatIsNotARepository(t *testing.T) {
57 for _, bad := range []string{"", "configs", "https://rickub.com", "https://rickub.com/turbo-editors", "ftp://x/y/z", "not a url at all ://"} {
58 if _, err := Parse(bad); err == nil {
59 t.Errorf("Parse(%q) accepted something that names no repository", bad)
60 }
61 }
62}
63
64func TestCloneURLsTryTheHostThenItsGitSubdomain(t *testing.T) {
65 // rickub's pages are on rickub.com and its repositories on git.rickub.com.
66 src := Source{Host: "rickub.com", Owner: "turbo-editors", Repo: "configs"}
67
68 want := []string{"https://rickub.com/turbo-editors/configs.git", "https://git.rickub.com/turbo-editors/configs.git"}
69 if got := src.CloneURLs(); !reflect.DeepEqual(got, want) {
70 t.Errorf("CloneURLs() = %v, want %v", got, want)
71 }
72}
73
74func TestResolveRefSettlesABranchWithASlashInIt(t *testing.T) {
75 refs := []string{"main", "feature/x", "feature/x/deeper", "v1.0.0"}
76 cases := []struct {
77 ref, path, wantRef, wantPath string
78 }{
79 {"main", "golang-init", "main", "golang-init"},
80 {"feature", "x/golang-init", "feature/x", "golang-init"},
81 {"feature", "x/deeper/golang-init", "feature/x/deeper", "golang-init"},
82 {"feature", "x", "feature/x", ""},
83 {"v1.0.0", "", "v1.0.0", ""},
84 {"nowhere", "golang-init", "nowhere", "golang-init"}, // left alone; the clone will say
85 {"", "", "", ""}, // the default branch
86 }
87 for _, c := range cases {
88 ref, path := resolveRef(refs, c.ref, c.path)
89 if ref != c.wantRef || path != c.wantPath {
90 t.Errorf("resolveRef(%q, %q) = %q, %q; want %q, %q", c.ref, c.path, ref, path, c.wantRef, c.wantPath)
91 }
92 }
93}
94
95func TestParseRefsReadsBranchesAndTagsAndDropsPeeledTags(t *testing.T) {
96 output := "abc\tHEAD\nabc\trefs/heads/main\ndef\trefs/heads/feature/x\n123\trefs/tags/v1.0.0\n456\trefs/tags/v1.0.0^{}\n"
97
98 want := []string{"main", "feature/x", "v1.0.0"}
99 if got := parseRefs(output); !reflect.DeepEqual(got, want) {
100 t.Errorf("parseRefs() = %v, want %v", got, want)
101 }
102}
103
104// testProfile is an editor whose project directory is .turbo-test.
105func testProfile() profile.Profile {
106 return profile.Profile{Name: "Turbo Test", Slug: "turbo-test", Language: "Test"}
107}
108
109// sampleRepo makes a repository on disk shaped like turbo-editors/configs:
110// golang-init/.turbo-test with three files and a nested one, on a branch
111// called main, and the same on a branch called feature/x with a marker file.
112func sampleRepo(t *testing.T) string {
113 t.Helper()
114 if _, err := exec.LookPath("git"); err != nil {
115 t.Skip("git is not installed")
116 }
117
118 repo := t.TempDir()
119 run := func(args ...string) {
120 t.Helper()
121 cmd := exec.Command("git", append([]string{"-C", repo, "-c", "user.name=t", "-c", "user.email=t@example.com", "-c", "commit.gpgsign=false"}, args...)...)
122 if out, err := cmd.CombinedOutput(); err != nil {
123 t.Fatalf("git %v: %v\n%s", args, err, out)
124 }
125 }
126 write := func(name, contents string) {
127 t.Helper()
128 path := filepath.Join(repo, filepath.FromSlash(name))
129 if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
130 t.Fatal(err)
131 }
132 if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
133 t.Fatal(err)
134 }
135 }
136
137 run("init", "--quiet", "--initial-branch=main")
138 write("README.md", "# configs\n")
139 write("golang-init/README.md", "# golang-init\n")
140 write("golang-init/.turbo-test/settings.toml", "theme = \"turbo-dark\"\n")
141 write("golang-init/.turbo-test/snippets.toml", "[[snippet]]\n")
142 write("golang-init/.turbo-test/tools.toml", "[[tool]]\n")
143 write("golang-init/.turbo-test/agents/bob.yaml", "name: bob\n")
144 write("empty-init/README.md", "nothing for the editor here\n")
145 run("add", "-A")
146 run("commit", "--quiet", "-m", "sample")
147 run("checkout", "--quiet", "-b", "feature/x")
148 write("golang-init/.turbo-test/feature.txt", "from the branch\n")
149 run("add", "-A")
150 run("commit", "--quiet", "-m", "branch")
151 run("checkout", "--quiet", "main")
152 return repo
153}
154
155func TestLoadCopiesTheEditorsDirectoryIntoTheProject(t *testing.T) {
156 repo := sampleRepo(t)
157 dest := t.TempDir()
158
159 result, err := loadFrom(context.Background(), testProfile(), []string{repo}, Source{Ref: "main", Path: "golang-init"}, dest)
160 if err != nil {
161 t.Fatalf("loadFrom() error = %v", err)
162 }
163
164 if result.Dir != filepath.Join(dest, ".turbo-test") || result.Remote != repo || result.Ref != "main" {
165 t.Errorf("Result = %+v", result)
166 }
167 want := []string{"agents/bob.yaml", "settings.toml", "snippets.toml", "tools.toml"}
168 if !reflect.DeepEqual(result.Files, want) {
169 t.Errorf("Files = %v, want %v", result.Files, want)
170 }
171 data, err := os.ReadFile(filepath.Join(dest, ".turbo-test", "settings.toml"))
172 if err != nil || string(data) != "theme = \"turbo-dark\"\n" {
173 t.Errorf("settings.toml = %q, %v", data, err)
174 }
175 if _, err := os.Stat(filepath.Join(dest, "README.md")); err == nil {
176 t.Error("the directory's README came along; only the editor's directory should")
177 }
178}
179
180func TestLoadTakesAURLThatAlreadyNamesTheEditorsDirectory(t *testing.T) {
181 repo := sampleRepo(t)
182 dest := t.TempDir()
183
184 result, err := loadFrom(context.Background(), testProfile(), []string{repo}, Source{Ref: "main", Path: "golang-init/.turbo-test"}, dest)
185 if err != nil {
186 t.Fatalf("loadFrom() error = %v", err)
187 }
188 if len(result.Files) != 4 {
189 t.Errorf("Files = %v, want the four files", result.Files)
190 }
191}
192
193func TestLoadResolvesABranchWithASlashAgainstTheRepository(t *testing.T) {
194 // The URL .../tree/feature/x/golang-init reads as ref "feature", path
195 // "x/golang-init"; only the repository knows the branch is feature/x.
196 repo := sampleRepo(t)
197 dest := t.TempDir()
198
199 result, err := loadFrom(context.Background(), testProfile(), []string{repo}, Source{Ref: "feature", Path: "x/golang-init"}, dest)
200 if err != nil {
201 t.Fatalf("loadFrom() error = %v", err)
202 }
203 if result.Ref != "feature/x" {
204 t.Errorf("Ref = %q, want feature/x", result.Ref)
205 }
206 if _, err := os.Stat(filepath.Join(dest, ".turbo-test", "feature.txt")); err != nil {
207 t.Error("the file only the branch has did not come: the wrong ref was cloned")
208 }
209}
210
211func TestLoadUsesTheDefaultBranchWhenTheURLNamesNone(t *testing.T) {
212 repo := sampleRepo(t)
213 dest := t.TempDir()
214
215 result, err := loadFrom(context.Background(), testProfile(), []string{repo}, Source{Path: "golang-init"}, dest)
216 if err != nil {
217 t.Fatalf("loadFrom() error = %v", err)
218 }
219 if result.Ref != "" {
220 t.Errorf("Ref = %q, want the default branch (empty)", result.Ref)
221 }
222 if _, err := os.Stat(filepath.Join(dest, ".turbo-test", "feature.txt")); err == nil {
223 t.Error("a file from the feature branch came from the default branch")
224 }
225}
226
227func TestLoadRefusesToOverwriteAConfigurationTheProjectHas(t *testing.T) {
228 repo := sampleRepo(t)
229 dest := t.TempDir()
230 if err := os.MkdirAll(filepath.Join(dest, ".turbo-test"), 0o755); err != nil {
231 t.Fatal(err)
232 }
233 kept := filepath.Join(dest, ".turbo-test", "settings.toml")
234 if err := os.WriteFile(kept, []byte("mine\n"), 0o644); err != nil {
235 t.Fatal(err)
236 }
237
238 _, err := loadFrom(context.Background(), testProfile(), []string{repo}, Source{Ref: "main", Path: "golang-init"}, dest)
239
240 if !errors.Is(err, ErrExists) {
241 t.Fatalf("error = %v, want ErrExists", err)
242 }
243 if data, _ := os.ReadFile(kept); string(data) != "mine\n" {
244 t.Errorf("the project's own settings were overwritten: %q", data)
245 }
246}
247
248func TestLoadSaysWhenTheDirectoryHasNoConfiguration(t *testing.T) {
249 repo := sampleRepo(t)
250
251 _, err := loadFrom(context.Background(), testProfile(), []string{repo}, Source{Ref: "main", Path: "empty-init"}, t.TempDir())
252
253 if !errors.Is(err, ErrNotFound) || !strings.Contains(err.Error(), ".turbo-test at empty-init") {
254 t.Errorf("error = %v, want ErrNotFound naming the directory and the path", err)
255 }
256}
257
258func TestLoadReportsABranchThatDoesNotExistInGitsWords(t *testing.T) {
259 repo := sampleRepo(t)
260
261 _, err := loadFrom(context.Background(), testProfile(), []string{repo}, Source{Ref: "nowhere", Path: "golang-init"}, t.TempDir())
262
263 if err == nil || !strings.Contains(err.Error(), "nowhere") {
264 t.Errorf("error = %v, want git's complaint about the branch", err)
265 }
266}
267
268func TestLoadFallsThroughToTheRemoteThatAnswers(t *testing.T) {
269 // rickub.com does not serve git; git.rickub.com does. The first candidate
270 // failing must cost a round trip, not the load.
271 repo := sampleRepo(t)
272 dest := t.TempDir()
273
274 result, err := loadFrom(context.Background(), testProfile(), []string{filepath.Join(t.TempDir(), "nowhere.git"), repo}, Source{Ref: "main", Path: "golang-init"}, dest)
275 if err != nil {
276 t.Fatalf("loadFrom() error = %v", err)
277 }
278 if result.Remote != repo {
279 t.Errorf("Remote = %q, want the one that answered", result.Remote)
280 }
281}
282
283func TestLoadNamesEveryRemoteWhenNoneAnswers(t *testing.T) {
284 sampleRepo(t) // for the git skip
285
286 _, err := loadFrom(context.Background(), testProfile(), []string{filepath.Join(t.TempDir(), "a.git"), filepath.Join(t.TempDir(), "b.git")}, Source{}, t.TempDir())
287
288 if err == nil || !strings.Contains(err.Error(), "a.git") || !strings.Contains(err.Error(), "b.git") {
289 t.Errorf("error = %v, want both remotes named", err)
290 }
291}
292
293func TestLoadFromRickubOverTheNetwork(t *testing.T) {
294 // The real thing, against the repository the feature was asked for. Off by
295 // default: the suite must pass on a machine with no network.
296 if os.Getenv("TURBO_CORE_NETWORK") == "" {
297 t.Skip("TURBO_CORE_NETWORK is not set")
298 }
299 dest := t.TempDir()
300 p := profile.Profile{Name: "Turbo Go", Slug: "turbo-go", Language: "Go"}
301
302 result, err := Load(context.Background(), p, "https://rickub.com/turbo-editors/configs/tree/main/golang-init", dest)
303 if err != nil {
304 t.Fatalf("Load() error = %v", err)
305 }
306 if result.Remote != "https://git.rickub.com/turbo-editors/configs.git" {
307 t.Errorf("Remote = %q", result.Remote)
308 }
309 for _, name := range []string{"settings.toml", "snippets.toml", "tools.toml"} {
310 if _, err := os.Stat(filepath.Join(dest, ".turbo-go", name)); err != nil {
311 t.Errorf("%s did not arrive: %v", name, err)
312 }
313 }
314}