e2e/migratemr_test.go
99 lines · 4119 bytes
1package e2e
2
3import (
4 "os"
5 "path/filepath"
6 "strings"
7 "testing"
8)
9
10// TestMigratedMRHasADiff covers #128's concrete symptom: a merge request
11// replayed from a bundle used to arrive with an empty head, so `mr diff`
12// on it could only fail. The bundle carries the head and the merge base
13// now, and the head ref is set once the git objects are pushed.
14//
15// The merge request here is merged and its source branch deleted, which
16// is the case that actually breaks. While the branch still exists the
17// diff resolves through it and an empty head_sha is invisible — the first
18// version of this test made that mistake and passed without the fix.
19func TestMigratedMRHasADiff(t *testing.T) {
20 src := startInstance(t)
21 dst := startInstance(t)
22 key := src.newKey(t, "alice")
23 src.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
24 dst.admin(t, "admin", "user", "create", "alice", "--key", key+".pub")
25
26 if _, errOut, code := src.ssh(t, key, "", "repo", "create", "alice/tool"); code != 0 {
27 t.Fatalf("repo create: %s", errOut)
28 }
29 env := src.gitEnv(key)
30 work := t.TempDir()
31 mustGit(t, work, env, "clone", src.sshURL("alice/tool"), "w")
32 dir := filepath.Join(work, "w")
33 os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n"), 0o644)
34 mustGit(t, dir, env, "checkout", "-q", "-b", "main")
35 mustGit(t, dir, env, "add", ".")
36 mustGit(t, dir, env, "commit", "-q", "-m", "base")
37 mustGit(t, dir, env, "push", "-q", "origin", "main")
38
39 mustGit(t, dir, env, "checkout", "-q", "-b", "feat")
40 os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\nfunc F() {}\n"), 0o644)
41 mustGit(t, dir, env, "add", ".")
42 mustGit(t, dir, env, "commit", "-q", "-m", "add F")
43 mustGit(t, dir, env, "push", "-q", "origin", "feat")
44 if _, errOut, code := src.ssh(t, key, "", "mr", "create", "alice/tool",
45 "--source", "feat", "--target", "main", "--title", "'add F'"); code != 0 {
46 t.Fatalf("mr create: %s", errOut)
47 }
48 // Merge it and delete the branch, the way a finished merge request
49 // ends up. Now nothing but the recorded head says what it contained.
50 if _, errOut, code := src.ssh(t, key, "", "mr", "merge", "alice/tool", "1"); code != 0 {
51 t.Fatalf("merge: %s", errOut)
52 }
53 mustGit(t, dir, env, "push", "-q", "origin", "--delete", "feat")
54
55 // The source's own diff is the standard to match.
56 want, errOut, code := src.ssh(t, key, "", "mr", "diff", "alice/tool", "1")
57 if code != 0 || !strings.Contains(want, "func F()") {
58 t.Fatalf("source diff: %s\n%s", errOut, want)
59 }
60
61 bundle, errOut, code := src.ssh(t, key, "", "account", "export")
62 if code != 0 {
63 t.Fatalf("export: %s", errOut)
64 }
65 if !strings.Contains(bundle, `"head_sha"`) {
66 t.Fatalf("bundle carries no head_sha:\n%s", bundle)
67 }
68 if _, errOut, code := dst.ssh(t, key, bundle, "account", "import-bundle"); code != 0 {
69 t.Fatalf("import: %s", errOut)
70 }
71
72 // Git data moves separately, which is the order a real migration runs
73 // in: the bundle first, the push after.
74 // Only main: the feature branch is gone, exactly as at the source.
75 // The merge commit carries the head's objects, so they arrive anyway.
76 denv := dst.gitEnv(key)
77 mustGit(t, dir, denv, "remote", "add", "dst", dst.sshURL("alice/tool"))
78 mustGit(t, dir, denv, "fetch", "-q", "origin", "main")
79 mustGit(t, dir, denv, "push", "-q", "dst", "refs/remotes/origin/main:refs/heads/main")
80
81 // Re-importing the same bundle is how the head ref gets set once the
82 // objects are present; the merge request itself is already there.
83 out, errOut, code := dst.ssh(t, key, bundle, "account", "import-bundle")
84 if code != 0 {
85 t.Fatalf("re-import: %s", errOut)
86 }
87 if !strings.Contains(out, "already present") {
88 t.Fatalf("re-import did not skip what it had: %s", out)
89 }
90
91 show, _, _ := dst.ssh(t, key, "", "mr", "show", "alice/tool", "1", "--json")
92 got, errOut, code := dst.ssh(t, key, "", "mr", "diff", "alice/tool", "1")
93 if code != 0 {
94 t.Fatalf("migrated MR has no diff: %s\n%s\nmr show: %s", errOut, got, show)
95 }
96 if !strings.Contains(got, "func F()") {
97 t.Fatalf("migrated diff does not match the source:\nwant to contain func F()\ngot:\n%s", got)
98 }
99}