e2e/migratemr_test.go

v1.25.0
gitbay/e2e/migratemr_test.go history · blame · raw

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}