1
2
3
4
5 package vcs
6
7 import (
8 "errors"
9 "fmt"
10 "internal/testenv"
11 "os"
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 "bitbucket.org/workspace/pkgname",
188 &RepoRoot{
189 VCS: vcsGit,
190 Repo: "https://bitbucket.org/workspace/pkgname",
191 },
192 },
193 }
194
195 for _, test := range tests {
196 got, err := RepoRootForImportPath(test.path, IgnoreMod, web.SecureOnly)
197 want := test.want
198
199 if want == nil {
200 if err == nil {
201 t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path)
202 }
203 continue
204 }
205 if err != nil {
206 t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
207 continue
208 }
209 if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo {
210 t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo)
211 }
212 }
213 }
214
215
216
217 func TestFromDir(t *testing.T) {
218 tempDir, err := os.MkdirTemp("", "vcstest")
219 if err != nil {
220 t.Fatal(err)
221 }
222 defer os.RemoveAll(tempDir)
223
224 for j, vcs := range vcsList {
225 for r, rootName := range vcs.RootNames {
226 vcsName := fmt.Sprint(vcs.Name, r)
227 dir := filepath.Join(tempDir, "example.com", vcsName, rootName)
228 if j&1 == 0 {
229 err := os.MkdirAll(dir, 0755)
230 if err != nil {
231 t.Fatal(err)
232 }
233 } else {
234 err := os.MkdirAll(filepath.Dir(dir), 0755)
235 if err != nil {
236 t.Fatal(err)
237 }
238 f, err := os.Create(dir)
239 if err != nil {
240 t.Fatal(err)
241 }
242 f.Close()
243 }
244
245 wantRepoDir := filepath.Dir(dir)
246 gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false)
247 if err != nil {
248 t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
249 continue
250 }
251 if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name {
252 t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name)
253 }
254 }
255 }
256 }
257
258 func TestIsSecure(t *testing.T) {
259 tests := []struct {
260 vcs *Cmd
261 url string
262 secure bool
263 }{
264 {vcsGit, "http://example.com/foo.git", false},
265 {vcsGit, "https://example.com/foo.git", true},
266 {vcsBzr, "http://example.com/foo.bzr", false},
267 {vcsBzr, "https://example.com/foo.bzr", true},
268 {vcsSvn, "http://example.com/svn", false},
269 {vcsSvn, "https://example.com/svn", true},
270 {vcsHg, "http://example.com/foo.hg", false},
271 {vcsHg, "https://example.com/foo.hg", true},
272 {vcsGit, "ssh://user@example.com/foo.git", true},
273 {vcsGit, "user@server:path/to/repo.git", false},
274 {vcsGit, "user@server:", false},
275 {vcsGit, "server:repo.git", false},
276 {vcsGit, "server:path/to/repo.git", false},
277 {vcsGit, "example.com:path/to/repo.git", false},
278 {vcsGit, "path/that/contains/a:colon/repo.git", false},
279 {vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
280 {vcsFossil, "http://example.com/foo", false},
281 {vcsFossil, "https://example.com/foo", true},
282 }
283
284 for _, test := range tests {
285 secure := test.vcs.IsSecure(test.url)
286 if secure != test.secure {
287 t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
288 }
289 }
290 }
291
292 func TestIsSecureGitAllowProtocol(t *testing.T) {
293 tests := []struct {
294 vcs *Cmd
295 url string
296 secure bool
297 }{
298
299 {vcsGit, "http://example.com/foo.git", false},
300 {vcsGit, "https://example.com/foo.git", true},
301 {vcsBzr, "http://example.com/foo.bzr", false},
302 {vcsBzr, "https://example.com/foo.bzr", true},
303 {vcsSvn, "http://example.com/svn", false},
304 {vcsSvn, "https://example.com/svn", true},
305 {vcsHg, "http://example.com/foo.hg", false},
306 {vcsHg, "https://example.com/foo.hg", true},
307 {vcsGit, "user@server:path/to/repo.git", false},
308 {vcsGit, "user@server:", false},
309 {vcsGit, "server:repo.git", false},
310 {vcsGit, "server:path/to/repo.git", false},
311 {vcsGit, "example.com:path/to/repo.git", false},
312 {vcsGit, "path/that/contains/a:colon/repo.git", false},
313 {vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
314
315 {vcsGit, "ssh://user@example.com/foo.git", false},
316 {vcsGit, "foo://example.com/bar.git", true},
317 {vcsHg, "foo://example.com/bar.hg", false},
318 {vcsSvn, "foo://example.com/svn", false},
319 {vcsBzr, "foo://example.com/bar.bzr", false},
320 }
321
322 defer os.Unsetenv("GIT_ALLOW_PROTOCOL")
323 os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo")
324 for _, test := range tests {
325 secure := test.vcs.IsSecure(test.url)
326 if secure != test.secure {
327 t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
328 }
329 }
330 }
331
332 func TestMatchGoImport(t *testing.T) {
333 tests := []struct {
334 imports []metaImport
335 path string
336 mi metaImport
337 err error
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 },
350 path: "example.com/user/foo/",
351 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
352 },
353 {
354 imports: []metaImport{
355 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
356 {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
357 },
358 path: "example.com/user/foo",
359 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
360 },
361 {
362 imports: []metaImport{
363 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
364 {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
365 },
366 path: "example.com/user/fooa",
367 mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
368 },
369 {
370 imports: []metaImport{
371 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
372 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
373 },
374 path: "example.com/user/foo/bar",
375 err: errors.New("should not be allowed to create nested repo"),
376 },
377 {
378 imports: []metaImport{
379 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
380 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
381 },
382 path: "example.com/user/foo/bar/baz",
383 err: errors.New("should not be allowed to create nested repo"),
384 },
385 {
386 imports: []metaImport{
387 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
388 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
389 },
390 path: "example.com/user/foo/bar/baz/qux",
391 err: errors.New("should not be allowed to create nested repo"),
392 },
393 {
394 imports: []metaImport{
395 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
396 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
397 },
398 path: "example.com/user/foo/bar/baz/",
399 err: errors.New("should not be allowed to create nested repo"),
400 },
401 {
402 imports: []metaImport{
403 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
404 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
405 },
406 path: "example.com",
407 err: errors.New("pathologically short path"),
408 },
409 {
410 imports: []metaImport{
411 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
412 },
413 path: "different.example.com/user/foo",
414 err: errors.New("meta tags do not match import path"),
415 },
416 {
417 imports: []metaImport{
418 {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
419 {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
420 },
421 path: "myitcv.io/blah2/foo",
422 mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
423 },
424 {
425 imports: []metaImport{
426 {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
427 {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
428 },
429 path: "myitcv.io/other",
430 mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
431 },
432 }
433
434 for _, test := range tests {
435 mi, err := matchGoImport(test.imports, test.path)
436 if mi != test.mi {
437 t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi)
438 }
439
440 got := err
441 want := test.err
442 if (got == nil) != (want == nil) {
443 t.Errorf("unexpected error; got %v, want %v", got, want)
444 }
445 }
446 }
447
448 func TestValidateRepoRoot(t *testing.T) {
449 tests := []struct {
450 root string
451 ok bool
452 }{
453 {
454 root: "",
455 ok: false,
456 },
457 {
458 root: "http://",
459 ok: true,
460 },
461 {
462 root: "git+ssh://",
463 ok: true,
464 },
465 {
466 root: "http#://",
467 ok: false,
468 },
469 {
470 root: "-config",
471 ok: false,
472 },
473 {
474 root: "-config://",
475 ok: false,
476 },
477 }
478
479 for _, test := range tests {
480 err := validateRepoRoot(test.root)
481 ok := err == nil
482 if ok != test.ok {
483 want := "error"
484 if test.ok {
485 want = "nil"
486 }
487 t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want)
488 }
489 }
490 }
491
492 var govcsTests = []struct {
493 govcs string
494 path string
495 vcs string
496 ok bool
497 }{
498 {"private:all", "is-public.com/foo", "zzz", false},
499 {"private:all", "is-private.com/foo", "zzz", true},
500 {"public:all", "is-public.com/foo", "zzz", true},
501 {"public:all", "is-private.com/foo", "zzz", false},
502 {"public:all,private:none", "is-public.com/foo", "zzz", true},
503 {"public:all,private:none", "is-private.com/foo", "zzz", false},
504 {"*:all", "is-public.com/foo", "zzz", true},
505 {"golang.org:git", "golang.org/x/text", "zzz", false},
506 {"golang.org:git", "golang.org/x/text", "git", true},
507 {"golang.org:zzz", "golang.org/x/text", "zzz", true},
508 {"golang.org:zzz", "golang.org/x/text", "git", false},
509 {"golang.org:zzz", "golang.org/x/text", "zzz", true},
510 {"golang.org:zzz", "golang.org/x/text", "git", false},
511 {"golang.org:git|hg", "golang.org/x/text", "hg", true},
512 {"golang.org:git|hg", "golang.org/x/text", "git", true},
513 {"golang.org:git|hg", "golang.org/x/text", "zzz", false},
514 {"golang.org:all", "golang.org/x/text", "hg", true},
515 {"golang.org:all", "golang.org/x/text", "git", true},
516 {"golang.org:all", "golang.org/x/text", "zzz", true},
517 {"other.xyz/p:none,golang.org/x:git", "other.xyz/p/x", "git", false},
518 {"other.xyz/p:none,golang.org/x:git", "unexpected.com", "git", false},
519 {"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "zzz", false},
520 {"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "git", true},
521 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "zzz", true},
522 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "git", false},
523 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "hg", true},
524 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "git", true},
525 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "zzz", false},
526 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "hg", true},
527 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "git", true},
528 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "zzz", true},
529 {"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "zzz", false},
530 {"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "git", false},
531 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "zzz", false},
532 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "git", false},
533 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "hg", false},
534 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "git", false},
535 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "zzz", false},
536 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "hg", false},
537 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "git", false},
538 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "zzz", false},
539 }
540
541 func TestGOVCS(t *testing.T) {
542 for _, tt := range govcsTests {
543 cfg, err := parseGOVCS(tt.govcs)
544 if err != nil {
545 t.Errorf("parseGOVCS(%q): %v", tt.govcs, err)
546 continue
547 }
548 private := strings.HasPrefix(tt.path, "is-private")
549 ok := cfg.allow(tt.path, private, tt.vcs)
550 if ok != tt.ok {
551 t.Errorf("parseGOVCS(%q).allow(%q, %v, %q) = %v, want %v",
552 tt.govcs, tt.path, private, tt.vcs, ok, tt.ok)
553 }
554 }
555 }
556
557 var govcsErrors = []struct {
558 s string
559 err string
560 }{
561 {`,`, `empty entry in GOVCS`},
562 {`,x`, `empty entry in GOVCS`},
563 {`x,`, `malformed entry in GOVCS (missing colon): "x"`},
564 {`x:y,`, `empty entry in GOVCS`},
565 {`x`, `malformed entry in GOVCS (missing colon): "x"`},
566 {`x:`, `empty VCS list in GOVCS: "x:"`},
567 {`x:|`, `empty VCS name in GOVCS: "x:|"`},
568 {`x:y|`, `empty VCS name in GOVCS: "x:y|"`},
569 {`x:|y`, `empty VCS name in GOVCS: "x:|y"`},
570 {`x:y,z:`, `empty VCS list in GOVCS: "z:"`},
571 {`x:y,z:|`, `empty VCS name in GOVCS: "z:|"`},
572 {`x:y,z:|w`, `empty VCS name in GOVCS: "z:|w"`},
573 {`x:y,z:w|`, `empty VCS name in GOVCS: "z:w|"`},
574 {`x:y,z:w||v`, `empty VCS name in GOVCS: "z:w||v"`},
575 {`x:y,x:z`, `unreachable pattern in GOVCS: "x:z" after "x:y"`},
576 }
577
578 func TestGOVCSErrors(t *testing.T) {
579 for _, tt := range govcsErrors {
580 _, err := parseGOVCS(tt.s)
581 if err == nil || !strings.Contains(err.Error(), tt.err) {
582 t.Errorf("parseGOVCS(%s): err=%v, want %v", tt.s, err, tt.err)
583 }
584 }
585 }
586
View as plain text