e2e/gates_test.go

419f6dfdc5489a0c6374e36dd1ebbfca68040056
gitbay/e2e/gates_test.go history · blame · raw

100 lines · 4537 bytes

  1package e2e
  2
  3import (
  4	"os"
  5	"path/filepath"
  6	"strings"
  7	"testing"
  8)
  9
 10// mr show reports the gates before the merge fails, mr merge names every
 11// unmet gate at once, and mr review says when a verdict is advisory
 12// (#199).
 13func TestMergeGatesVisible(t *testing.T) {
 14	t.Parallel()
 15	inst := startInstance(t)
 16	aliceKey := inst.newKey(t, "alice")
 17	bobKey := inst.newKey(t, "bob")
 18	carolKey := inst.newKey(t, "carol")
 19	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
 20	inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
 21	inst.admin(t, "admin", "user", "create", "carol", "--key", carolKey+".pub")
 22	for _, args := range [][]string{
 23		{"repo", "create", "alice/svc"},
 24		{"repo", "access", "grant", "alice/svc", "bob", "write"},
 25		{"repo", "settings", "require-approvals", "alice/svc", "1"},
 26		{"repo", "settings", "require-codeowners", "alice/svc", "on"},
 27		{"repo", "settings", "require-resolved", "alice/svc", "on"},
 28	} {
 29		if _, errOut, code := inst.ssh(t, aliceKey, "", args...); code != 0 {
 30			t.Fatalf("%v: %s", args, errOut)
 31		}
 32	}
 33	env := inst.gitEnv(aliceKey)
 34	work := t.TempDir()
 35	mustGit(t, work, env, "clone", "-q", inst.sshURL("alice/svc"), "w")
 36	dir := filepath.Join(work, "w")
 37	mustGit(t, dir, env, "checkout", "-q", "-b", "main")
 38	os.WriteFile(filepath.Join(dir, "CODEOWNERS"), []byte("*.go @bob\n"), 0o644)
 39	os.WriteFile(filepath.Join(dir, "svc.go"), []byte("package svc\n"), 0o644)
 40	mustGit(t, dir, env, "add", ".")
 41	mustGit(t, dir, env, "commit", "-q", "-m", "base")
 42	mustGit(t, dir, env, "push", "-q", "origin", "main")
 43	mustGit(t, dir, env, "checkout", "-q", "-b", "feat")
 44	os.WriteFile(filepath.Join(dir, "svc.go"), []byte("package svc\n\nvar V = 1\n"), 0o644)
 45	mustGit(t, dir, env, "commit", "-q", "-am", "change")
 46	mustGit(t, dir, env, "push", "-q", "origin", "feat")
 47	if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/svc",
 48		"--source", "feat", "--target", "main", "--title", "'change'"); code != 0 {
 49		t.Fatalf("mr create: %s", errOut)
 50	}
 51
 52	// Before anyone reviews: two unmet gates, visible on mr show.
 53	out, _, _ := inst.ssh(t, aliceKey, "", "mr", "show", "alice/svc", "1", "--json")
 54	for _, want := range []string{
 55		`"approvals_required":1`, `"codeowners_required":true`, `"resolved_required":true`,
 56		`"owners_outstanding":[{"files":["svc.go"],"owners":["bob"]}]`, `"fast_forward":true`,
 57		`requires 1 fresh approval(s)`, `CODEOWNERS approval missing for: svc.go (owned by bob)`,
 58	} {
 59		if !strings.Contains(out, want) {
 60			t.Errorf("mr show gates missing %s:\n%s", want, out)
 61		}
 62	}
 63	// The merge names both at once.
 64	if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "merge", "alice/svc", "1"); code != 4 ||
 65		!strings.Contains(errOut, "fresh approval") || !strings.Contains(errOut, "CODEOWNERS approval missing") {
 66		t.Fatalf("merge refusal does not name every gate: %d %s", code, errOut)
 67	}
 68
 69	// A reader's approval is advisory, and says so when made.
 70	out, _, code := inst.ssh(t, carolKey, "", "mr", "review", "alice/svc", "1", "--approve", "--json")
 71	if code != 0 || !strings.Contains(out, `"counts":false`) {
 72		t.Fatalf("reader review: %d %s", code, out)
 73	}
 74	if out, _, _ := inst.ssh(t, carolKey, "", "mr", "review", "alice/svc", "1", "--comment"); !strings.Contains(out, "advisory") {
 75		t.Fatalf("reader review does not say it is advisory: %s", out)
 76	}
 77	out, _, _ = inst.ssh(t, aliceKey, "", "mr", "show", "alice/svc", "1", "--json")
 78	if !strings.Contains(out, `requires 1 fresh approval(s)`) {
 79		t.Fatalf("advisory approval counted in the gates:\n%s", out)
 80	}
 81
 82	// The owner's approval meets both; the block says so and the merge lands.
 83	out, _, _ = inst.ssh(t, bobKey, "", "mr", "review", "alice/svc", "1", "--approve", "--json")
 84	if !strings.Contains(out, `"counts":true`) {
 85		t.Fatalf("writer review: %s", out)
 86	}
 87	out, _, _ = inst.ssh(t, aliceKey, "", "mr", "show", "alice/svc", "1", "--json")
 88	if strings.Contains(out, `"unmet"`) || !strings.Contains(out, `"approvals":["bob"]`) {
 89		t.Fatalf("gates after approval:\n%s", out)
 90	}
 91	if out, _, _ := inst.ssh(t, aliceKey, "", "mr", "show", "alice/svc", "1"); !strings.Contains(out, "gates: met; fast-forward possible") {
 92		t.Fatalf("text gates line: %s", out)
 93	}
 94	if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "merge", "alice/svc", "1"); code != 0 {
 95		t.Fatalf("merge: %s", errOut)
 96	}
 97	if out, _, _ := inst.ssh(t, aliceKey, "", "mr", "show", "alice/svc", "1", "--json"); strings.Contains(out, `"gates"`) {
 98		t.Fatalf("gates reported on a merged request:\n%s", out)
 99	}
100}