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.

issue_test.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. // Copyright 2017 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 models
  5. import (
  6. "sort"
  7. "testing"
  8. "time"
  9. "github.com/stretchr/testify/assert"
  10. )
  11. func TestIssue_ReplaceLabels(t *testing.T) {
  12. assert.NoError(t, PrepareTestDatabase())
  13. testSuccess := func(issueID int64, labelIDs []int64) {
  14. issue := AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue)
  15. repo := AssertExistsAndLoadBean(t, &Repository{ID: issue.RepoID}).(*Repository)
  16. doer := AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User)
  17. labels := make([]*Label, len(labelIDs))
  18. for i, labelID := range labelIDs {
  19. labels[i] = AssertExistsAndLoadBean(t, &Label{ID: labelID, RepoID: repo.ID}).(*Label)
  20. }
  21. assert.NoError(t, issue.ReplaceLabels(labels, doer))
  22. AssertCount(t, &IssueLabel{IssueID: issueID}, len(labelIDs))
  23. for _, labelID := range labelIDs {
  24. AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issueID, LabelID: labelID})
  25. }
  26. }
  27. testSuccess(1, []int64{2})
  28. testSuccess(1, []int64{1, 2})
  29. testSuccess(1, []int64{})
  30. }
  31. func Test_GetIssueIDsByRepoID(t *testing.T) {
  32. assert.NoError(t, PrepareTestDatabase())
  33. ids, err := GetIssueIDsByRepoID(1)
  34. assert.NoError(t, err)
  35. assert.Len(t, ids, 5)
  36. }
  37. func TestIssueAPIURL(t *testing.T) {
  38. assert.NoError(t, PrepareTestDatabase())
  39. issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
  40. err := issue.LoadAttributes()
  41. assert.NoError(t, err)
  42. assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/issues/1", issue.APIURL())
  43. }
  44. func TestGetIssuesByIDs(t *testing.T) {
  45. assert.NoError(t, PrepareTestDatabase())
  46. testSuccess := func(expectedIssueIDs, nonExistentIssueIDs []int64) {
  47. issues, err := GetIssuesByIDs(append(expectedIssueIDs, nonExistentIssueIDs...))
  48. assert.NoError(t, err)
  49. actualIssueIDs := make([]int64, len(issues))
  50. for i, issue := range issues {
  51. actualIssueIDs[i] = issue.ID
  52. }
  53. assert.Equal(t, expectedIssueIDs, actualIssueIDs)
  54. }
  55. testSuccess([]int64{1, 2, 3}, []int64{})
  56. testSuccess([]int64{1, 2, 3}, []int64{NonexistentID})
  57. }
  58. func TestGetParticipantIDsByIssue(t *testing.T) {
  59. assert.NoError(t, PrepareTestDatabase())
  60. checkParticipants := func(issueID int64, userIDs []int) {
  61. issue, err := GetIssueByID(issueID)
  62. assert.NoError(t, err)
  63. participants, err := issue.getParticipantIDsByIssue(x)
  64. if assert.NoError(t, err) {
  65. participantsIDs := make([]int, len(participants))
  66. for i, uid := range participants {
  67. participantsIDs[i] = int(uid)
  68. }
  69. sort.Ints(participantsIDs)
  70. sort.Ints(userIDs)
  71. assert.Equal(t, userIDs, participantsIDs)
  72. }
  73. }
  74. // User 1 is issue1 poster (see fixtures/issue.yml)
  75. // User 2 only labeled issue1 (see fixtures/comment.yml)
  76. // Users 3 and 5 made actual comments (see fixtures/comment.yml)
  77. // User 3 is inactive, thus not active participant
  78. checkParticipants(1, []int{1, 5})
  79. }
  80. func TestIssue_ClearLabels(t *testing.T) {
  81. tests := []struct {
  82. issueID int64
  83. doerID int64
  84. }{
  85. {1, 2}, // non-pull-request, has labels
  86. {2, 2}, // pull-request, has labels
  87. {3, 2}, // pull-request, has no labels
  88. }
  89. for _, test := range tests {
  90. assert.NoError(t, PrepareTestDatabase())
  91. issue := AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue)
  92. doer := AssertExistsAndLoadBean(t, &User{ID: test.doerID}).(*User)
  93. assert.NoError(t, issue.ClearLabels(doer))
  94. AssertNotExistsBean(t, &IssueLabel{IssueID: test.issueID})
  95. }
  96. }
  97. func TestUpdateIssueCols(t *testing.T) {
  98. assert.NoError(t, PrepareTestDatabase())
  99. issue := AssertExistsAndLoadBean(t, &Issue{}).(*Issue)
  100. const newTitle = "New Title for unit test"
  101. issue.Title = newTitle
  102. prevContent := issue.Content
  103. issue.Content = "This should have no effect"
  104. now := time.Now().Unix()
  105. assert.NoError(t, updateIssueCols(x, issue, "name"))
  106. then := time.Now().Unix()
  107. updatedIssue := AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
  108. assert.EqualValues(t, newTitle, updatedIssue.Title)
  109. assert.EqualValues(t, prevContent, updatedIssue.Content)
  110. AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
  111. }
  112. func TestIssues(t *testing.T) {
  113. assert.NoError(t, PrepareTestDatabase())
  114. for _, test := range []struct {
  115. Opts IssuesOptions
  116. ExpectedIssueIDs []int64
  117. }{
  118. {
  119. IssuesOptions{
  120. AssigneeID: 1,
  121. SortType: "oldest",
  122. },
  123. []int64{1, 6},
  124. },
  125. {
  126. IssuesOptions{
  127. RepoIDs: []int64{1, 3},
  128. SortType: "oldest",
  129. ListOptions: ListOptions{
  130. Page: 1,
  131. PageSize: 4,
  132. },
  133. },
  134. []int64{1, 2, 3, 5},
  135. },
  136. {
  137. IssuesOptions{
  138. LabelIDs: []int64{1},
  139. ListOptions: ListOptions{
  140. Page: 1,
  141. PageSize: 4,
  142. },
  143. },
  144. []int64{2, 1},
  145. },
  146. {
  147. IssuesOptions{
  148. LabelIDs: []int64{1, 2},
  149. ListOptions: ListOptions{
  150. Page: 1,
  151. PageSize: 4,
  152. },
  153. },
  154. []int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
  155. },
  156. } {
  157. issues, err := Issues(&test.Opts)
  158. assert.NoError(t, err)
  159. if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
  160. for i, issue := range issues {
  161. assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID)
  162. }
  163. }
  164. }
  165. }
  166. func TestGetUserIssueStats(t *testing.T) {
  167. assert.NoError(t, PrepareTestDatabase())
  168. for _, test := range []struct {
  169. Opts UserIssueStatsOptions
  170. ExpectedIssueStats IssueStats
  171. }{
  172. {
  173. UserIssueStatsOptions{
  174. UserID: 1,
  175. RepoIDs: []int64{1},
  176. FilterMode: FilterModeAll,
  177. },
  178. IssueStats{
  179. YourRepositoriesCount: 0,
  180. AssignCount: 1,
  181. CreateCount: 1,
  182. OpenCount: 0,
  183. ClosedCount: 0,
  184. },
  185. },
  186. {
  187. UserIssueStatsOptions{
  188. UserID: 1,
  189. FilterMode: FilterModeAssign,
  190. },
  191. IssueStats{
  192. YourRepositoriesCount: 0,
  193. AssignCount: 2,
  194. CreateCount: 2,
  195. OpenCount: 2,
  196. ClosedCount: 0,
  197. },
  198. },
  199. {
  200. UserIssueStatsOptions{
  201. UserID: 1,
  202. FilterMode: FilterModeCreate,
  203. },
  204. IssueStats{
  205. YourRepositoriesCount: 0,
  206. AssignCount: 2,
  207. CreateCount: 2,
  208. OpenCount: 2,
  209. ClosedCount: 0,
  210. },
  211. },
  212. {
  213. UserIssueStatsOptions{
  214. UserID: 2,
  215. UserRepoIDs: []int64{1, 2},
  216. FilterMode: FilterModeAll,
  217. IsClosed: true,
  218. },
  219. IssueStats{
  220. YourRepositoriesCount: 2,
  221. AssignCount: 0,
  222. CreateCount: 2,
  223. OpenCount: 2,
  224. ClosedCount: 2,
  225. },
  226. },
  227. {
  228. UserIssueStatsOptions{
  229. UserID: 1,
  230. FilterMode: FilterModeMention,
  231. },
  232. IssueStats{
  233. YourRepositoriesCount: 0,
  234. AssignCount: 2,
  235. CreateCount: 2,
  236. OpenCount: 0,
  237. ClosedCount: 0,
  238. },
  239. },
  240. {
  241. UserIssueStatsOptions{
  242. UserID: 1,
  243. FilterMode: FilterModeCreate,
  244. IssueIDs: []int64{1},
  245. },
  246. IssueStats{
  247. YourRepositoriesCount: 0,
  248. AssignCount: 1,
  249. CreateCount: 1,
  250. OpenCount: 1,
  251. ClosedCount: 0,
  252. },
  253. },
  254. } {
  255. stats, err := GetUserIssueStats(test.Opts)
  256. if !assert.NoError(t, err) {
  257. continue
  258. }
  259. assert.Equal(t, test.ExpectedIssueStats, *stats)
  260. }
  261. }
  262. func TestIssue_loadTotalTimes(t *testing.T) {
  263. assert.NoError(t, PrepareTestDatabase())
  264. ms, err := GetIssueByID(2)
  265. assert.NoError(t, err)
  266. assert.NoError(t, ms.loadTotalTimes(x))
  267. assert.Equal(t, int64(3682), ms.TotalTrackedTime)
  268. }
  269. func TestIssue_SearchIssueIDsByKeyword(t *testing.T) {
  270. assert.NoError(t, PrepareTestDatabase())
  271. total, ids, err := SearchIssueIDsByKeyword("issue2", []int64{1}, 10, 0)
  272. assert.NoError(t, err)
  273. assert.EqualValues(t, 1, total)
  274. assert.EqualValues(t, []int64{2}, ids)
  275. total, ids, err = SearchIssueIDsByKeyword("first", []int64{1}, 10, 0)
  276. assert.NoError(t, err)
  277. assert.EqualValues(t, 1, total)
  278. assert.EqualValues(t, []int64{1}, ids)
  279. total, ids, err = SearchIssueIDsByKeyword("for", []int64{1}, 10, 0)
  280. assert.NoError(t, err)
  281. assert.EqualValues(t, 5, total)
  282. assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids)
  283. // issue1's comment id 2
  284. total, ids, err = SearchIssueIDsByKeyword("good", []int64{1}, 10, 0)
  285. assert.NoError(t, err)
  286. assert.EqualValues(t, 1, total)
  287. assert.EqualValues(t, []int64{1}, ids)
  288. }
  289. func TestGetRepoIDsForIssuesOptions(t *testing.T) {
  290. assert.NoError(t, PrepareTestDatabase())
  291. user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
  292. for _, test := range []struct {
  293. Opts IssuesOptions
  294. ExpectedRepoIDs []int64
  295. }{
  296. {
  297. IssuesOptions{
  298. AssigneeID: 2,
  299. },
  300. []int64{3},
  301. },
  302. {
  303. IssuesOptions{
  304. RepoIDs: []int64{1, 2},
  305. },
  306. []int64{1, 2},
  307. },
  308. } {
  309. repoIDs, err := GetRepoIDsForIssuesOptions(&test.Opts, user)
  310. assert.NoError(t, err)
  311. if assert.Len(t, repoIDs, len(test.ExpectedRepoIDs)) {
  312. for i, repoID := range repoIDs {
  313. assert.EqualValues(t, test.ExpectedRepoIDs[i], repoID)
  314. }
  315. }
  316. }
  317. }
  318. func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *Issue {
  319. var newIssue Issue
  320. t.Run(title, func(t *testing.T) {
  321. repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
  322. user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
  323. issue := Issue{
  324. RepoID: repo.ID,
  325. PosterID: user.ID,
  326. Title: title,
  327. Content: content,
  328. }
  329. err := NewIssue(repo, &issue, nil, nil)
  330. assert.NoError(t, err)
  331. has, err := x.ID(issue.ID).Get(&newIssue)
  332. assert.NoError(t, err)
  333. assert.True(t, has)
  334. assert.EqualValues(t, issue.Title, newIssue.Title)
  335. assert.EqualValues(t, issue.Content, newIssue.Content)
  336. if expectIndex > 0 {
  337. assert.EqualValues(t, expectIndex, newIssue.Index)
  338. }
  339. })
  340. return &newIssue
  341. }
  342. func TestIssue_InsertIssue(t *testing.T) {
  343. assert.NoError(t, PrepareTestDatabase())
  344. // there are 5 issues and max index is 5 on repository 1, so this one should 6
  345. issue := testInsertIssue(t, "my issue1", "special issue's comments?", 6)
  346. _, err := x.ID(issue.ID).Delete(new(Issue))
  347. assert.NoError(t, err)
  348. issue = testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?", 7)
  349. _, err = x.ID(issue.ID).Delete(new(Issue))
  350. assert.NoError(t, err)
  351. }
  352. func TestIssue_ResolveMentions(t *testing.T) {
  353. assert.NoError(t, PrepareTestDatabase())
  354. testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
  355. o := AssertExistsAndLoadBean(t, &User{LowerName: owner}).(*User)
  356. r := AssertExistsAndLoadBean(t, &Repository{OwnerID: o.ID, LowerName: repo}).(*Repository)
  357. issue := &Issue{RepoID: r.ID}
  358. d := AssertExistsAndLoadBean(t, &User{LowerName: doer}).(*User)
  359. resolved, err := issue.ResolveMentionsByVisibility(DefaultDBContext(), d, mentions)
  360. assert.NoError(t, err)
  361. ids := make([]int64, len(resolved))
  362. for i, user := range resolved {
  363. ids[i] = user.ID
  364. }
  365. sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
  366. assert.EqualValues(t, expected, ids)
  367. }
  368. // Public repo, existing user
  369. testSuccess("user2", "repo1", "user1", []string{"user5"}, []int64{5})
  370. // Public repo, non-existing user
  371. testSuccess("user2", "repo1", "user1", []string{"nonexisting"}, []int64{})
  372. // Public repo, doer
  373. testSuccess("user2", "repo1", "user1", []string{"user1"}, []int64{})
  374. // Private repo, team member
  375. testSuccess("user17", "big_test_private_4", "user20", []string{"user2"}, []int64{2})
  376. // Private repo, not a team member
  377. testSuccess("user17", "big_test_private_4", "user20", []string{"user5"}, []int64{})
  378. // Private repo, whole team
  379. testSuccess("user17", "big_test_private_4", "user15", []string{"user17/owners"}, []int64{18})
  380. }