e2e/mergequeue_test.go
103 lines · 4304 bytes
1package e2e
2
3import (
4 "net/url"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9)
10
11// A merge queued with mr merge --when-ready waits on its gates, shows on
12// the page with a cancel button, and merges as the user who queued it
13// when a status turns the checks green (#289).
14func TestMergeWhenReady(t *testing.T) {
15 t.Parallel()
16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
17 aliceKey := inst.newKey(t, "alice")
18 bobKey := inst.newKey(t, "bob")
19 inst.admin(t, "admin", "user", "create", "alice",
20 "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
21 inst.admin(t, "admin", "user", "create", "bob",
22 "--key", bobKey+".pub", "--email", "bob@example.test", "--verified")
23 for _, args := range [][]string{
24 {"repo", "create", "alice/lib"},
25 {"repo", "access", "grant", "alice/lib", "bob", "write"},
26 {"repo", "settings", "require-contexts", "alice/lib", "ext/test"},
27 } {
28 if _, errOut, code := inst.ssh(t, aliceKey, "", args...); code != 0 {
29 t.Fatalf("%v: %s", args, errOut)
30 }
31 }
32
33 env := inst.gitEnv(aliceKey)
34 work := t.TempDir()
35 mustGit(t, work, env, "clone", "-q", inst.sshURL("alice/lib"), "w")
36 dir := filepath.Join(work, "w")
37 mustGit(t, dir, env, "checkout", "-q", "-b", "main")
38 os.WriteFile(filepath.Join(dir, "lib.txt"), []byte("v1\n"), 0o644)
39 mustGit(t, dir, env, "add", ".")
40 mustGit(t, dir, env, "commit", "-q", "-m", "base")
41 mustGit(t, dir, env, "push", "-q", "origin", "main")
42 mustGit(t, dir, env, "checkout", "-q", "-b", "feature")
43 os.WriteFile(filepath.Join(dir, "lib.txt"), []byte("v2\n"), 0o644)
44 mustGit(t, dir, env, "commit", "-q", "-am", "change")
45 mustGit(t, dir, env, "push", "-q", "origin", "feature")
46 head := strings.TrimSpace(mustGit(t, dir, env, "rev-parse", "HEAD"))
47 if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/lib",
48 "--source", "feature", "--target", "main", "--title", "'change'"); code != 0 {
49 t.Fatalf("mr create: %s", errOut)
50 }
51
52 // Queued over SSH: the checks have not reported, so it waits.
53 out, errOut, code := inst.ssh(t, bobKey, "", "mr", "merge", "alice/lib", "1", "--when-ready")
54 if code != 0 || !strings.Contains(out, "queued") || !strings.Contains(out, "ext/test=missing") {
55 t.Fatalf("mr merge --when-ready: %d %s %s", code, out, errOut)
56 }
57 out, _, _ = inst.ssh(t, aliceKey, "", "mr", "show", "alice/lib", "1", "--json")
58 if !strings.Contains(out, `"queued":{"by":"bob"`) {
59 t.Fatalf("mr show does not carry the queue:\n%s", out)
60 }
61 out, _, _ = inst.ssh(t, bobKey, "", "dashboard", "--json")
62 if !strings.Contains(out, `"queued":true`) {
63 t.Fatalf("dashboard does not carry the queue:\n%s", out)
64 }
65
66 // The page shows it; its cancel button dequeues and its queue button
67 // queues again, now as alice.
68 mrURL := inst.base() + "/alice/lib/mrs/1"
69 alice := inst.login(t, aliceKey)
70 _, body := browserGet(t, alice, mrURL)
71 for _, want := range []string{"Queued to merge by", "waiting: ", "Cancel queued merge"} {
72 if !strings.Contains(body, want) {
73 t.Fatalf("MR page missing %q:\n%s", want, body)
74 }
75 }
76 if status, _ := browserPost(t, alice, mrURL+"/merge", url.Values{"cancel": {"on"}}); status != 200 {
77 t.Fatalf("cancel post: %d", status)
78 }
79 if out, _, _ := inst.ssh(t, aliceKey, "", "mr", "show", "alice/lib", "1", "--json"); strings.Contains(out, `"queued":`) {
80 t.Fatalf("web cancel left the queue:\n%s", out)
81 }
82 if status, _ := browserPost(t, alice, mrURL+"/merge",
83 url.Values{"strategy": {"auto"}, "when_ready": {"on"}}); status != 200 {
84 t.Fatalf("queue post: %d", status)
85 }
86 if st := inst.mrShow(t, aliceKey, "alice/lib", "1").State; st != "open" {
87 t.Fatalf("queued MR is %s before its checks", st)
88 }
89
90 // The status that turns the checks green merges it, as alice.
91 if _, errOut, code := inst.ssh(t, bobKey, "", "status", "set", "alice/lib", head,
92 "--context", "ext/test", "--state", "success"); code != 0 {
93 t.Fatalf("status set: %s", errOut)
94 }
95 merged := inst.mrShow(t, aliceKey, "alice/lib", "1")
96 if merged.State != "merged" || merged.MergedBy != "alice" {
97 t.Fatalf("after green checks: state %s merged by %q", merged.State, merged.MergedBy)
98 }
99 mustGit(t, dir, env, "fetch", "-q", "origin")
100 if got := strings.TrimSpace(mustGit(t, dir, env, "rev-parse", "origin/main")); got != head {
101 t.Fatalf("main = %s, want %s", got, head)
102 }
103}