internal/autolink/autolink_test.go

a733a81313bf75b49fc786e6ff21cbfb58d70bae
gitbay/internal/autolink/autolink_test.go history · blame · raw

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 &lt;script&gt; #4 &amp; done</p>")
 96	if !strings.Contains(got, "&lt;script&gt;") || !strings.Contains(got, "&amp; 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}