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 12KB

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