gitech
560 строк · 15.0 Кб
1// Copyright 2019 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package references
5
6import (
7"regexp"
8"testing"
9
10"code.gitea.io/gitea/modules/setting"
11
12"github.com/stretchr/testify/assert"
13)
14
15type testFixture struct {
16input string
17expected []testResult
18}
19
20type testResult struct {
21Index int64
22Owner string
23Name string
24Issue string
25IsPull bool
26Action XRefAction
27RefLocation *RefSpan
28ActionLocation *RefSpan
29TimeLog string
30}
31
32func TestConvertFullHTMLReferencesToShortRefs(t *testing.T) {
33re := regexp.MustCompile(`(\s|^|\(|\[)` +
34regexp.QuoteMeta("https://ourgitea.com/git/") +
35`([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+)/` +
36`((?:issues)|(?:pulls))/([0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
37test := `this is a https://ourgitea.com/git/owner/repo/issues/123456789, foo
38https://ourgitea.com/git/owner/repo/pulls/123456789
39And https://ourgitea.com/git/owner/repo/pulls/123
40`
41expect := `this is a owner/repo#123456789, foo
42owner/repo!123456789
43And owner/repo!123
44`
45
46contentBytes := []byte(test)
47convertFullHTMLReferencesToShortRefs(re, &contentBytes)
48result := string(contentBytes)
49assert.EqualValues(t, expect, result)
50}
51
52func TestFindAllIssueReferences(t *testing.T) {
53fixtures := []testFixture{
54{
55"Simply closes: #29 yes",
56[]testResult{
57{29, "", "", "29", false, XRefActionCloses, &RefSpan{Start: 15, End: 18}, &RefSpan{Start: 7, End: 13}, ""},
58},
59},
60{
61"Simply closes: !29 yes",
62[]testResult{
63{29, "", "", "29", true, XRefActionCloses, &RefSpan{Start: 15, End: 18}, &RefSpan{Start: 7, End: 13}, ""},
64},
65},
66{
67" #124 yes, this is a reference.",
68[]testResult{
69{124, "", "", "124", false, XRefActionNone, &RefSpan{Start: 0, End: 4}, nil, ""},
70},
71},
72{
73"```\nThis is a code block.\n#723 no, it's a code block.```",
74[]testResult{},
75},
76{
77"This `#724` no, it's inline code.",
78[]testResult{},
79},
80{
81"This org3/repo4#200 yes.",
82[]testResult{
83{200, "org3", "repo4", "200", false, XRefActionNone, &RefSpan{Start: 5, End: 19}, nil, ""},
84},
85},
86{
87"This org3/repo4!200 yes.",
88[]testResult{
89{200, "org3", "repo4", "200", true, XRefActionNone, &RefSpan{Start: 5, End: 19}, nil, ""},
90},
91},
92{
93"This [one](#919) no, this is a URL fragment.",
94[]testResult{},
95},
96{
97"This [two](/user2/repo1/issues/921) yes.",
98[]testResult{
99{921, "user2", "repo1", "921", false, XRefActionNone, nil, nil, ""},
100},
101},
102{
103"This [three](/user2/repo1/pulls/922) yes.",
104[]testResult{
105{922, "user2", "repo1", "922", true, XRefActionNone, nil, nil, ""},
106},
107},
108{
109"This [four](http://gitea.com:3000/org3/repo4/issues/203) yes.",
110[]testResult{
111{203, "org3", "repo4", "203", false, XRefActionNone, nil, nil, ""},
112},
113},
114{
115"This [five](http://github.com/org3/repo4/issues/204) no.",
116[]testResult{},
117},
118{
119"This http://gitea.com:3000/user4/repo5/201 no, bad URL.",
120[]testResult{},
121},
122{
123"This http://gitea.com:3000/user4/repo5/pulls/202 yes.",
124[]testResult{
125{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
126},
127},
128{
129"This http://gitea.com:3000/user4/repo5/pulls/202 yes. http://gitea.com:3000/user4/repo5/pulls/203 no",
130[]testResult{
131{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
132{203, "user4", "repo5", "203", true, XRefActionNone, nil, nil, ""},
133},
134},
135{
136"This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.",
137[]testResult{
138{205, "user4", "repo6", "205", true, XRefActionNone, nil, nil, ""},
139},
140},
141{
142"Reopens #15 yes",
143[]testResult{
144{15, "", "", "15", false, XRefActionReopens, &RefSpan{Start: 8, End: 11}, &RefSpan{Start: 0, End: 7}, ""},
145},
146},
147{
148"This closes #20 for you yes",
149[]testResult{
150{20, "", "", "20", false, XRefActionCloses, &RefSpan{Start: 12, End: 15}, &RefSpan{Start: 5, End: 11}, ""},
151},
152},
153{
154"Do you fix org6/repo6#300 ? yes",
155[]testResult{
156{300, "org6", "repo6", "300", false, XRefActionCloses, &RefSpan{Start: 11, End: 25}, &RefSpan{Start: 7, End: 10}, ""},
157},
158},
159{
160"For 999 #1235 no keyword, but yes",
161[]testResult{
162{1235, "", "", "1235", false, XRefActionNone, &RefSpan{Start: 8, End: 13}, nil, ""},
163},
164},
165{
166"For [!123] yes",
167[]testResult{
168{123, "", "", "123", true, XRefActionNone, &RefSpan{Start: 5, End: 9}, nil, ""},
169},
170},
171{
172"For (#345) yes",
173[]testResult{
174{345, "", "", "345", false, XRefActionNone, &RefSpan{Start: 5, End: 9}, nil, ""},
175},
176},
177{
178"For #22,#23 no, neither #28:#29 or !30!31#32;33 should",
179[]testResult{},
180},
181{
182"For #24, and #25. yes; also #26; #27? #28! and #29: should",
183[]testResult{
184{24, "", "", "24", false, XRefActionNone, &RefSpan{Start: 4, End: 7}, nil, ""},
185{25, "", "", "25", false, XRefActionNone, &RefSpan{Start: 13, End: 16}, nil, ""},
186{26, "", "", "26", false, XRefActionNone, &RefSpan{Start: 28, End: 31}, nil, ""},
187{27, "", "", "27", false, XRefActionNone, &RefSpan{Start: 33, End: 36}, nil, ""},
188{28, "", "", "28", false, XRefActionNone, &RefSpan{Start: 38, End: 41}, nil, ""},
189{29, "", "", "29", false, XRefActionNone, &RefSpan{Start: 47, End: 50}, nil, ""},
190},
191},
192{
193"This org3/repo4#200, yes.",
194[]testResult{
195{200, "org3", "repo4", "200", false, XRefActionNone, &RefSpan{Start: 5, End: 19}, nil, ""},
196},
197},
198{
199"Merge pull request '#12345 My fix for a bug' (!1337) from feature-branch into main",
200[]testResult{
201{12345, "", "", "12345", false, XRefActionNone, &RefSpan{Start: 20, End: 26}, nil, ""},
202{1337, "", "", "1337", true, XRefActionNone, &RefSpan{Start: 46, End: 51}, nil, ""},
203},
204},
205{
206"Which abc. #9434 same as above",
207[]testResult{
208{9434, "", "", "9434", false, XRefActionNone, &RefSpan{Start: 11, End: 16}, nil, ""},
209},
210},
211{
212"This closes #600 and reopens #599",
213[]testResult{
214{600, "", "", "600", false, XRefActionCloses, &RefSpan{Start: 12, End: 16}, &RefSpan{Start: 5, End: 11}, ""},
215{599, "", "", "599", false, XRefActionReopens, &RefSpan{Start: 29, End: 33}, &RefSpan{Start: 21, End: 28}, ""},
216},
217},
218{
219"This fixes #100 spent @40m and reopens #101, also fixes #102 spent @4h15m",
220[]testResult{
221{100, "", "", "100", false, XRefActionCloses, &RefSpan{Start: 11, End: 15}, &RefSpan{Start: 5, End: 10}, "40m"},
222{101, "", "", "101", false, XRefActionReopens, &RefSpan{Start: 39, End: 43}, &RefSpan{Start: 31, End: 38}, ""},
223{102, "", "", "102", false, XRefActionCloses, &RefSpan{Start: 56, End: 60}, &RefSpan{Start: 50, End: 55}, "4h15m"},
224},
225},
226}
227
228testFixtures(t, fixtures, "default")
229
230type alnumFixture struct {
231input string
232issue string
233refLocation *RefSpan
234action XRefAction
235actionLocation *RefSpan
236}
237
238alnumFixtures := []alnumFixture{
239{
240"This ref ABC-123 is alphanumeric",
241"ABC-123", &RefSpan{Start: 9, End: 16},
242XRefActionNone, nil,
243},
244{
245"This closes ABCD-1234 alphanumeric",
246"ABCD-1234", &RefSpan{Start: 12, End: 21},
247XRefActionCloses, &RefSpan{Start: 5, End: 11},
248},
249}
250
251for _, fixture := range alnumFixtures {
252found, ref := FindRenderizableReferenceAlphanumeric(fixture.input)
253if fixture.issue == "" {
254assert.False(t, found, "Failed to parse: {%s}", fixture.input)
255} else {
256assert.True(t, found, "Failed to parse: {%s}", fixture.input)
257assert.Equal(t, fixture.issue, ref.Issue, "Failed to parse: {%s}", fixture.input)
258assert.Equal(t, fixture.refLocation, ref.RefLocation, "Failed to parse: {%s}", fixture.input)
259assert.Equal(t, fixture.action, ref.Action, "Failed to parse: {%s}", fixture.input)
260assert.Equal(t, fixture.actionLocation, ref.ActionLocation, "Failed to parse: {%s}", fixture.input)
261}
262}
263}
264
265func testFixtures(t *testing.T, fixtures []testFixture, context string) {
266// Save original value for other tests that may rely on it
267prevURL := setting.AppURL
268setting.AppURL = "https://gitea.com:3000/"
269
270for _, fixture := range fixtures {
271expraw := make([]*rawReference, len(fixture.expected))
272for i, e := range fixture.expected {
273expraw[i] = &rawReference{
274index: e.Index,
275owner: e.Owner,
276name: e.Name,
277isPull: e.IsPull,
278action: e.Action,
279issue: e.Issue,
280refLocation: e.RefLocation,
281actionLocation: e.ActionLocation,
282timeLog: e.TimeLog,
283}
284}
285expref := rawToIssueReferenceList(expraw)
286refs := FindAllIssueReferencesMarkdown(fixture.input)
287assert.EqualValues(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input)
288rawrefs := findAllIssueReferencesMarkdown(fixture.input)
289assert.EqualValues(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input)
290}
291
292// Restore for other tests that may rely on the original value
293setting.AppURL = prevURL
294}
295
296func TestFindAllMentions(t *testing.T) {
297res := FindAllMentionsBytes([]byte("@tasha, @mike; @lucy: @john"))
298assert.EqualValues(t, []RefSpan{
299{Start: 0, End: 6},
300{Start: 8, End: 13},
301{Start: 15, End: 20},
302{Start: 22, End: 27},
303}, res)
304}
305
306func TestFindRenderizableCommitCrossReference(t *testing.T) {
307cases := []struct {
308Input string
309Expected *RenderizableReference
310}{
311{
312Input: "",
313Expected: nil,
314},
315{
316Input: "test",
317Expected: nil,
318},
319{
320Input: "go-gitea/gitea@test",
321Expected: nil,
322},
323{
324Input: "go-gitea/gitea@ab1234",
325Expected: nil,
326},
327{
328Input: "go-gitea/gitea@abcd1234",
329Expected: &RenderizableReference{
330Owner: "go-gitea",
331Name: "gitea",
332CommitSha: "abcd1234",
333RefLocation: &RefSpan{Start: 0, End: 23},
334},
335},
336{
337Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd1234",
338Expected: &RenderizableReference{
339Owner: "go-gitea",
340Name: "gitea",
341CommitSha: "abcd1234abcd1234abcd1234abcd1234abcd1234",
342RefLocation: &RefSpan{Start: 0, End: 55},
343},
344},
345{
346Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd12341234512345123451234512345", // longer than 64 characters
347Expected: nil,
348},
349{
350Input: "test go-gitea/gitea@abcd1234 test",
351Expected: &RenderizableReference{
352Owner: "go-gitea",
353Name: "gitea",
354CommitSha: "abcd1234",
355RefLocation: &RefSpan{Start: 5, End: 28},
356},
357},
358}
359
360for _, c := range cases {
361found, ref := FindRenderizableCommitCrossReference(c.Input)
362assert.Equal(t, ref != nil, found)
363assert.Equal(t, c.Expected, ref)
364}
365}
366
367func TestRegExp_mentionPattern(t *testing.T) {
368trueTestCases := []struct {
369pat string
370exp string
371}{
372{"@User", "@User"},
373{"@ANT_123", "@ANT_123"},
374{"@xxx-DiN0-z-A..uru..s-xxx", "@xxx-DiN0-z-A..uru..s-xxx"},
375{" @lol ", "@lol"},
376{" @Te-st", "@Te-st"},
377{"(@gitea)", "@gitea"},
378{"[@gitea]", "@gitea"},
379{"@gitea! this", "@gitea"},
380{"@gitea? this", "@gitea"},
381{"@gitea. this", "@gitea"},
382{"@gitea, this", "@gitea"},
383{"@gitea; this", "@gitea"},
384{"@gitea!\nthis", "@gitea"},
385{"\n@gitea?\nthis", "@gitea"},
386{"\t@gitea.\nthis", "@gitea"},
387{"@gitea,\nthis", "@gitea"},
388{"@gitea;\nthis", "@gitea"},
389{"@gitea!", "@gitea"},
390{"@gitea?", "@gitea"},
391{"@gitea.", "@gitea"},
392{"@gitea,", "@gitea"},
393{"@gitea;", "@gitea"},
394{"@gitea/team1;", "@gitea/team1"},
395}
396falseTestCases := []string{
397"@ 0",
398"@ ",
399"@",
400"",
401"ABC",
402"@.ABC",
403"/home/gitea/@gitea",
404"\"@gitea\"",
405"@@gitea",
406"@gitea!this",
407"@gitea?this",
408"@gitea,this",
409"@gitea;this",
410"@gitea/team1/more",
411}
412
413for _, testCase := range trueTestCases {
414found := mentionPattern.FindStringSubmatch(testCase.pat)
415assert.Len(t, found, 2)
416assert.Equal(t, testCase.exp, found[1])
417}
418for _, testCase := range falseTestCases {
419res := mentionPattern.MatchString(testCase)
420assert.False(t, res, "[%s] should be false", testCase)
421}
422}
423
424func TestRegExp_issueNumericPattern(t *testing.T) {
425trueTestCases := []string{
426"#1234",
427"#0",
428"#1234567890987654321",
429" #12",
430"#12:",
431"ref: #12: msg",
432"\"#1234\"",
433"'#1234'",
434}
435falseTestCases := []string{
436"# 1234",
437"# 0",
438"# ",
439"#",
440"#ABC",
441"#1A2B",
442"",
443"ABC",
444}
445
446for _, testCase := range trueTestCases {
447assert.True(t, issueNumericPattern.MatchString(testCase))
448}
449for _, testCase := range falseTestCases {
450assert.False(t, issueNumericPattern.MatchString(testCase))
451}
452}
453
454func TestRegExp_issueAlphanumericPattern(t *testing.T) {
455trueTestCases := []string{
456"ABC-1234",
457"A-1",
458"RC-80",
459"ABCDEFGHIJ-1234567890987654321234567890",
460"ABC-123.",
461"(ABC-123)",
462"[ABC-123]",
463"ABC-123:",
464"\"ABC-123\"",
465"'ABC-123'",
466}
467falseTestCases := []string{
468"RC-08",
469"PR-0",
470"ABCDEFGHIJK-1",
471"PR_1",
472"",
473"#ABC",
474"",
475"ABC",
476"GG-",
477"rm-1",
478"/home/gitea/ABC-1234",
479"MY-STRING-ABC-123",
480}
481
482for _, testCase := range trueTestCases {
483assert.True(t, issueAlphanumericPattern.MatchString(testCase))
484}
485for _, testCase := range falseTestCases {
486assert.False(t, issueAlphanumericPattern.MatchString(testCase))
487}
488}
489
490func TestCustomizeCloseKeywords(t *testing.T) {
491fixtures := []testFixture{
492{
493"Simplemente cierra: #29 yes",
494[]testResult{
495{29, "", "", "29", false, XRefActionCloses, &RefSpan{Start: 20, End: 23}, &RefSpan{Start: 12, End: 18}, ""},
496},
497},
498{
499"Closes: #123 no, this English.",
500[]testResult{
501{123, "", "", "123", false, XRefActionNone, &RefSpan{Start: 8, End: 12}, nil, ""},
502},
503},
504{
505"Cerró org6/repo6#300 yes",
506[]testResult{
507{300, "org6", "repo6", "300", false, XRefActionCloses, &RefSpan{Start: 7, End: 21}, &RefSpan{Start: 0, End: 6}, ""},
508},
509},
510{
511"Reabre org3/repo4#200 yes",
512[]testResult{
513{200, "org3", "repo4", "200", false, XRefActionReopens, &RefSpan{Start: 7, End: 21}, &RefSpan{Start: 0, End: 6}, ""},
514},
515},
516}
517
518issueKeywordsOnce.Do(func() {})
519
520doNewKeywords([]string{"cierra", "cerró"}, []string{"reabre"})
521testFixtures(t, fixtures, "spanish")
522
523// Restore default settings
524doNewKeywords(setting.Repository.PullRequest.CloseKeywords, setting.Repository.PullRequest.ReopenKeywords)
525}
526
527func TestParseCloseKeywords(t *testing.T) {
528// Test parsing of CloseKeywords and ReopenKeywords
529assert.Len(t, parseKeywords([]string{""}), 0)
530assert.Len(t, parseKeywords([]string{" aa ", " bb ", "99", "#", "", "this is", "cc"}), 3)
531
532for _, test := range []struct {
533pattern string
534match string
535expected string
536}{
537{"close", "This PR will close ", "close"},
538{"cerró", "cerró ", "cerró"},
539{"cerró", "AQUÍ SE CERRÓ: ", "CERRÓ"},
540{"закрывается", "закрывается ", "закрывается"},
541{"κλείνει", "κλείνει: ", "κλείνει"},
542{"关闭", "关闭 ", "关闭"},
543{"閉じます", "閉じます ", "閉じます"},
544{",$!", "", ""},
545{"1234", "", ""},
546} {
547// The pattern only needs to match the part that precedes the reference.
548// getCrossReference() takes care of finding the reference itself.
549pat := makeKeywordsPat([]string{test.pattern})
550if test.expected == "" {
551assert.Nil(t, pat)
552} else {
553assert.NotNil(t, pat)
554res := pat.FindAllStringSubmatch(test.match, -1)
555assert.Len(t, res, 1)
556assert.Len(t, res[0], 2)
557assert.EqualValues(t, test.expected, res[0][1])
558}
559}
560}
561