e2e/teams_test.go
119 lines · 5208 bytes
1package e2e
2
3import (
4 "strings"
5 "testing"
6)
7
8func TestOrgTeams(t *testing.T) {
9 inst := startInstance(t)
10 adminKey := inst.newKey(t, "alice")
11 bobKey := inst.newKey(t, "bob")
12 carolKey := inst.newKey(t, "carol")
13 eveKey := inst.newKey(t, "eve")
14 inst.admin(t, "admin", "user", "create", "alice", "--key", adminKey+".pub")
15 inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
16 inst.admin(t, "admin", "user", "create", "carol", "--key", carolKey+".pub")
17 inst.admin(t, "admin", "user", "create", "eve", "--key", eveKey+".pub")
18
19 // Org with two private repos; bob and carol are plain members.
20 for _, args := range [][]string{
21 {"org", "create", "acme"},
22 {"org", "members", "add", "acme", "bob"},
23 {"org", "members", "add", "acme", "carol"},
24 {"repo", "create", "acme/core", "--private"},
25 {"repo", "create", "acme/site", "--private"},
26 } {
27 if _, errOut, code := inst.ssh(t, adminKey, "", args...); code != 0 {
28 t.Fatalf("%v: %s", args, errOut)
29 }
30 }
31
32 // Degenerate case: plain membership implies write everywhere.
33 if _, _, code := inst.ssh(t, bobKey, "", "issue", "create", "acme/core", "--title", "'pre'"); code != 0 {
34 t.Fatal("member write lost (degenerate case broken)")
35 }
36
37 // Scope the org: members get nothing by default, teams grant.
38 if _, _, code := inst.ssh(t, bobKey, "", "org", "settings", "members-role", "acme", "none"); code != 4 {
39 t.Fatal("non-admin changed members-role")
40 }
41 if _, _, code := inst.ssh(t, adminKey, "", "org", "settings", "members-role", "acme", "none"); code != 0 {
42 t.Fatal("members-role failed")
43 }
44 // bob now cannot even see the private repo.
45 if _, _, code := inst.ssh(t, bobKey, "", "repo", "show", "acme/core"); code != 3 {
46 t.Fatal("scoped member still sees private repo")
47 }
48
49 // Team "core-devs": bob gets write on core, read on site.
50 if _, _, code := inst.ssh(t, adminKey, "", "org", "team", "create", "acme", "core-devs"); code != 0 {
51 t.Fatal("team create failed")
52 }
53 if _, errOut, code := inst.ssh(t, adminKey, "", "org", "team", "add", "acme", "core-devs", "eve"); code != 2 || !strings.Contains(errOut, "not a member") {
54 t.Fatalf("non-member added to team: %d %s", code, errOut)
55 }
56 if _, _, code := inst.ssh(t, adminKey, "", "org", "team", "add", "acme", "core-devs", "bob"); code != 0 {
57 t.Fatal("team add failed")
58 }
59 if _, _, code := inst.ssh(t, adminKey, "", "org", "team", "grant", "acme", "core-devs", "acme/core", "write"); code != 0 {
60 t.Fatal("team grant failed")
61 }
62 if _, _, code := inst.ssh(t, adminKey, "", "org", "team", "grant", "acme", "core-devs", "acme/site", "read"); code != 0 {
63 t.Fatal("second grant failed")
64 }
65 // Grants are limited to the org's own repos.
66 if _, _, code := inst.ssh(t, adminKey, "", "repo", "create", "alice/own"); code != 0 {
67 t.Fatal("repo create failed")
68 }
69 if _, errOut, code := inst.ssh(t, adminKey, "", "org", "team", "grant", "acme", "core-devs", "alice/own", "read"); code != 2 || !strings.Contains(errOut, "own org") {
70 t.Fatalf("cross-org grant allowed: %d %s", code, errOut)
71 }
72
73 // bob: write on core (can open issues), read-only on site (visible,
74 // not writable). carol (no team): nothing.
75 if _, _, code := inst.ssh(t, bobKey, "", "issue", "create", "acme/core", "--title", "'works'"); code != 0 {
76 t.Fatal("team write not effective")
77 }
78 if _, _, code := inst.ssh(t, bobKey, "", "repo", "show", "acme/site"); code != 0 {
79 t.Fatal("team read not effective")
80 }
81 if _, _, code := inst.ssh(t, bobKey, "", "repo", "settings", "protect", "acme/site", "main"); code != 4 {
82 t.Fatal("read grant allowed admin action")
83 }
84 if _, _, code := inst.ssh(t, carolKey, "", "repo", "show", "acme/core"); code != 3 {
85 t.Fatal("teamless member sees scoped repo")
86 }
87 out, _, _ := inst.ssh(t, bobKey, "", "repo", "list")
88 if !strings.Contains(out, "acme/core") || !strings.Contains(out, "acme/site") {
89 t.Fatalf("team repos missing from listing: %s", out)
90 }
91
92 // show reflects members and grants; member removal drops access.
93 out, _, _ = inst.ssh(t, adminKey, "", "org", "team", "show", "acme", "core-devs", "--json")
94 if !strings.Contains(out, `"members":["bob"]`) || !strings.Contains(out, `"repo":"acme/core","role":"write"`) {
95 t.Fatalf("team show: %s", out)
96 }
97 if _, _, code := inst.ssh(t, adminKey, "", "org", "team", "remove", "acme", "core-devs", "bob"); code != 0 {
98 t.Fatal("team remove failed")
99 }
100 if _, _, code := inst.ssh(t, bobKey, "", "repo", "show", "acme/core"); code != 3 {
101 t.Fatal("removed member kept access")
102 }
103
104 // Deleting the team cascades its grants.
105 inst.ssh(t, adminKey, "", "org", "team", "add", "acme", "core-devs", "carol")
106 if _, _, code := inst.ssh(t, carolKey, "", "repo", "show", "acme/core"); code != 0 {
107 t.Fatal("carol team access missing")
108 }
109 if _, _, code := inst.ssh(t, adminKey, "", "org", "team", "delete", "acme", "core-devs"); code != 0 {
110 t.Fatal("team delete failed")
111 }
112 if _, _, code := inst.ssh(t, carolKey, "", "repo", "show", "acme/core"); code != 3 {
113 t.Fatal("deleted team's grant survived")
114 }
115 // Org admins keep admin regardless of scoping.
116 if _, _, code := inst.ssh(t, adminKey, "", "repo", "settings", "protect", "acme/core", "main"); code != 0 {
117 t.Fatal("org admin lost access")
118 }
119}