e2e/commitrefs_test.go
138 lines · 6385 bytes
1package e2e
2
3import (
4 "os"
5 "path/filepath"
6 "strings"
7 "testing"
8)
9
10func TestCommitMessageIssueActions(t *testing.T) {
11 t.Parallel()
12 inst := startInstance(t)
13 aliceKey := inst.newKey(t, "alice")
14 inst.admin(t, "admin", "user", "create", "alice",
15 "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
16
17 if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 {
18 t.Fatalf("repo create: %s", errOut)
19 }
20 work := t.TempDir()
21 env := inst.gitEnv(aliceKey)
22 mustGit(t, work, env, "clone", inst.sshURL("alice/app"), "w")
23 dir := filepath.Join(work, "w")
24 os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a\n"), 0o644)
25 mustGit(t, dir, env, "checkout", "-q", "-b", "main")
26 mustGit(t, dir, env, "add", ".")
27 mustGit(t, dir, env, "commit", "-q", "-m", "base")
28 mustGit(t, dir, env, "push", "-q", "origin", "main")
29 for _, title := range []string{"'one'", "'two'", "'three'"} {
30 if _, _, code := inst.ssh(t, aliceKey, "", "issue", "create", "alice/app", "--title", title); code != 0 {
31 t.Fatal("issue create failed")
32 }
33 }
34
35 // A closing keyword on the default branch closes the issue with a
36 // comment; a bare reference (and a nonexistent #99) only comments.
37 os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b\n"), 0o644)
38 aliceEnv := append(append([]string{}, env...),
39 "GIT_AUTHOR_NAME=Alice", "GIT_AUTHOR_EMAIL=alice@example.test")
40 mustGit(t, dir, env, "add", ".")
41 mustGit(t, dir, aliceEnv, "commit", "-q", "-m", "repair the widget\n\nFixes #1. Related to #2 but not #99.")
42 mustGit(t, dir, env, "push", "-q", "origin", "main")
43
44 out, _, _ := inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "1", "--json")
45 if !strings.Contains(out, `"state":"closed"`) || !strings.Contains(out, "closed by ") {
46 t.Fatalf("issue 1 not closed by commit: %s", out)
47 }
48 // The entry is a system message with a linked sha, not a user comment.
49 if !strings.Contains(out, `"author":"system"`) || !strings.Contains(out, "](/alice/app/commit/") {
50 t.Fatalf("close entry not a linked system message: %s", out)
51 }
52 if status, body := inst.get(t, "/alice/app/issues/1"); status != 200 ||
53 !strings.Contains(body, `class="syscomment"`) || !strings.Contains(body, `/alice/app/commit/`) {
54 t.Fatalf("web system message: %d", status)
55 }
56 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "2", "--json")
57 if !strings.Contains(out, `"state":"open"`) || !strings.Contains(out, "referenced in commit") ||
58 !strings.Contains(out, "repair the widget") {
59 t.Fatalf("issue 2 not referenced: %s", out)
60 }
61 // The reference names who wrote the commit, and links them when the
62 // author email is verified on an account here.
63 if !strings.Contains(out, "by [alice](/alice)") {
64 t.Fatalf("reference does not attribute the author: %s", out)
65 }
66 if status, body := inst.get(t, "/alice/app/issues/2"); status != 200 ||
67 !strings.Contains(body, `href="/alice"`) {
68 t.Fatalf("web reference does not link the author: %d\n%s", status, body)
69 }
70
71 // Commits on a feature branch do nothing until they land on the
72 // default branch via a merge — then the merge path acts exactly once.
73 mustGit(t, dir, env, "checkout", "-q", "-b", "feat")
74 os.WriteFile(filepath.Join(dir, "c.txt"), []byte("c\n"), 0o644)
75 mustGit(t, dir, env, "add", ".")
76 mustGit(t, dir, env, "commit", "-q", "-m", "finish the gadget\n\nCloses #3")
77 mustGit(t, dir, env, "push", "-q", "origin", "feat")
78 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "3", "--json")
79 if !strings.Contains(out, `"state":"open"`) {
80 t.Fatalf("branch push acted early: %s", out)
81 }
82 if _, _, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/app",
83 "--source", "feat", "--target", "main", "--title", "'gadget'"); code != 0 {
84 t.Fatal("mr create failed")
85 }
86 if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "merge", "alice/app", "1"); code != 0 {
87 t.Fatalf("merge: %s", errOut)
88 }
89 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "3", "--json")
90 if !strings.Contains(out, `"state":"closed"`) || !strings.Contains(out, "closed by ") {
91 t.Fatalf("merge did not close issue 3: %s", out)
92 }
93 // This one was authored by an address nobody has verified, so it names
94 // git's author without inventing a profile link for them.
95 if !strings.Contains(out, "closed by t in commit") || strings.Contains(out, "closed by [t]") {
96 t.Fatalf("unresolved author should stay plain text: %s", out)
97 }
98 if strings.Count(out, "closed by ") != 1 {
99 t.Fatalf("duplicate close comments: %s", out)
100 }
101
102 // Pushing more commits does not re-act on already-processed shas.
103 os.WriteFile(filepath.Join(dir, "d.txt"), []byte("d\n"), 0o644)
104 mustGit(t, dir, env, "checkout", "-q", "main")
105 mustGit(t, dir, env, "pull", "-q", "origin", "main")
106 mustGit(t, dir, env, "add", ".")
107 mustGit(t, dir, env, "commit", "-q", "-m", "unrelated")
108 mustGit(t, dir, env, "push", "-q", "origin", "main")
109 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "2", "--json")
110 if strings.Count(out, "referenced in commit") != 1 {
111 t.Fatalf("reference duplicated: %s", out)
112 }
113
114 // A commit written by repo commit-file (the web editor's path) lands
115 // on the default branch without receive-pack or a merge, and acts
116 // the same (#210). On another branch it does nothing.
117 if _, errOut, code := inst.ssh(t, aliceKey, "e\n", "repo", "commit-file", "alice/app", "e.txt",
118 "--ref", "feat", "--message", "'Closes #2 on a branch'", "--file", "-"); code != 0 {
119 t.Fatalf("commit-file on branch: %s", errOut)
120 }
121 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "2", "--json")
122 if !strings.Contains(out, `"state":"open"`) {
123 t.Fatalf("commit-file on a branch acted: %s", out)
124 }
125 if _, errOut, code := inst.ssh(t, aliceKey, "e\n", "repo", "commit-file", "alice/app", "e.txt",
126 "--ref", "main", "--message", "'Closes #2 from the editor'", "--file", "-"); code != 0 {
127 t.Fatalf("commit-file: %s", errOut)
128 }
129 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "2", "--json")
130 if !strings.Contains(out, `"state":"closed"`) ||
131 !strings.Contains(out, "closed by [alice](/alice) in commit") ||
132 !strings.Contains(out, ": Closes #2 from the editor") {
133 t.Fatalf("commit-file did not close issue 2: %s", out)
134 }
135 if strings.Count(out, "referenced in commit") != 1 {
136 t.Fatalf("commit-file re-acted on earlier commits: %s", out)
137 }
138}