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

references_test.go 11 kB

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