e2e/orglabels_test.go

3bcdce33fb9a2309312854331359d376171c7368
gitbay/e2e/orglabels_test.go history · blame · raw

119 lines · 5595 bytes

  1package e2e
  2
  3import (
  4	"os"
  5	"path/filepath"
  6	"strings"
  7	"testing"
  8)
  9
 10// An org's labels and milestones reach every repository under it; a
 11// commit in one repository closes an issue in another; the org pages
 12// answer members and outsiders as their access allows.
 13func TestOrgLabelsMilestonesAndCrossRepoCloses(t *testing.T) {
 14	t.Parallel()
 15	inst := startInstance(t)
 16	aliceKey := inst.newKey(t, "alice")
 17	carolKey := inst.newKey(t, "carol")
 18	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
 19	inst.admin(t, "admin", "user", "create", "carol", "--key", carolKey+".pub", "--email", "carol@example.test", "--verified")
 20	must := func(key string, args ...string) string {
 21		t.Helper()
 22		out, errOut, code := inst.ssh(t, key, "", args...)
 23		if code != 0 {
 24			t.Fatalf("%v: exit %d %s", args, code, errOut)
 25		}
 26		return out
 27	}
 28	must(aliceKey, "org", "create", "acme")
 29	must(aliceKey, "repo", "create", "acme/lib")
 30	must(aliceKey, "repo", "create", "acme/widget", "--private")
 31	must(aliceKey, "issue", "create", "acme/lib", "--title", "'lib one'")
 32	must(aliceKey, "issue", "create", "acme/widget", "--title", "'widget one'")
 33
 34	// Repo labels in both, then the org set folds them in.
 35	must(aliceKey, "issue", "label", "acme/lib", "1", "--add", "bug")
 36	must(aliceKey, "issue", "label", "acme/widget", "1", "--add", "bug")
 37	out := must(aliceKey, "org", "label", "set", "acme", "bug", "--color", "ff0000", "--json")
 38	if !strings.Contains(out, `"folded":2`) {
 39		t.Fatalf("org label set: %s", out)
 40	}
 41	out = must(aliceKey, "label", "list", "acme/lib", "--json")
 42	if !strings.Contains(out, `"org":true`) || !strings.Contains(out, `"issues":2`) {
 43		t.Fatalf("lib label list: %s", out)
 44	}
 45	if _, errOut, code := inst.ssh(t, aliceKey, "", "label", "set", "acme/lib", "bug"); code == 0 || !strings.Contains(errOut, "org label set acme bug") {
 46		t.Fatalf("repo label set over org name: exit %d %s", code, errOut)
 47	}
 48
 49	// An org milestone attaches from both repositories and counts across.
 50	must(aliceKey, "org", "milestone", "create", "acme", "v1", "--due", "2027-01-01")
 51	must(aliceKey, "issue", "milestone", "acme/lib", "1", "v1")
 52	must(aliceKey, "issue", "milestone", "acme/widget", "1", "v1")
 53	out = must(aliceKey, "org", "milestone", "list", "acme", "--json")
 54	if !strings.Contains(out, `"open":2`) {
 55		t.Fatalf("org milestone list: %s", out)
 56	}
 57	out = must(aliceKey, "issue", "list", "acme/lib", "--milestone", "v1", "--json")
 58	if !strings.Contains(out, `"number":1`) {
 59		t.Fatalf("issue list filtered by org milestone: %s", out)
 60	}
 61
 62	// A push to acme/lib closes acme/widget#1 and leaves a comment there.
 63	work := t.TempDir()
 64	env := inst.gitEnv(aliceKey)
 65	mustGit(t, work, env, "clone", inst.sshURL("acme/lib"), "w")
 66	dir := filepath.Join(work, "w")
 67	os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a\n"), 0o644)
 68	mustGit(t, dir, env, "checkout", "-q", "-b", "main")
 69	mustGit(t, dir, env, "add", ".")
 70	mustGit(t, dir, env, "commit", "-q", "-m", "fix the widget\n\nCloses acme/widget#1")
 71	mustGit(t, dir, env, "push", "-q", "origin", "main")
 72	out = must(aliceKey, "issue", "show", "acme/widget", "1", "--json")
 73	if !strings.Contains(out, `"state":"closed"`) || !strings.Contains(out, "](/acme/lib/commit/") {
 74		t.Fatalf("widget#1 after cross-repo close: %s", out)
 75	}
 76	out = must(aliceKey, "org", "milestone", "list", "acme", "--json")
 77	if !strings.Contains(out, `"open":1`) || !strings.Contains(out, `"closed":1`) {
 78		t.Fatalf("org milestone progress after close: %s", out)
 79	}
 80
 81	// carol is outside: her counts stop at acme/lib, the public one. The
 82	// page checks below are anonymous, which acme/lib being public allows
 83	// just the same.
 84	out = must(carolKey, "org", "milestone", "list", "acme", "--json")
 85	if !strings.Contains(out, `"open":1`) || !strings.Contains(out, `"closed":0`) {
 86		t.Fatalf("outsider progress: %s", out)
 87	}
 88	if status, body := inst.get(t, "/acme/-/labels"); status != 200 || !strings.Contains(body, ">bug<") {
 89		t.Fatalf("org labels page: %d", status)
 90	}
 91	if status, body := inst.get(t, "/acme/-/milestones"); status != 200 || !strings.Contains(body, "v1") || !strings.Contains(body, "0 closed, 1 open") {
 92		t.Fatalf("org milestones page: %d\n%s", status, body)
 93	}
 94	if status, body := inst.get(t, "/acme/lib/labels"); status != 200 || !strings.Contains(body, `chip-neutral">org<`) {
 95		t.Fatalf("repo labels page lacks the org mark: %d", status)
 96	}
 97	// carol cannot close into the private repo from a repo she owns.
 98	must(carolKey, "repo", "create", "carol/own")
 99	must(aliceKey, "issue", "create", "acme/widget", "--title", "'widget two'")
100	cwork := t.TempDir()
101	cenv := inst.gitEnv(carolKey)
102	mustGit(t, cwork, cenv, "clone", inst.sshURL("carol/own"), "w")
103	cdir := filepath.Join(cwork, "w")
104	os.WriteFile(filepath.Join(cdir, "a.txt"), []byte("a\n"), 0o644)
105	mustGit(t, cdir, cenv, "checkout", "-q", "-b", "main")
106	mustGit(t, cdir, cenv, "add", ".")
107	mustGit(t, cdir, cenv, "commit", "-q", "-m", "sneaky\n\nCloses acme/widget#2")
108	mustGit(t, cdir, cenv, "push", "-q", "origin", "main")
109	out = must(aliceKey, "issue", "show", "acme/widget", "2", "--json")
110	if !strings.Contains(out, `"state":"open"`) || strings.Contains(out, "sneaky") {
111		t.Fatalf("outsider acted on a private repo's issue: %s", out)
112	}
113	// With the public repo gone private, the org pages are not found for
114	// an anonymous reader.
115	must(aliceKey, "repo", "settings", "visibility", "acme/lib", "private")
116	if status, _ := inst.get(t, "/acme/-/labels"); status != 404 {
117		t.Fatalf("private org labels page for anonymous: %d", status)
118	}
119}