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