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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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. "context"
  7. "fmt"
  8. "sort"
  9. "strconv"
  10. "sync"
  11. "testing"
  12. "time"
  13. "code.gitea.io/gitea/models/db"
  14. "code.gitea.io/gitea/models/foreignreference"
  15. issues_model "code.gitea.io/gitea/models/issues"
  16. repo_model "code.gitea.io/gitea/models/repo"
  17. "code.gitea.io/gitea/models/unittest"
  18. user_model "code.gitea.io/gitea/models/user"
  19. "github.com/stretchr/testify/assert"
  20. "xorm.io/builder"
  21. )
  22. func TestIssue_ReplaceLabels(t *testing.T) {
  23. assert.NoError(t, unittest.PrepareTestDatabase())
  24. testSuccess := func(issueID int64, labelIDs []int64) {
  25. issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue)
  26. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
  27. doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
  28. labels := make([]*Label, len(labelIDs))
  29. for i, labelID := range labelIDs {
  30. labels[i] = unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID, RepoID: repo.ID}).(*Label)
  31. }
  32. assert.NoError(t, ReplaceIssueLabels(issue, labels, doer))
  33. unittest.AssertCount(t, &IssueLabel{IssueID: issueID}, len(labelIDs))
  34. for _, labelID := range labelIDs {
  35. unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issueID, LabelID: labelID})
  36. }
  37. }
  38. testSuccess(1, []int64{2})
  39. testSuccess(1, []int64{1, 2})
  40. testSuccess(1, []int64{})
  41. }
  42. func Test_GetIssueIDsByRepoID(t *testing.T) {
  43. assert.NoError(t, unittest.PrepareTestDatabase())
  44. ids, err := GetIssueIDsByRepoID(1)
  45. assert.NoError(t, err)
  46. assert.Len(t, ids, 5)
  47. }
  48. func TestIssueAPIURL(t *testing.T) {
  49. assert.NoError(t, unittest.PrepareTestDatabase())
  50. issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
  51. err := issue.LoadAttributes()
  52. assert.NoError(t, err)
  53. assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/issues/1", issue.APIURL())
  54. }
  55. func TestGetIssuesByIDs(t *testing.T) {
  56. assert.NoError(t, unittest.PrepareTestDatabase())
  57. testSuccess := func(expectedIssueIDs, nonExistentIssueIDs []int64) {
  58. issues, err := GetIssuesByIDs(append(expectedIssueIDs, nonExistentIssueIDs...))
  59. assert.NoError(t, err)
  60. actualIssueIDs := make([]int64, len(issues))
  61. for i, issue := range issues {
  62. actualIssueIDs[i] = issue.ID
  63. }
  64. assert.Equal(t, expectedIssueIDs, actualIssueIDs)
  65. }
  66. testSuccess([]int64{1, 2, 3}, []int64{})
  67. testSuccess([]int64{1, 2, 3}, []int64{unittest.NonexistentID})
  68. }
  69. func TestGetParticipantIDsByIssue(t *testing.T) {
  70. assert.NoError(t, unittest.PrepareTestDatabase())
  71. checkParticipants := func(issueID int64, userIDs []int) {
  72. issue, err := GetIssueByID(issueID)
  73. assert.NoError(t, err)
  74. participants, err := issue.getParticipantIDsByIssue(db.GetEngine(db.DefaultContext))
  75. if assert.NoError(t, err) {
  76. participantsIDs := make([]int, len(participants))
  77. for i, uid := range participants {
  78. participantsIDs[i] = int(uid)
  79. }
  80. sort.Ints(participantsIDs)
  81. sort.Ints(userIDs)
  82. assert.Equal(t, userIDs, participantsIDs)
  83. }
  84. }
  85. // User 1 is issue1 poster (see fixtures/issue.yml)
  86. // User 2 only labeled issue1 (see fixtures/comment.yml)
  87. // Users 3 and 5 made actual comments (see fixtures/comment.yml)
  88. // User 3 is inactive, thus not active participant
  89. checkParticipants(1, []int{1, 5})
  90. }
  91. func TestIssue_ClearLabels(t *testing.T) {
  92. tests := []struct {
  93. issueID int64
  94. doerID int64
  95. }{
  96. {1, 2}, // non-pull-request, has labels
  97. {2, 2}, // pull-request, has labels
  98. {3, 2}, // pull-request, has no labels
  99. }
  100. for _, test := range tests {
  101. assert.NoError(t, unittest.PrepareTestDatabase())
  102. issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue)
  103. doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User)
  104. assert.NoError(t, ClearIssueLabels(issue, doer))
  105. unittest.AssertNotExistsBean(t, &IssueLabel{IssueID: test.issueID})
  106. }
  107. }
  108. func TestUpdateIssueCols(t *testing.T) {
  109. assert.NoError(t, unittest.PrepareTestDatabase())
  110. issue := unittest.AssertExistsAndLoadBean(t, &Issue{}).(*Issue)
  111. const newTitle = "New Title for unit test"
  112. issue.Title = newTitle
  113. prevContent := issue.Content
  114. issue.Content = "This should have no effect"
  115. now := time.Now().Unix()
  116. assert.NoError(t, UpdateIssueCols(db.DefaultContext, issue, "name"))
  117. then := time.Now().Unix()
  118. updatedIssue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
  119. assert.EqualValues(t, newTitle, updatedIssue.Title)
  120. assert.EqualValues(t, prevContent, updatedIssue.Content)
  121. unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
  122. }
  123. func TestIssues(t *testing.T) {
  124. assert.NoError(t, unittest.PrepareTestDatabase())
  125. for _, test := range []struct {
  126. Opts IssuesOptions
  127. ExpectedIssueIDs []int64
  128. }{
  129. {
  130. IssuesOptions{
  131. AssigneeID: 1,
  132. SortType: "oldest",
  133. },
  134. []int64{1, 6},
  135. },
  136. {
  137. IssuesOptions{
  138. RepoCond: builder.In("repo_id", 1, 3),
  139. SortType: "oldest",
  140. ListOptions: db.ListOptions{
  141. Page: 1,
  142. PageSize: 4,
  143. },
  144. },
  145. []int64{1, 2, 3, 5},
  146. },
  147. {
  148. IssuesOptions{
  149. LabelIDs: []int64{1},
  150. ListOptions: db.ListOptions{
  151. Page: 1,
  152. PageSize: 4,
  153. },
  154. },
  155. []int64{2, 1},
  156. },
  157. {
  158. IssuesOptions{
  159. LabelIDs: []int64{1, 2},
  160. ListOptions: db.ListOptions{
  161. Page: 1,
  162. PageSize: 4,
  163. },
  164. },
  165. []int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
  166. },
  167. } {
  168. issues, err := Issues(&test.Opts)
  169. assert.NoError(t, err)
  170. if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
  171. for i, issue := range issues {
  172. assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID)
  173. }
  174. }
  175. }
  176. }
  177. func TestGetUserIssueStats(t *testing.T) {
  178. assert.NoError(t, unittest.PrepareTestDatabase())
  179. for _, test := range []struct {
  180. Opts UserIssueStatsOptions
  181. ExpectedIssueStats IssueStats
  182. }{
  183. {
  184. UserIssueStatsOptions{
  185. UserID: 1,
  186. RepoIDs: []int64{1},
  187. FilterMode: FilterModeAll,
  188. },
  189. IssueStats{
  190. YourRepositoriesCount: 1, // 6
  191. AssignCount: 1, // 6
  192. CreateCount: 1, // 6
  193. OpenCount: 1, // 6
  194. ClosedCount: 1, // 1
  195. },
  196. },
  197. {
  198. UserIssueStatsOptions{
  199. UserID: 1,
  200. RepoIDs: []int64{1},
  201. FilterMode: FilterModeAll,
  202. IsClosed: true,
  203. },
  204. IssueStats{
  205. YourRepositoriesCount: 1, // 6
  206. AssignCount: 0,
  207. CreateCount: 0,
  208. OpenCount: 1, // 6
  209. ClosedCount: 1, // 1
  210. },
  211. },
  212. {
  213. UserIssueStatsOptions{
  214. UserID: 1,
  215. FilterMode: FilterModeAssign,
  216. },
  217. IssueStats{
  218. YourRepositoriesCount: 1, // 6
  219. AssignCount: 1, // 6
  220. CreateCount: 1, // 6
  221. OpenCount: 1, // 6
  222. ClosedCount: 0,
  223. },
  224. },
  225. {
  226. UserIssueStatsOptions{
  227. UserID: 1,
  228. FilterMode: FilterModeCreate,
  229. },
  230. IssueStats{
  231. YourRepositoriesCount: 1, // 6
  232. AssignCount: 1, // 6
  233. CreateCount: 1, // 6
  234. OpenCount: 1, // 6
  235. ClosedCount: 0,
  236. },
  237. },
  238. {
  239. UserIssueStatsOptions{
  240. UserID: 1,
  241. FilterMode: FilterModeMention,
  242. },
  243. IssueStats{
  244. YourRepositoriesCount: 1, // 6
  245. AssignCount: 1, // 6
  246. CreateCount: 1, // 6
  247. MentionCount: 0,
  248. OpenCount: 0,
  249. ClosedCount: 0,
  250. },
  251. },
  252. {
  253. UserIssueStatsOptions{
  254. UserID: 1,
  255. FilterMode: FilterModeCreate,
  256. IssueIDs: []int64{1},
  257. },
  258. IssueStats{
  259. YourRepositoriesCount: 1, // 1
  260. AssignCount: 1, // 1
  261. CreateCount: 1, // 1
  262. OpenCount: 1, // 1
  263. ClosedCount: 0,
  264. },
  265. },
  266. } {
  267. t.Run(fmt.Sprintf("%#v", test.Opts), func(t *testing.T) {
  268. stats, err := GetUserIssueStats(test.Opts)
  269. if !assert.NoError(t, err) {
  270. return
  271. }
  272. assert.Equal(t, test.ExpectedIssueStats, *stats)
  273. })
  274. }
  275. }
  276. func TestIssue_loadTotalTimes(t *testing.T) {
  277. assert.NoError(t, unittest.PrepareTestDatabase())
  278. ms, err := GetIssueByID(2)
  279. assert.NoError(t, err)
  280. assert.NoError(t, ms.loadTotalTimes(db.GetEngine(db.DefaultContext)))
  281. assert.Equal(t, int64(3682), ms.TotalTrackedTime)
  282. }
  283. func TestIssue_SearchIssueIDsByKeyword(t *testing.T) {
  284. assert.NoError(t, unittest.PrepareTestDatabase())
  285. total, ids, err := SearchIssueIDsByKeyword(context.TODO(), "issue2", []int64{1}, 10, 0)
  286. assert.NoError(t, err)
  287. assert.EqualValues(t, 1, total)
  288. assert.EqualValues(t, []int64{2}, ids)
  289. total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "first", []int64{1}, 10, 0)
  290. assert.NoError(t, err)
  291. assert.EqualValues(t, 1, total)
  292. assert.EqualValues(t, []int64{1}, ids)
  293. total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "for", []int64{1}, 10, 0)
  294. assert.NoError(t, err)
  295. assert.EqualValues(t, 5, total)
  296. assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids)
  297. // issue1's comment id 2
  298. total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "good", []int64{1}, 10, 0)
  299. assert.NoError(t, err)
  300. assert.EqualValues(t, 1, total)
  301. assert.EqualValues(t, []int64{1}, ids)
  302. }
  303. func TestGetRepoIDsForIssuesOptions(t *testing.T) {
  304. assert.NoError(t, unittest.PrepareTestDatabase())
  305. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
  306. for _, test := range []struct {
  307. Opts IssuesOptions
  308. ExpectedRepoIDs []int64
  309. }{
  310. {
  311. IssuesOptions{
  312. AssigneeID: 2,
  313. },
  314. []int64{3},
  315. },
  316. {
  317. IssuesOptions{
  318. RepoCond: builder.In("repo_id", 1, 2),
  319. },
  320. []int64{1, 2},
  321. },
  322. } {
  323. repoIDs, err := GetRepoIDsForIssuesOptions(&test.Opts, user)
  324. assert.NoError(t, err)
  325. if assert.Len(t, repoIDs, len(test.ExpectedRepoIDs)) {
  326. for i, repoID := range repoIDs {
  327. assert.EqualValues(t, test.ExpectedRepoIDs[i], repoID)
  328. }
  329. }
  330. }
  331. }
  332. func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *Issue {
  333. var newIssue Issue
  334. t.Run(title, func(t *testing.T) {
  335. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
  336. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
  337. issue := Issue{
  338. RepoID: repo.ID,
  339. PosterID: user.ID,
  340. Poster: user,
  341. Title: title,
  342. Content: content,
  343. }
  344. err := NewIssue(repo, &issue, nil, nil)
  345. assert.NoError(t, err)
  346. has, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Get(&newIssue)
  347. assert.NoError(t, err)
  348. assert.True(t, has)
  349. assert.EqualValues(t, issue.Title, newIssue.Title)
  350. assert.EqualValues(t, issue.Content, newIssue.Content)
  351. if expectIndex > 0 {
  352. assert.EqualValues(t, expectIndex, newIssue.Index)
  353. }
  354. })
  355. return &newIssue
  356. }
  357. func TestIssue_InsertIssue(t *testing.T) {
  358. assert.NoError(t, unittest.PrepareTestDatabase())
  359. // there are 5 issues and max index is 5 on repository 1, so this one should 6
  360. issue := testInsertIssue(t, "my issue1", "special issue's comments?", 6)
  361. _, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(Issue))
  362. assert.NoError(t, err)
  363. issue = testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?", 7)
  364. _, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(Issue))
  365. assert.NoError(t, err)
  366. }
  367. func TestIssue_DeleteIssue(t *testing.T) {
  368. assert.NoError(t, unittest.PrepareTestDatabase())
  369. issueIDs, err := GetIssueIDsByRepoID(1)
  370. assert.NoError(t, err)
  371. assert.EqualValues(t, 5, len(issueIDs))
  372. issue := &Issue{
  373. RepoID: 1,
  374. ID: issueIDs[2],
  375. }
  376. err = DeleteIssue(issue)
  377. assert.NoError(t, err)
  378. issueIDs, err = GetIssueIDsByRepoID(1)
  379. assert.NoError(t, err)
  380. assert.EqualValues(t, 4, len(issueIDs))
  381. // check attachment removal
  382. attachments, err := repo_model.GetAttachmentsByIssueID(4)
  383. assert.NoError(t, err)
  384. issue, err = GetIssueByID(4)
  385. assert.NoError(t, err)
  386. err = DeleteIssue(issue)
  387. assert.NoError(t, err)
  388. assert.EqualValues(t, 2, len(attachments))
  389. for i := range attachments {
  390. attachment, err := repo_model.GetAttachmentByUUID(attachments[i].UUID)
  391. assert.Error(t, err)
  392. assert.True(t, repo_model.IsErrAttachmentNotExist(err))
  393. assert.Nil(t, attachment)
  394. }
  395. // check issue dependencies
  396. user, err := user_model.GetUserByID(1)
  397. assert.NoError(t, err)
  398. issue1, err := GetIssueByID(1)
  399. assert.NoError(t, err)
  400. issue2, err := GetIssueByID(2)
  401. assert.NoError(t, err)
  402. err = CreateIssueDependency(user, issue1, issue2)
  403. assert.NoError(t, err)
  404. left, err := IssueNoDependenciesLeft(issue1)
  405. assert.NoError(t, err)
  406. assert.False(t, left)
  407. err = DeleteIssue(&Issue{ID: 2})
  408. assert.NoError(t, err)
  409. left, err = IssueNoDependenciesLeft(issue1)
  410. assert.NoError(t, err)
  411. assert.True(t, left)
  412. }
  413. func TestIssue_ResolveMentions(t *testing.T) {
  414. assert.NoError(t, unittest.PrepareTestDatabase())
  415. testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
  416. o := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: owner}).(*user_model.User)
  417. r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: o.ID, LowerName: repo}).(*repo_model.Repository)
  418. issue := &Issue{RepoID: r.ID}
  419. d := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: doer}).(*user_model.User)
  420. resolved, err := ResolveIssueMentionsByVisibility(db.DefaultContext, issue, d, mentions)
  421. assert.NoError(t, err)
  422. ids := make([]int64, len(resolved))
  423. for i, user := range resolved {
  424. ids[i] = user.ID
  425. }
  426. sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
  427. assert.EqualValues(t, expected, ids)
  428. }
  429. // Public repo, existing user
  430. testSuccess("user2", "repo1", "user1", []string{"user5"}, []int64{5})
  431. // Public repo, non-existing user
  432. testSuccess("user2", "repo1", "user1", []string{"nonexisting"}, []int64{})
  433. // Public repo, doer
  434. testSuccess("user2", "repo1", "user1", []string{"user1"}, []int64{})
  435. // Private repo, team member
  436. testSuccess("user17", "big_test_private_4", "user20", []string{"user2"}, []int64{2})
  437. // Private repo, not a team member
  438. testSuccess("user17", "big_test_private_4", "user20", []string{"user5"}, []int64{})
  439. // Private repo, whole team
  440. testSuccess("user17", "big_test_private_4", "user15", []string{"user17/owners"}, []int64{18})
  441. }
  442. func TestResourceIndex(t *testing.T) {
  443. assert.NoError(t, unittest.PrepareTestDatabase())
  444. var wg sync.WaitGroup
  445. for i := 0; i < 100; i++ {
  446. wg.Add(1)
  447. go func(i int) {
  448. testInsertIssue(t, fmt.Sprintf("issue %d", i+1), "my issue", 0)
  449. wg.Done()
  450. }(i)
  451. }
  452. wg.Wait()
  453. }
  454. func TestCorrectIssueStats(t *testing.T) {
  455. assert.NoError(t, unittest.PrepareTestDatabase())
  456. // Because the condition is to have chunked database look-ups,
  457. // We have to more issues than `maxQueryParameters`, we will insert.
  458. // maxQueryParameters + 10 issues into the testDatabase.
  459. // Each new issues will have a constant description "Bugs are nasty"
  460. // Which will be used later on.
  461. issueAmount := maxQueryParameters + 10
  462. var wg sync.WaitGroup
  463. for i := 0; i < issueAmount; i++ {
  464. wg.Add(1)
  465. go func(i int) {
  466. testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
  467. wg.Done()
  468. }(i)
  469. }
  470. wg.Wait()
  471. // Now we will get all issueID's that match the "Bugs are nasty" query.
  472. total, ids, err := SearchIssueIDsByKeyword(context.TODO(), "Bugs are nasty", []int64{1}, issueAmount, 0)
  473. // Just to be sure.
  474. assert.NoError(t, err)
  475. assert.EqualValues(t, issueAmount, total)
  476. // Now we will call the GetIssueStats with these IDs and if working,
  477. // get the correct stats back.
  478. issueStats, err := GetIssueStats(&IssueStatsOptions{
  479. RepoID: 1,
  480. IssueIDs: ids,
  481. })
  482. // Now check the values.
  483. assert.NoError(t, err)
  484. assert.EqualValues(t, issueStats.OpenCount, issueAmount)
  485. }
  486. func TestIssueForeignReference(t *testing.T) {
  487. assert.NoError(t, unittest.PrepareTestDatabase())
  488. issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 4}).(*Issue)
  489. assert.NotEqualValues(t, issue.Index, issue.ID) // make sure they are different to avoid false positive
  490. // it is fine for an issue to not have a foreign reference
  491. err := issue.LoadAttributes()
  492. assert.NoError(t, err)
  493. assert.Nil(t, issue.ForeignReference)
  494. var foreignIndex int64 = 12345
  495. _, err = GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex)
  496. assert.True(t, foreignreference.IsErrLocalIndexNotExist(err))
  497. _, err = db.GetEngine(db.DefaultContext).Insert(&foreignreference.ForeignReference{
  498. LocalIndex: issue.Index,
  499. ForeignIndex: strconv.FormatInt(foreignIndex, 10),
  500. RepoID: issue.RepoID,
  501. Type: foreignreference.TypeIssue,
  502. })
  503. assert.NoError(t, err)
  504. err = issue.LoadAttributes()
  505. assert.NoError(t, err)
  506. assert.EqualValues(t, issue.ForeignReference.ForeignIndex, strconv.FormatInt(foreignIndex, 10))
  507. found, err := GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex)
  508. assert.NoError(t, err)
  509. assert.EqualValues(t, found.Index, issue.Index)
  510. }
  511. func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) {
  512. assert.NoError(t, unittest.PrepareTestDatabase())
  513. miles := issues_model.MilestoneList{
  514. unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone),
  515. }
  516. assert.NoError(t, miles.LoadTotalTrackedTimes())
  517. assert.Equal(t, int64(3682), miles[0].TotalTrackedTime)
  518. }
  519. func TestLoadTotalTrackedTime(t *testing.T) {
  520. assert.NoError(t, unittest.PrepareTestDatabase())
  521. milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
  522. assert.NoError(t, milestone.LoadTotalTrackedTime())
  523. assert.Equal(t, int64(3682), milestone.TotalTrackedTime)
  524. }