e2e/edit_test.go
111 lines · 4560 bytes
1package e2e
2
3import (
4 "encoding/json"
5 "net/url"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10)
11
12func TestIssueMREditing(t *testing.T) {
13 t.Parallel()
14 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
15 aliceKey := inst.newKey(t, "alice")
16 bobKey := inst.newKey(t, "bob")
17 eveKey := inst.newKey(t, "eve")
18 inst.admin(t, "admin", "user", "create", "alice",
19 "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
20 inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
21 inst.admin(t, "admin", "user", "create", "eve", "--key", eveKey+".pub")
22
23 if _, _, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 {
24 t.Fatal("repo create failed")
25 }
26 // bob authors an issue (no write access); eve is an outsider.
27 if _, _, code := inst.ssh(t, bobKey, "", "issue", "create", "alice/app", "--title", "'typo titel'", "--body", "'first'"); code != 0 {
28 t.Fatal("issue create failed")
29 }
30
31 // SSH edit: the author may fix it; an outsider may not; empty titles
32 // are refused; write access (alice) may edit someone else's.
33 if _, errOut, code := inst.ssh(t, bobKey, "", "issue", "edit", "alice/app", "1", "--title", "'typo title'"); code != 0 {
34 t.Fatalf("author edit: %s", errOut)
35 }
36 if _, _, code := inst.ssh(t, eveKey, "", "issue", "edit", "alice/app", "1", "--title", "'hax'"); code != 4 {
37 t.Fatal("outsider edited an issue")
38 }
39 if _, _, code := inst.ssh(t, bobKey, "", "issue", "edit", "alice/app", "1", "--title", "''"); code != 2 {
40 t.Fatal("empty title accepted")
41 }
42 if _, _, code := inst.ssh(t, aliceKey, "", "issue", "edit", "alice/app", "1", "--body", "'rewritten'"); code != 0 {
43 t.Fatal("write-access edit failed")
44 }
45 out, _, _ := inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "1", "--json")
46 if !strings.Contains(out, "typo title") || !strings.Contains(out, "rewritten") {
47 t.Fatalf("edits not applied: %s", out)
48 }
49
50 // MR edit over SSH.
51 work := t.TempDir()
52 env := inst.gitEnv(aliceKey)
53 mustGit(t, work, env, "clone", inst.sshURL("alice/app"), "w")
54 dir := filepath.Join(work, "w")
55 os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a\n"), 0o644)
56 mustGit(t, dir, env, "checkout", "-q", "-b", "main")
57 mustGit(t, dir, env, "add", ".")
58 mustGit(t, dir, env, "commit", "-q", "-m", "base")
59 mustGit(t, dir, env, "push", "-q", "origin", "main")
60 mustGit(t, dir, env, "checkout", "-q", "-b", "feat")
61 os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b\n"), 0o644)
62 mustGit(t, dir, env, "add", ".")
63 mustGit(t, dir, env, "commit", "-q", "-m", "feat")
64 mustGit(t, dir, env, "push", "-q", "origin", "feat")
65 if _, _, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/app",
66 "--source", "feat", "--target", "main", "--title", "'draft'"); code != 0 {
67 t.Fatal("mr create failed")
68 }
69 if _, _, code := inst.ssh(t, aliceKey, "", "mr", "edit", "alice/app", "1", "--title", "'ready'"); code != 0 {
70 t.Fatal("mr edit failed")
71 }
72 if out, _, _ := inst.ssh(t, aliceKey, "", "mr", "show", "alice/app", "1", "--json"); !strings.Contains(out, "ready") {
73 t.Fatalf("mr edit not applied: %s", out)
74 }
75
76 // Web edit: form appears for the authorized viewer, POST applies, and
77 // with write access the label set is replaced.
78 out, _, code := inst.ssh(t, aliceKey, "", "web", "login", "--json")
79 if code != 0 {
80 t.Fatal("web login failed")
81 }
82 var env2 struct {
83 Data struct {
84 URL string `json:"url"`
85 } `json:"data"`
86 }
87 json.Unmarshal([]byte(out), &env2)
88 browser := newBrowser(t)
89 browserGet(t, browser, inst.base()+env2.Data.URL[strings.Index(env2.Data.URL, "/login"):])
90 _, body := browserGet(t, browser, inst.base()+"/alice/app/issues/1")
91 if !strings.Contains(body, "/alice/app/issues/1/edit") {
92 t.Fatal("edit form missing for authorized viewer")
93 }
94 if status, _ := browserPost(t, browser, inst.base()+"/alice/app/issues/1/edit",
95 url.Values{"title": {"web-edited"}, "body": {"web body"}, "labels": {"bug"}}); status != 200 {
96 t.Fatal("web issue edit failed")
97 }
98 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "1", "--json")
99 if !strings.Contains(out, "web-edited") || !strings.Contains(out, `"labels":["bug"]`) {
100 t.Fatalf("web edit not applied: %s", out)
101 }
102 if status, _ := browserPost(t, browser, inst.base()+"/alice/app/mrs/1/edit",
103 url.Values{"title": {"web-mr"}, "body": {"mb"}}); status != 200 {
104 t.Fatal("web mr edit failed")
105 }
106 // Anonymous view shows no edit affordance.
107 _, body = inst.get(t, "/alice/app/issues/1")
108 if strings.Contains(body, "/issues/1/edit") {
109 t.Fatal("edit form leaked to anonymous viewer")
110 }
111}