Go · 2489 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package issues
4
5 import (
6 "reflect"
7 "sort"
8 "testing"
9 )
10
11 // findSameRepoRefs runs the regex used by the same-repo branch and
12 // returns the parsed numbers. It mirrors the production loop modulo the
13 // DB lookup, so the regex behavior can be unit-tested without a DB.
14 func findSameRepoRefs(body string) []string {
15 stripped := reCrossRepoIssueRef.ReplaceAllString(body, " ")
16 out := []string{}
17 for _, m := range reSameRepoIssueRef.FindAllStringSubmatch(stripped, -1) {
18 out = append(out, m[1])
19 }
20 return out
21 }
22
23 func findCrossRepoRefs(body string) [][3]string {
24 out := [][3]string{}
25 for _, m := range reCrossRepoIssueRef.FindAllStringSubmatch(body, -1) {
26 out = append(out, [3]string{m[1], m[2], m[3]})
27 }
28 return out
29 }
30
31 func TestSameRepoRefRegex(t *testing.T) {
32 t.Parallel()
33 cases := []struct {
34 name string
35 body string
36 want []string
37 }{
38 {"plain", "fixes #1 and #42", []string{"1", "42"}},
39 {"sentence_start", "#7 should land", []string{"7"}},
40 {"no_word_prefix", "abc#1 isn't a ref", []string{}},
41 {"trailing_punct", "see #3, please", []string{"3"}},
42 {"line_start", "Done.\n#5 next", []string{"5"}},
43 // owner/repo#N must NOT also produce a same-repo hit on the #N
44 // portion; the cross-repo regex strips the whole token first.
45 {"cross_repo_excluded", "alice/repo#9 only", []string{}},
46 }
47 for _, c := range cases {
48 t.Run(c.name, func(t *testing.T) {
49 got := findSameRepoRefs(c.body)
50 sort.Strings(got)
51 sort.Strings(c.want)
52 if !reflect.DeepEqual(got, c.want) {
53 t.Errorf("body %q: got %v, want %v", c.body, got, c.want)
54 }
55 })
56 }
57 }
58
59 func TestCrossRepoRefRegex(t *testing.T) {
60 t.Parallel()
61 got := findCrossRepoRefs("see alice/proj#3 and bob/lib#42 for context, but not just #1")
62 want := [][3]string{
63 {"alice", "proj", "3"},
64 {"bob", "lib", "42"},
65 }
66 if !reflect.DeepEqual(got, want) {
67 t.Errorf("got %v, want %v", got, want)
68 }
69 }
70
71 func TestExtractMentions(t *testing.T) {
72 t.Parallel()
73 got := extractMentions("hi @alice and @bob, also @alice again — but not foo@example.com or a@b")
74 // `a@b` doesn't match (single-char user is fine but there's no
75 // leading word-boundary punctuation that's not a `@` itself, and
76 // our regex requires the `@` to be preceded by a non-word char or
77 // the start of input — `b a@b` would match `b` itself but `b@`
78 // fails the regex).
79 want := []string{"alice", "bob"}
80 if !reflect.DeepEqual(got, want) {
81 t.Errorf("got %v, want %v", got, want)
82 }
83 }
84