e2e/status_test.go
114 lines · 5081 bytes
1 symbol in this file
1package e2e
2
3import (
4 "os"
5 "path/filepath"
6 "strings"
7 "testing"
8)
9
10func TestCommitStatuses(t *testing.T) {
11 t.Parallel()
12 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice")
14 bobKey := inst.newKey(t, "bob")
15 eveKey := inst.newKey(t, "eve")
16 inst.admin(t, "admin", "user", "create", "alice",
17 "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
18 inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
19 inst.admin(t, "admin", "user", "create", "eve", "--key", eveKey+".pub")
20
21 // Repo with a branch and an MR.
22 if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/svc"); code != 0 {
23 t.Fatalf("repo create: %s", errOut)
24 }
25 if _, _, code := inst.ssh(t, aliceKey, "", "repo", "access", "grant", "alice/svc", "bob", "write"); code != 0 {
26 t.Fatal("grant failed")
27 }
28 work := t.TempDir()
29 env := inst.gitEnv(aliceKey)
30 mustGit(t, work, env, "clone", inst.sshURL("alice/svc"), "w")
31 dir := filepath.Join(work, "w")
32 os.WriteFile(filepath.Join(dir, "svc.txt"), []byte("v1\n"), 0o644)
33 mustGit(t, dir, env, "checkout", "-q", "-b", "main")
34 mustGit(t, dir, env, "add", ".")
35 mustGit(t, dir, env, "commit", "-q", "-m", "base")
36 mustGit(t, dir, env, "push", "-q", "origin", "main")
37 mustGit(t, dir, env, "checkout", "-q", "-b", "feat")
38 mustGit(t, dir, env, "commit", "-q", "--allow-empty", "-m", "feature")
39 mustGit(t, dir, env, "push", "-q", "origin", "feat")
40 head := strings.TrimSpace(mustGit(t, dir, env, "rev-parse", "feat"))
41 if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/svc",
42 "--source", "feat", "--target", "main", "--title", "'gated'"); code != 0 {
43 t.Fatalf("mr create: %s", errOut)
44 }
45
46 // Reporting requires write: eve (read-only public) is denied.
47 if _, _, code := inst.ssh(t, eveKey, "", "status", "set", "alice/svc", head, "--context", "build", "--state", "success"); code != 4 {
48 t.Fatal("reader reported a status")
49 }
50
51 // ci/ is the instance's own: a writer is refused it (#258).
52 if _, errOut, code := inst.ssh(t, bobKey, "", "status", "set", "alice/svc", head, "--context", "ci/build", "--state", "success"); code != 4 || !strings.Contains(errOut, "reserved") {
53 t.Fatalf("writer posted a ci/ status: exit %d, %s", code, errOut)
54 }
55
56 // Bob (write) reports pending, then success: upsert, not duplicate.
57 if _, errOut, code := inst.ssh(t, bobKey, "", "status", "set", "alice/svc", head,
58 "--context", "build", "--state", "pending", "--description", "'compiling'"); code != 0 {
59 t.Fatalf("status set: %s", errOut)
60 }
61 if _, _, code := inst.ssh(t, bobKey, "", "status", "set", "alice/svc", head,
62 "--context", "build", "--state", "success", "--url", "https://ci.example/1"); code != 0 {
63 t.Fatal("status update failed")
64 }
65 out, _, _ := inst.ssh(t, aliceKey, "", "status", "list", "alice/svc", head, "--json")
66 if !strings.Contains(out, `"combined":"success"`) || strings.Count(out, `"context":"build"`) != 1 {
67 t.Fatalf("status list after upsert: %s", out)
68 }
69
70 // A second failing context drags the combined state down; mr show sees it.
71 if _, _, code := inst.ssh(t, bobKey, "", "status", "set", "alice/svc", head,
72 "--context", "lint", "--state", "failure"); code != 0 {
73 t.Fatal("lint status failed")
74 }
75 out, _, _ = inst.ssh(t, aliceKey, "", "mr", "show", "alice/svc", "1", "--json")
76 if !strings.Contains(out, `"checks_combined":"failure"`) {
77 t.Fatalf("mr show checks: %s", out)
78 }
79
80 // require-checks gates the merge: failing -> refused naming context,
81 // green -> merges.
82 if _, _, code := inst.ssh(t, aliceKey, "", "repo", "settings", "require-checks", "alice/svc", "on"); code != 0 {
83 t.Fatal("require-checks failed")
84 }
85 _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "merge", "alice/svc", "1")
86 if code != 4 || !strings.Contains(errOut, "lint=failure") {
87 t.Fatalf("merge with red checks: exit %d, %s", code, errOut)
88 }
89 if _, _, code := inst.ssh(t, bobKey, "", "status", "set", "alice/svc", head, "--context", "lint", "--state", "success"); code != 0 {
90 t.Fatal("lint fix failed")
91 }
92 if _, errOut, code = inst.ssh(t, aliceKey, "", "mr", "merge", "alice/svc", "1"); code != 0 {
93 t.Fatalf("merge with green checks: %s", errOut)
94 }
95
96 // A repo requiring checks refuses merges with NO statuses at all.
97 mustGit(t, dir, env, "checkout", "-q", "-b", "feat2", "main")
98 mustGit(t, dir, env, "commit", "-q", "--allow-empty", "-m", "next")
99 mustGit(t, dir, env, "push", "-q", "origin", "feat2")
100 if _, _, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/svc",
101 "--source", "feat2", "--target", "main", "--title", "'unchecked'"); code != 0 {
102 t.Fatal("mr2 create failed")
103 }
104 _, errOut, code = inst.ssh(t, aliceKey, "", "mr", "merge", "alice/svc", "2")
105 if code != 4 || !strings.Contains(errOut, "none were reported") {
106 t.Fatalf("merge with no checks: exit %d, %s", code, errOut)
107 }
108
109 // Web: the commit page shows check badges.
110 status, body := inst.get(t, "/alice/svc/commit/"+head)
111 if status != 200 || !strings.Contains(body, "check-success") || !strings.Contains(body, "build") {
112 t.Fatalf("commit page checks: %d", status)
113 }
114}