internal/autolink/autolink_test.go
102 lines · 3482 bytes
1package autolink
2
3import (
4 "strings"
5 "testing"
6)
7
8// fakeResolver knows issue 4 and MR 2 in krz/gitbay, issue 7 in cmc/tools,
9// and users alice and krz.
10type fakeResolver struct{}
11
12func (fakeResolver) RefURL(owner, name string, kind byte, n int64) string {
13 switch {
14 case owner == "krz" && name == "gitbay" && kind == '#' && n == 4:
15 return IssueURL(owner, name, n)
16 case owner == "krz" && name == "gitbay" && kind == '!' && n == 2:
17 return MRURL(owner, name, n)
18 case owner == "cmc" && name == "tools" && kind == '#' && n == 7:
19 return IssueURL(owner, name, n)
20 }
21 return ""
22}
23
24func (fakeResolver) UserURL(name string) string {
25 if name == "alice" || name == "krz" {
26 return "/" + name
27 }
28 return ""
29}
30
31func rw(t *testing.T, in string) string {
32 t.Helper()
33 return Rewrite(in, "krz", "gitbay", fakeResolver{})
34}
35
36func TestRewrite(t *testing.T) {
37 cases := []struct {
38 name, in string
39 want []string // substrings that must appear
40 wantNot []string
41 }{
42 {"bare issue ref", "<p>see #4 for details</p>",
43 []string{`<a href="/krz/gitbay/issues/4" class="xref">#4</a>`}, nil},
44 {"bare mr ref", "<p>fixed in !2.</p>",
45 []string{`<a href="/krz/gitbay/mrs/2" class="xref">!2</a>`}, nil},
46 {"cross-repo ref", "<p>tracked at cmc/tools#7 upstream</p>",
47 []string{`<a href="/cmc/tools/issues/7" class="xref">cmc/tools#7</a>`}, nil},
48 {"mention", "<p>ping @alice about it</p>",
49 []string{`<a href="/alice" class="xref">@alice</a>`}, nil},
50 {"org mention", "<p>@krz owns this</p>",
51 []string{`<a href="/krz" class="xref">@krz</a>`}, nil},
52 {"nonexistent issue stays text", "<p>see #999</p>",
53 []string{"<p>see #999</p>"}, []string{"<a"}},
54 {"nonexistent user stays text", "<p>hi @nobody</p>",
55 []string{"<p>hi @nobody</p>"}, []string{"<a"}},
56 {"code spans untouched", "<p>run <code>git show #4</code> now</p>",
57 []string{"<code>git show #4</code>"}, []string{`issues/4`}},
58 {"pre blocks untouched", "<pre>#4 !2 @alice</pre>",
59 []string{"<pre>#4 !2 @alice</pre>"}, []string{"<a"}},
60 {"existing links untouched", `<a href="/x">#4</a>`,
61 []string{`<a href="/x">#4</a>`}, []string{"issues/4"}},
62 {"mid-word hash not a ref", "<p>sha a1b2#4 is odd</p>",
63 nil, []string{"<a"}},
64 {"mid-word at not a mention", "<p>mail me@alice.example ok</p>",
65 nil, []string{"<a"}},
66 {"multiple refs one line", "<p>#4 and !2 and @alice</p>",
67 []string{"issues/4", "mrs/2", `href="/alice"`}, nil},
68 {"nested markup", "<ul><li>fixes #4</li><li><em>see !2</em></li></ul>",
69 []string{"issues/4", "mrs/2"}, nil},
70 {"punctuation after ref", "<p>(#4), and #4.</p>",
71 []string{`class="xref">#4</a>),`}, nil},
72 {"mention with trailing period", "<p>ask @alice.</p>",
73 []string{`class="xref">@alice</a>.`}, nil},
74 }
75 for _, tc := range cases {
76 t.Run(tc.name, func(t *testing.T) {
77 got := rw(t, tc.in)
78 for _, w := range tc.want {
79 if !strings.Contains(got, w) {
80 t.Errorf("missing %q in:\n%s", w, got)
81 }
82 }
83 for _, w := range tc.wantNot {
84 if strings.Contains(got, w) {
85 t.Errorf("unexpected %q in:\n%s", w, got)
86 }
87 }
88 })
89 }
90}
91
92func TestRewriteEscaping(t *testing.T) {
93 // Text around references must stay properly escaped after the
94 // parse/render round trip.
95 got := rw(t, "<p>x <script> #4 & done</p>")
96 if !strings.Contains(got, "<script>") || !strings.Contains(got, "& done") {
97 t.Fatalf("escaping lost:\n%s", got)
98 }
99 if !strings.Contains(got, "issues/4") {
100 t.Fatalf("ref not linked:\n%s", got)
101 }
102}