e2e/rangediff_test.go
125 lines · 4723 bytes
1package e2e
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9)
10
11// TestMRRangeDiff is #111's last stage: a push stales every review and
12// nothing said what had changed between the two heads. A plain diff of
13// the heads cannot answer that — it shows the whole branch again.
14func TestMRRangeDiff(t *testing.T) {
15 t.Parallel()
16 inst := startInstance(t)
17 key := inst.newKey(t, "alice")
18 inst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
19 if _, errOut, code := inst.ssh(t, key, "", "repo", "create", "alice/app"); code != 0 {
20 t.Fatalf("repo create: %s", errOut)
21 }
22 env := inst.gitEnv(key)
23 work := t.TempDir()
24 mustGit(t, work, env, "clone", inst.sshURL("alice/app"), "w")
25 dir := filepath.Join(work, "w")
26 os.WriteFile(filepath.Join(dir, "a.txt"), []byte("one\n"), 0o644)
27 mustGit(t, dir, env, "checkout", "-q", "-b", "main")
28 mustGit(t, dir, env, "add", ".")
29 mustGit(t, dir, env, "commit", "-q", "-m", "base")
30 mustGit(t, dir, env, "push", "-q", "origin", "main")
31
32 mustGit(t, dir, env, "checkout", "-q", "-b", "feat")
33 os.WriteFile(filepath.Join(dir, "b.txt"), []byte("alpha\nbeta\ngamma\n"), 0o644)
34 mustGit(t, dir, env, "add", ".")
35 mustGit(t, dir, env, "commit", "-q", "-m", "add b")
36 mustGit(t, dir, env, "push", "-q", "origin", "feat")
37 if _, errOut, code := inst.ssh(t, key, "", "mr", "create", "alice/app",
38 "--source", "feat", "--target", "main", "--title", "'add b'"); code != 0 {
39 t.Fatalf("mr create: %s", errOut)
40 }
41
42 // One revision: nothing to compare, and saying so is not a failure.
43 out, errOut, code := inst.ssh(t, key, "", "mr", "range-diff", "alice/app", "1")
44 if code != 0 || !strings.Contains(errOut, "one revision") {
45 t.Fatalf("single-revision range-diff: exit %d, %s, %s", code, errOut, out)
46 }
47
48 // Amend and force-push: the same commit with one line changed, which
49 // is what addressing review feedback looks like. A diff of the two
50 // heads cannot describe this — it shows the whole branch again.
51 os.WriteFile(filepath.Join(dir, "b.txt"), []byte("alpha\nbeta revised\ngamma\n"), 0o644)
52 mustGit(t, dir, env, "add", ".")
53 mustGit(t, dir, env, "commit", "-q", "--amend", "--no-edit")
54 mustGit(t, dir, env, "push", "-q", "--force", "origin", "feat")
55
56 revs := revisions(t, inst, key)
57 if len(revs) != 2 {
58 t.Fatalf("revisions = %d, want 2 after a force-push: %+v", len(revs), revs)
59 }
60 if !revs[1].Current {
61 t.Fatalf("the newest revision is not marked current: %+v", revs)
62 }
63
64 out, errOut, code = inst.ssh(t, key, "", "mr", "range-diff", "alice/app", "1")
65 if code != 0 {
66 t.Fatalf("range-diff: %s", errOut)
67 }
68 // The two versions of the one commit are paired — "1: <old> ! 1:
69 // <new>" — and the interdiff shows the one line that moved, not the
70 // whole branch.
71 if !strings.Contains(out, "add b") {
72 t.Fatalf("range-diff does not mention the commit:\n%s", out)
73 }
74 if !strings.Contains(out, "!") {
75 t.Fatalf("range-diff did not pair the amended commit:\n%s", out)
76 }
77 if !strings.Contains(out, "beta revised") {
78 t.Fatalf("range-diff does not show the changed line:\n%s", out)
79 }
80 // One commit on each side, paired: a plain diff of the two heads
81 // would instead show b.txt created from nothing all over again.
82 if strings.Count(out, "add b") != 1 {
83 t.Fatalf("range-diff lists the commit more than once:\n%s", out)
84 }
85
86 // Naming revisions explicitly, and refusing one that is not a
87 // revision of this merge request.
88 if _, _, code := inst.ssh(t, key, "", "mr", "range-diff", "alice/app", "1",
89 "--from", revs[0].SHA, "--to", revs[1].SHA); code != 0 {
90 t.Fatal("explicit --from/--to failed")
91 }
92 if _, errOut, code := inst.ssh(t, key, "", "mr", "range-diff", "alice/app", "1",
93 "--from", "0123456789ab"); code != 3 || !strings.Contains(errOut, "not a revision") {
94 t.Fatalf("unknown revision: exit %d, %s", code, errOut)
95 }
96 if _, _, code := inst.ssh(t, key, "", "mr", "range-diff", "alice/app", "1",
97 "--from", revs[1].SHA, "--to", revs[1].SHA); code != 2 {
98 t.Fatal("comparing a revision with itself was accepted")
99 }
100
101 // A push that changes nothing does not add a revision.
102 mustGit(t, dir, env, "push", "-q", "--force", "origin", "feat")
103 if got := revisions(t, inst, key); len(got) != 2 {
104 t.Fatalf("a no-op push added a revision: %d", len(got))
105 }
106}
107
108type revision struct {
109 N int `json:"n"`
110 SHA string `json:"sha"`
111 Current bool `json:"current"`
112}
113
114func revisions(t *testing.T, inst *instance, key string) []revision {
115 t.Helper()
116 out, errOut, code := inst.ssh(t, key, "", "mr", "revisions", "alice/app", "1", "--json")
117 if code != 0 {
118 t.Fatalf("mr revisions: %s", errOut)
119 }
120 var env struct {
121 Data []revision `json:"data"`
122 }
123 json.Unmarshal([]byte(out), &env)
124 return env.Data
125}