1
2
3
4
5 package vcs
6
7 import (
8 "errors"
9 "internal/testenv"
10 "os"
11 "path"
12 "path/filepath"
13 "strings"
14 "testing"
15
16 "cmd/go/internal/web"
17 )
18
19 func init() {
20
21
22
23
24 os.Setenv("GOVCS", "*:all")
25 }
26
27
28
29 func TestRepoRootForImportPath(t *testing.T) {
30 testenv.MustHaveExternalNetwork(t)
31
32 tests := []struct {
33 path string
34 want *RepoRoot
35 }{
36 {
37 "github.com/golang/groupcache",
38 &RepoRoot{
39 VCS: vcsGit,
40 Repo: "https://github.com/golang/groupcache",
41 },
42 },
43
44 {
45 "github.com/user/unicode/испытание",
46 nil,
47 },
48
49 {
50 "hub.jazz.net/git/user1/pkgname",
51 &RepoRoot{
52 VCS: vcsGit,
53 Repo: "https://hub.jazz.net/git/user1/pkgname",
54 },
55 },
56 {
57 "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
58 &RepoRoot{
59 VCS: vcsGit,
60 Repo: "https://hub.jazz.net/git/user1/pkgname",
61 },
62 },
63 {
64 "hub.jazz.net",
65 nil,
66 },
67 {
68 "hubajazz.net",
69 nil,
70 },
71 {
72 "hub2.jazz.net",
73 nil,
74 },
75 {
76 "hub.jazz.net/someotherprefix",
77 nil,
78 },
79 {
80 "hub.jazz.net/someotherprefix/user1/pkgname",
81 nil,
82 },
83
84 {
85 "hub.jazz.net/git/User 1/pkgname",
86 nil,
87 },
88 {
89 "hub.jazz.net/git/user1/pkg name",
90 nil,
91 },
92
93 {
94 "hub.jazz.net/git/user.1/pkgname",
95 nil,
96 },
97 {
98 "hub.jazz.net/git/user/pkg.name",
99 &RepoRoot{
100 VCS: vcsGit,
101 Repo: "https://hub.jazz.net/git/user/pkg.name",
102 },
103 },
104
105 {
106 "hub.jazz.net/git/USER/pkgname",
107 nil,
108 },
109
110 {
111 "git.openstack.org/openstack/swift",
112 &RepoRoot{
113 VCS: vcsGit,
114 Repo: "https://git.openstack.org/openstack/swift",
115 },
116 },
117
118
119
120 {
121 "git.openstack.org/openstack/swift.git",
122 &RepoRoot{
123 VCS: vcsGit,
124 Repo: "https://git.openstack.org/openstack/swift.git",
125 },
126 },
127 {
128 "git.openstack.org/openstack/swift/go/hummingbird",
129 &RepoRoot{
130 VCS: vcsGit,
131 Repo: "https://git.openstack.org/openstack/swift",
132 },
133 },
134 {
135 "git.openstack.org",
136 nil,
137 },
138 {
139 "git.openstack.org/openstack",
140 nil,
141 },
142
143 {
144 "git.apache.org/package name/path/to/lib",
145 nil,
146 },
147
148 {
149 "git.apache.org/package-name/path/to/lib",
150 nil,
151 },
152 {
153 "gitbapache.org",
154 nil,
155 },
156 {
157 "git.apache.org/package-name.git",
158 &RepoRoot{
159 VCS: vcsGit,
160 Repo: "https://git.apache.org/package-name.git",
161 },
162 },
163 {
164 "git.apache.org/package-name_2.x.git/path/to/lib",
165 &RepoRoot{
166 VCS: vcsGit,
167 Repo: "https://git.apache.org/package-name_2.x.git",
168 },
169 },
170 {
171 "chiselapp.com/user/kyle/repository/fossilgg",
172 &RepoRoot{
173 VCS: vcsFossil,
174 Repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
175 },
176 },
177 {
178
179 "chiselapp.com/kyle/repository/fossilgg",
180 nil,
181 },
182 {
183 "chiselapp.com/user/kyle/fossilgg",
184 nil,
185 },
186 }
187
188 for _, test := range tests {
189 got, err := RepoRootForImportPath(test.path, IgnoreMod, web.SecureOnly)
190 want := test.want
191
192 if want == nil {
193 if err == nil {
194 t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path)
195 }
196 continue
197 }
198 if err != nil {
199 t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
200 continue
201 }
202 if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo {
203 t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo)
204 }
205 }
206 }
207
208
209 func TestFromDir(t *testing.T) {
210 tempDir, err := os.MkdirTemp("", "vcstest")
211 if err != nil {
212 t.Fatal(err)
213 }
214 defer os.RemoveAll(tempDir)
215
216 for j, vcs := range vcsList {
217 dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd)
218 if j&1 == 0 {
219 err := os.MkdirAll(dir, 0755)
220 if err != nil {
221 t.Fatal(err)
222 }
223 } else {
224 err := os.MkdirAll(filepath.Dir(dir), 0755)
225 if err != nil {
226 t.Fatal(err)
227 }
228 f, err := os.Create(dir)
229 if err != nil {
230 t.Fatal(err)
231 }
232 f.Close()
233 }
234
235 want := RepoRoot{
236 VCS: vcs,
237 Root: path.Join("example.com", vcs.Name),
238 }
239 var got RepoRoot
240 got.VCS, got.Root, err = FromDir(dir, tempDir)
241 if err != nil {
242 t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
243 continue
244 }
245 if got.VCS.Name != want.VCS.Name || got.Root != want.Root {
246 t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root)
247 }
248 }
249 }
250
251 func TestIsSecure(t *testing.T) {
252 tests := []struct {
253 vcs *Cmd
254 url string
255 secure bool
256 }{
257 {vcsGit, "http://example.com/foo.git", false},
258 {vcsGit, "https://example.com/foo.git", true},
259 {vcsBzr, "http://example.com/foo.bzr", false},
260 {vcsBzr, "https://example.com/foo.bzr", true},
261 {vcsSvn, "http://example.com/svn", false},
262 {vcsSvn, "https://example.com/svn", true},
263 {vcsHg, "http://example.com/foo.hg", false},
264 {vcsHg, "https://example.com/foo.hg", true},
265 {vcsGit, "ssh://user@example.com/foo.git", true},
266 {vcsGit, "user@server:path/to/repo.git", false},
267 {vcsGit, "user@server:", false},
268 {vcsGit, "server:repo.git", false},
269 {vcsGit, "server:path/to/repo.git", false},
270 {vcsGit, "example.com:path/to/repo.git", false},
271 {vcsGit, "path/that/contains/a:colon/repo.git", false},
272 {vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
273 {vcsFossil, "http://example.com/foo", false},
274 {vcsFossil, "https://example.com/foo", true},
275 }
276
277 for _, test := range tests {
278 secure := test.vcs.IsSecure(test.url)
279 if secure != test.secure {
280 t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
281 }
282 }
283 }
284
285 func TestIsSecureGitAllowProtocol(t *testing.T) {
286 tests := []struct {
287 vcs *Cmd
288 url string
289 secure bool
290 }{
291
292 {vcsGit, "http://example.com/foo.git", false},
293 {vcsGit, "https://example.com/foo.git", true},
294 {vcsBzr, "http://example.com/foo.bzr", false},
295 {vcsBzr, "https://example.com/foo.bzr", true},
296 {vcsSvn, "http://example.com/svn", false},
297 {vcsSvn, "https://example.com/svn", true},
298 {vcsHg, "http://example.com/foo.hg", false},
299 {vcsHg, "https://example.com/foo.hg", true},
300 {vcsGit, "user@server:path/to/repo.git", false},
301 {vcsGit, "user@server:", false},
302 {vcsGit, "server:repo.git", false},
303 {vcsGit, "server:path/to/repo.git", false},
304 {vcsGit, "example.com:path/to/repo.git", false},
305 {vcsGit, "path/that/contains/a:colon/repo.git", false},
306 {vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
307
308 {vcsGit, "ssh://user@example.com/foo.git", false},
309 {vcsGit, "foo://example.com/bar.git", true},
310 {vcsHg, "foo://example.com/bar.hg", false},
311 {vcsSvn, "foo://example.com/svn", false},
312 {vcsBzr, "foo://example.com/bar.bzr", false},
313 }
314
315 defer os.Unsetenv("GIT_ALLOW_PROTOCOL")
316 os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo")
317 for _, test := range tests {
318 secure := test.vcs.IsSecure(test.url)
319 if secure != test.secure {
320 t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
321 }
322 }
323 }
324
325 func TestMatchGoImport(t *testing.T) {
326 tests := []struct {
327 imports []metaImport
328 path string
329 mi metaImport
330 err error
331 }{
332 {
333 imports: []metaImport{
334 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
335 },
336 path: "example.com/user/foo",
337 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
338 },
339 {
340 imports: []metaImport{
341 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
342 },
343 path: "example.com/user/foo/",
344 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
345 },
346 {
347 imports: []metaImport{
348 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
349 {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
350 },
351 path: "example.com/user/foo",
352 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
353 },
354 {
355 imports: []metaImport{
356 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
357 {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
358 },
359 path: "example.com/user/fooa",
360 mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
361 },
362 {
363 imports: []metaImport{
364 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
365 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
366 },
367 path: "example.com/user/foo/bar",
368 err: errors.New("should not be allowed to create nested repo"),
369 },
370 {
371 imports: []metaImport{
372 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
373 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
374 },
375 path: "example.com/user/foo/bar/baz",
376 err: errors.New("should not be allowed to create nested repo"),
377 },
378 {
379 imports: []metaImport{
380 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
381 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
382 },
383 path: "example.com/user/foo/bar/baz/qux",
384 err: errors.New("should not be allowed to create nested repo"),
385 },
386 {
387 imports: []metaImport{
388 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
389 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
390 },
391 path: "example.com/user/foo/bar/baz/",
392 err: errors.New("should not be allowed to create nested repo"),
393 },
394 {
395 imports: []metaImport{
396 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
397 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
398 },
399 path: "example.com",
400 err: errors.New("pathologically short path"),
401 },
402 {
403 imports: []metaImport{
404 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
405 },
406 path: "different.example.com/user/foo",
407 err: errors.New("meta tags do not match import path"),
408 },
409 {
410 imports: []metaImport{
411 {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
412 {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
413 },
414 path: "myitcv.io/blah2/foo",
415 mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
416 },
417 {
418 imports: []metaImport{
419 {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
420 {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
421 },
422 path: "myitcv.io/other",
423 mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
424 },
425 }
426
427 for _, test := range tests {
428 mi, err := matchGoImport(test.imports, test.path)
429 if mi != test.mi {
430 t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi)
431 }
432
433 got := err
434 want := test.err
435 if (got == nil) != (want == nil) {
436 t.Errorf("unexpected error; got %v, want %v", got, want)
437 }
438 }
439 }
440
441 func TestValidateRepoRoot(t *testing.T) {
442 tests := []struct {
443 root string
444 ok bool
445 }{
446 {
447 root: "",
448 ok: false,
449 },
450 {
451 root: "http://",
452 ok: true,
453 },
454 {
455 root: "git+ssh://",
456 ok: true,
457 },
458 {
459 root: "http#://",
460 ok: false,
461 },
462 {
463 root: "-config",
464 ok: false,
465 },
466 {
467 root: "-config://",
468 ok: false,
469 },
470 }
471
472 for _, test := range tests {
473 err := validateRepoRoot(test.root)
474 ok := err == nil
475 if ok != test.ok {
476 want := "error"
477 if test.ok {
478 want = "nil"
479 }
480 t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want)
481 }
482 }
483 }
484
485 var govcsTests = []struct {
486 govcs string
487 path string
488 vcs string
489 ok bool
490 }{
491 {"private:all", "is-public.com/foo", "zzz", false},
492 {"private:all", "is-private.com/foo", "zzz", true},
493 {"public:all", "is-public.com/foo", "zzz", true},
494 {"public:all", "is-private.com/foo", "zzz", false},
495 {"public:all,private:none", "is-public.com/foo", "zzz", true},
496 {"public:all,private:none", "is-private.com/foo", "zzz", false},
497 {"*:all", "is-public.com/foo", "zzz", true},
498 {"golang.org:git", "golang.org/x/text", "zzz", false},
499 {"golang.org:git", "golang.org/x/text", "git", true},
500 {"golang.org:zzz", "golang.org/x/text", "zzz", true},
501 {"golang.org:zzz", "golang.org/x/text", "git", false},
502 {"golang.org:zzz", "golang.org/x/text", "zzz", true},
503 {"golang.org:zzz", "golang.org/x/text", "git", false},
504 {"golang.org:git|hg", "golang.org/x/text", "hg", true},
505 {"golang.org:git|hg", "golang.org/x/text", "git", true},
506 {"golang.org:git|hg", "golang.org/x/text", "zzz", false},
507 {"golang.org:all", "golang.org/x/text", "hg", true},
508 {"golang.org:all", "golang.org/x/text", "git", true},
509 {"golang.org:all", "golang.org/x/text", "zzz", true},
510 {"other.xyz/p:none,golang.org/x:git", "other.xyz/p/x", "git", false},
511 {"other.xyz/p:none,golang.org/x:git", "unexpected.com", "git", false},
512 {"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "zzz", false},
513 {"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "git", true},
514 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "zzz", true},
515 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "git", false},
516 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "hg", true},
517 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "git", true},
518 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "zzz", false},
519 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "hg", true},
520 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "git", true},
521 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "zzz", true},
522 {"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "zzz", false},
523 {"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "git", false},
524 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "zzz", false},
525 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "git", false},
526 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "hg", false},
527 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "git", false},
528 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "zzz", false},
529 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "hg", false},
530 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "git", false},
531 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "zzz", false},
532 }
533
534 func TestGOVCS(t *testing.T) {
535 for _, tt := range govcsTests {
536 cfg, err := parseGOVCS(tt.govcs)
537 if err != nil {
538 t.Errorf("parseGOVCS(%q): %v", tt.govcs, err)
539 continue
540 }
541 private := strings.HasPrefix(tt.path, "is-private")
542 ok := cfg.allow(tt.path, private, tt.vcs)
543 if ok != tt.ok {
544 t.Errorf("parseGOVCS(%q).allow(%q, %v, %q) = %v, want %v",
545 tt.govcs, tt.path, private, tt.vcs, ok, tt.ok)
546 }
547 }
548 }
549
550 var govcsErrors = []struct {
551 s string
552 err string
553 }{
554 {`,`, `empty entry in GOVCS`},
555 {`,x`, `empty entry in GOVCS`},
556 {`x,`, `malformed entry in GOVCS (missing colon): "x"`},
557 {`x:y,`, `empty entry in GOVCS`},
558 {`x`, `malformed entry in GOVCS (missing colon): "x"`},
559 {`x:`, `empty VCS list in GOVCS: "x:"`},
560 {`x:|`, `empty VCS name in GOVCS: "x:|"`},
561 {`x:y|`, `empty VCS name in GOVCS: "x:y|"`},
562 {`x:|y`, `empty VCS name in GOVCS: "x:|y"`},
563 {`x:y,z:`, `empty VCS list in GOVCS: "z:"`},
564 {`x:y,z:|`, `empty VCS name in GOVCS: "z:|"`},
565 {`x:y,z:|w`, `empty VCS name in GOVCS: "z:|w"`},
566 {`x:y,z:w|`, `empty VCS name in GOVCS: "z:w|"`},
567 {`x:y,z:w||v`, `empty VCS name in GOVCS: "z:w||v"`},
568 {`x:y,x:z`, `unreachable pattern in GOVCS: "x:z" after "x:y"`},
569 }
570
571 func TestGOVCSErrors(t *testing.T) {
572 for _, tt := range govcsErrors {
573 _, err := parseGOVCS(tt.s)
574 if err == nil || !strings.Contains(err.Error(), tt.err) {
575 t.Errorf("parseGOVCS(%s): err=%v, want %v", tt.s, err, tt.err)
576 }
577 }
578 }
579
View as plain text