e2e/orglabels_test.go

v1.24.1
gitbay/e2e/orglabels_test.go history · blame · raw

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