You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

references_test.go 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package references
  5. import (
  6. "testing"
  7. "code.gitea.io/gitea/modules/setting"
  8. "github.com/stretchr/testify/assert"
  9. )
  10. type testFixture struct {
  11. input string
  12. expected []testResult
  13. }
  14. type testResult struct {
  15. Index int64
  16. Owner string
  17. Name string
  18. Issue string
  19. Action XRefAction
  20. RefLocation *RefSpan
  21. ActionLocation *RefSpan
  22. }
  23. func TestFindAllIssueReferences(t *testing.T) {
  24. fixtures := []testFixture{
  25. {
  26. "Simply closes: #29 yes",
  27. []testResult{
  28. {29, "", "", "29", XRefActionCloses, &RefSpan{Start: 15, End: 18}, &RefSpan{Start: 7, End: 13}},
  29. },
  30. },
  31. {
  32. "#123 no, this is a title.",
  33. []testResult{},
  34. },
  35. {
  36. " #124 yes, this is a reference.",
  37. []testResult{
  38. {124, "", "", "124", XRefActionNone, &RefSpan{Start: 0, End: 4}, nil},
  39. },
  40. },
  41. {
  42. "```\nThis is a code block.\n#723 no, it's a code block.```",
  43. []testResult{},
  44. },
  45. {
  46. "This `#724` no, it's inline code.",
  47. []testResult{},
  48. },
  49. {
  50. "This user3/repo4#200 yes.",
  51. []testResult{
  52. {200, "user3", "repo4", "200", XRefActionNone, &RefSpan{Start: 5, End: 20}, nil},
  53. },
  54. },
  55. {
  56. "This [one](#919) no, this is a URL fragment.",
  57. []testResult{},
  58. },
  59. {
  60. "This [two](/user2/repo1/issues/921) yes.",
  61. []testResult{
  62. {921, "user2", "repo1", "921", XRefActionNone, nil, nil},
  63. },
  64. },
  65. {
  66. "This [three](/user2/repo1/pulls/922) yes.",
  67. []testResult{
  68. {922, "user2", "repo1", "922", XRefActionNone, nil, nil},
  69. },
  70. },
  71. {
  72. "This [four](http://gitea.com:3000/user3/repo4/issues/203) yes.",
  73. []testResult{
  74. {203, "user3", "repo4", "203", XRefActionNone, nil, nil},
  75. },
  76. },
  77. {
  78. "This [five](http://github.com/user3/repo4/issues/204) no.",
  79. []testResult{},
  80. },
  81. {
  82. "This http://gitea.com:3000/user4/repo5/201 no, bad URL.",
  83. []testResult{},
  84. },
  85. {
  86. "This http://gitea.com:3000/user4/repo5/pulls/202 yes.",
  87. []testResult{
  88. {202, "user4", "repo5", "202", XRefActionNone, nil, nil},
  89. },
  90. },
  91. {
  92. "This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.",
  93. []testResult{
  94. {205, "user4", "repo6", "205", XRefActionNone, nil, nil},
  95. },
  96. },
  97. {
  98. "Reopens #15 yes",
  99. []testResult{
  100. {15, "", "", "15", XRefActionReopens, &RefSpan{Start: 8, End: 11}, &RefSpan{Start: 0, End: 7}},
  101. },
  102. },
  103. {
  104. "This closes #20 for you yes",
  105. []testResult{
  106. {20, "", "", "20", XRefActionCloses, &RefSpan{Start: 12, End: 15}, &RefSpan{Start: 5, End: 11}},
  107. },
  108. },
  109. {
  110. "Do you fix user6/repo6#300 ? yes",
  111. []testResult{
  112. {300, "user6", "repo6", "300", XRefActionCloses, &RefSpan{Start: 11, End: 26}, &RefSpan{Start: 7, End: 10}},
  113. },
  114. },
  115. {
  116. "For 999 #1235 no keyword, but yes",
  117. []testResult{
  118. {1235, "", "", "1235", XRefActionNone, &RefSpan{Start: 8, End: 13}, nil},
  119. },
  120. },
  121. {
  122. "Which abc. #9434 same as above",
  123. []testResult{
  124. {9434, "", "", "9434", XRefActionNone, &RefSpan{Start: 11, End: 16}, nil},
  125. },
  126. },
  127. {
  128. "This closes #600 and reopens #599",
  129. []testResult{
  130. {600, "", "", "600", XRefActionCloses, &RefSpan{Start: 12, End: 16}, &RefSpan{Start: 5, End: 11}},
  131. {599, "", "", "599", XRefActionReopens, &RefSpan{Start: 29, End: 33}, &RefSpan{Start: 21, End: 28}},
  132. },
  133. },
  134. }
  135. testFixtures(t, fixtures, "default")
  136. type alnumFixture struct {
  137. input string
  138. issue string
  139. refLocation *RefSpan
  140. action XRefAction
  141. actionLocation *RefSpan
  142. }
  143. alnumFixtures := []alnumFixture{
  144. {
  145. "This ref ABC-123 is alphanumeric",
  146. "ABC-123", &RefSpan{Start: 9, End: 16},
  147. XRefActionNone, nil,
  148. },
  149. {
  150. "This closes ABCD-1234 alphanumeric",
  151. "ABCD-1234", &RefSpan{Start: 12, End: 21},
  152. XRefActionCloses, &RefSpan{Start: 5, End: 11},
  153. },
  154. }
  155. for _, fixture := range alnumFixtures {
  156. found, ref := FindRenderizableReferenceAlphanumeric(fixture.input)
  157. if fixture.issue == "" {
  158. assert.False(t, found, "Failed to parse: {%s}", fixture.input)
  159. } else {
  160. assert.True(t, found, "Failed to parse: {%s}", fixture.input)
  161. assert.Equal(t, fixture.issue, ref.Issue, "Failed to parse: {%s}", fixture.input)
  162. assert.Equal(t, fixture.refLocation, ref.RefLocation, "Failed to parse: {%s}", fixture.input)
  163. assert.Equal(t, fixture.action, ref.Action, "Failed to parse: {%s}", fixture.input)
  164. assert.Equal(t, fixture.actionLocation, ref.ActionLocation, "Failed to parse: {%s}", fixture.input)
  165. }
  166. }
  167. }
  168. func testFixtures(t *testing.T, fixtures []testFixture, context string) {
  169. // Save original value for other tests that may rely on it
  170. prevURL := setting.AppURL
  171. setting.AppURL = "https://gitea.com:3000/"
  172. for _, fixture := range fixtures {
  173. expraw := make([]*rawReference, len(fixture.expected))
  174. for i, e := range fixture.expected {
  175. expraw[i] = &rawReference{
  176. index: e.Index,
  177. owner: e.Owner,
  178. name: e.Name,
  179. action: e.Action,
  180. issue: e.Issue,
  181. refLocation: e.RefLocation,
  182. actionLocation: e.ActionLocation,
  183. }
  184. }
  185. expref := rawToIssueReferenceList(expraw)
  186. refs := FindAllIssueReferencesMarkdown(fixture.input)
  187. assert.EqualValues(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input)
  188. rawrefs := findAllIssueReferencesMarkdown(fixture.input)
  189. assert.EqualValues(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input)
  190. }
  191. // Restore for other tests that may rely on the original value
  192. setting.AppURL = prevURL
  193. }
  194. func TestRegExp_mentionPattern(t *testing.T) {
  195. trueTestCases := []struct {
  196. pat string
  197. exp string
  198. }{
  199. {"@Unknwon", "@Unknwon"},
  200. {"@ANT_123", "@ANT_123"},
  201. {"@xxx-DiN0-z-A..uru..s-xxx", "@xxx-DiN0-z-A..uru..s-xxx"},
  202. {" @lol ", "@lol"},
  203. {" @Te-st", "@Te-st"},
  204. {"(@gitea)", "@gitea"},
  205. {"[@gitea]", "@gitea"},
  206. {"@gitea! this", "@gitea"},
  207. {"@gitea? this", "@gitea"},
  208. {"@gitea. this", "@gitea"},
  209. {"@gitea, this", "@gitea"},
  210. {"@gitea; this", "@gitea"},
  211. {"@gitea!\nthis", "@gitea"},
  212. {"\n@gitea?\nthis", "@gitea"},
  213. {"\t@gitea.\nthis", "@gitea"},
  214. {"@gitea,\nthis", "@gitea"},
  215. {"@gitea;\nthis", "@gitea"},
  216. {"@gitea!", "@gitea"},
  217. {"@gitea?", "@gitea"},
  218. {"@gitea.", "@gitea"},
  219. {"@gitea,", "@gitea"},
  220. {"@gitea;", "@gitea"},
  221. }
  222. falseTestCases := []string{
  223. "@ 0",
  224. "@ ",
  225. "@",
  226. "",
  227. "ABC",
  228. "@.ABC",
  229. "/home/gitea/@gitea",
  230. "\"@gitea\"",
  231. "@@gitea",
  232. "@gitea!this",
  233. "@gitea?this",
  234. "@gitea,this",
  235. "@gitea;this",
  236. }
  237. for _, testCase := range trueTestCases {
  238. found := mentionPattern.FindStringSubmatch(testCase.pat)
  239. assert.Len(t, found, 2)
  240. assert.Equal(t, testCase.exp, found[1])
  241. }
  242. for _, testCase := range falseTestCases {
  243. res := mentionPattern.MatchString(testCase)
  244. assert.False(t, res, "[%s] should be false", testCase)
  245. }
  246. }
  247. func TestRegExp_issueNumericPattern(t *testing.T) {
  248. trueTestCases := []string{
  249. "#1234",
  250. "#0",
  251. "#1234567890987654321",
  252. " #12",
  253. "#12:",
  254. "ref: #12: msg",
  255. }
  256. falseTestCases := []string{
  257. "# 1234",
  258. "# 0",
  259. "# ",
  260. "#",
  261. "#ABC",
  262. "#1A2B",
  263. "",
  264. "ABC",
  265. }
  266. for _, testCase := range trueTestCases {
  267. assert.True(t, issueNumericPattern.MatchString(testCase))
  268. }
  269. for _, testCase := range falseTestCases {
  270. assert.False(t, issueNumericPattern.MatchString(testCase))
  271. }
  272. }
  273. func TestRegExp_issueAlphanumericPattern(t *testing.T) {
  274. trueTestCases := []string{
  275. "ABC-1234",
  276. "A-1",
  277. "RC-80",
  278. "ABCDEFGHIJ-1234567890987654321234567890",
  279. "ABC-123.",
  280. "(ABC-123)",
  281. "[ABC-123]",
  282. "ABC-123:",
  283. }
  284. falseTestCases := []string{
  285. "RC-08",
  286. "PR-0",
  287. "ABCDEFGHIJK-1",
  288. "PR_1",
  289. "",
  290. "#ABC",
  291. "",
  292. "ABC",
  293. "GG-",
  294. "rm-1",
  295. "/home/gitea/ABC-1234",
  296. "MY-STRING-ABC-123",
  297. }
  298. for _, testCase := range trueTestCases {
  299. assert.True(t, issueAlphanumericPattern.MatchString(testCase))
  300. }
  301. for _, testCase := range falseTestCases {
  302. assert.False(t, issueAlphanumericPattern.MatchString(testCase))
  303. }
  304. }
  305. func TestCustomizeCloseKeywords(t *testing.T) {
  306. fixtures := []testFixture{
  307. {
  308. "Simplemente cierra: #29 yes",
  309. []testResult{
  310. {29, "", "", "29", XRefActionCloses, &RefSpan{Start: 20, End: 23}, &RefSpan{Start: 12, End: 18}},
  311. },
  312. },
  313. {
  314. "Closes: #123 no, this English.",
  315. []testResult{
  316. {123, "", "", "123", XRefActionNone, &RefSpan{Start: 8, End: 12}, nil},
  317. },
  318. },
  319. {
  320. "Cerró user6/repo6#300 yes",
  321. []testResult{
  322. {300, "user6", "repo6", "300", XRefActionCloses, &RefSpan{Start: 7, End: 22}, &RefSpan{Start: 0, End: 6}},
  323. },
  324. },
  325. {
  326. "Reabre user3/repo4#200 yes",
  327. []testResult{
  328. {200, "user3", "repo4", "200", XRefActionReopens, &RefSpan{Start: 7, End: 22}, &RefSpan{Start: 0, End: 6}},
  329. },
  330. },
  331. }
  332. issueKeywordsOnce.Do(func() {})
  333. doNewKeywords([]string{"cierra", "cerró"}, []string{"reabre"})
  334. testFixtures(t, fixtures, "spanish")
  335. // Restore default settings
  336. doNewKeywords(setting.Repository.PullRequest.CloseKeywords, setting.Repository.PullRequest.ReopenKeywords)
  337. }
  338. func TestParseCloseKeywords(t *testing.T) {
  339. // Test parsing of CloseKeywords and ReopenKeywords
  340. assert.Len(t, parseKeywords([]string{""}), 0)
  341. assert.Len(t, parseKeywords([]string{" aa ", " bb ", "99", "#", "", "this is", "cc"}), 3)
  342. for _, test := range []struct {
  343. pattern string
  344. match string
  345. expected string
  346. }{
  347. {"close", "This PR will close ", "close"},
  348. {"cerró", "cerró ", "cerró"},
  349. {"cerró", "AQUÍ SE CERRÓ: ", "CERRÓ"},
  350. {"закрывается", "закрывается ", "закрывается"},
  351. {"κλείνει", "κλείνει: ", "κλείνει"},
  352. {"关闭", "关闭 ", "关闭"},
  353. {"閉じます", "閉じます ", "閉じます"},
  354. {",$!", "", ""},
  355. {"1234", "", ""},
  356. } {
  357. // The patern only needs to match the part that precedes the reference.
  358. // getCrossReference() takes care of finding the reference itself.
  359. pat := makeKeywordsPat([]string{test.pattern})
  360. if test.expected == "" {
  361. assert.Nil(t, pat)
  362. } else {
  363. assert.NotNil(t, pat)
  364. res := pat.FindAllStringSubmatch(test.match, -1)
  365. assert.Len(t, res, 1)
  366. assert.Len(t, res[0], 2)
  367. assert.EqualValues(t, test.expected, res[0][1])
  368. }
  369. }
  370. }