code.gitea.io/gitea@v1.22.3/models/issues/milestone_test.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package issues_test 5 6 import ( 7 "sort" 8 "testing" 9 10 "code.gitea.io/gitea/models/db" 11 issues_model "code.gitea.io/gitea/models/issues" 12 repo_model "code.gitea.io/gitea/models/repo" 13 "code.gitea.io/gitea/models/unittest" 14 "code.gitea.io/gitea/modules/optional" 15 "code.gitea.io/gitea/modules/setting" 16 api "code.gitea.io/gitea/modules/structs" 17 "code.gitea.io/gitea/modules/timeutil" 18 19 "github.com/stretchr/testify/assert" 20 ) 21 22 func TestMilestone_State(t *testing.T) { 23 assert.Equal(t, api.StateOpen, (&issues_model.Milestone{IsClosed: false}).State()) 24 assert.Equal(t, api.StateClosed, (&issues_model.Milestone{IsClosed: true}).State()) 25 } 26 27 func TestGetMilestoneByRepoID(t *testing.T) { 28 assert.NoError(t, unittest.PrepareTestDatabase()) 29 30 milestone, err := issues_model.GetMilestoneByRepoID(db.DefaultContext, 1, 1) 31 assert.NoError(t, err) 32 assert.EqualValues(t, 1, milestone.ID) 33 assert.EqualValues(t, 1, milestone.RepoID) 34 35 _, err = issues_model.GetMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) 36 assert.True(t, issues_model.IsErrMilestoneNotExist(err)) 37 } 38 39 func TestGetMilestonesByRepoID(t *testing.T) { 40 assert.NoError(t, unittest.PrepareTestDatabase()) 41 test := func(repoID int64, state api.StateType) { 42 var isClosed optional.Option[bool] 43 switch state { 44 case api.StateClosed, api.StateOpen: 45 isClosed = optional.Some(state == api.StateClosed) 46 } 47 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) 48 milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 49 RepoID: repo.ID, 50 IsClosed: isClosed, 51 }) 52 assert.NoError(t, err) 53 54 var n int 55 56 switch state { 57 case api.StateClosed: 58 n = repo.NumClosedMilestones 59 60 case api.StateAll: 61 n = repo.NumMilestones 62 63 case api.StateOpen: 64 fallthrough 65 66 default: 67 n = repo.NumOpenMilestones 68 } 69 70 assert.Len(t, milestones, n) 71 for _, milestone := range milestones { 72 assert.EqualValues(t, repoID, milestone.RepoID) 73 } 74 } 75 test(1, api.StateOpen) 76 test(1, api.StateAll) 77 test(1, api.StateClosed) 78 test(2, api.StateOpen) 79 test(2, api.StateAll) 80 test(2, api.StateClosed) 81 test(3, api.StateOpen) 82 test(3, api.StateClosed) 83 test(3, api.StateAll) 84 85 milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 86 RepoID: unittest.NonexistentID, 87 IsClosed: optional.Some(false), 88 }) 89 assert.NoError(t, err) 90 assert.Len(t, milestones, 0) 91 } 92 93 func TestGetMilestones(t *testing.T) { 94 assert.NoError(t, unittest.PrepareTestDatabase()) 95 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 96 test := func(sortType string, sortCond func(*issues_model.Milestone) int) { 97 for _, page := range []int{0, 1} { 98 milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 99 ListOptions: db.ListOptions{ 100 Page: page, 101 PageSize: setting.UI.IssuePagingNum, 102 }, 103 RepoID: repo.ID, 104 IsClosed: optional.Some(false), 105 SortType: sortType, 106 }) 107 assert.NoError(t, err) 108 assert.Len(t, milestones, repo.NumMilestones-repo.NumClosedMilestones) 109 values := make([]int, len(milestones)) 110 for i, milestone := range milestones { 111 values[i] = sortCond(milestone) 112 } 113 assert.True(t, sort.IntsAreSorted(values)) 114 115 milestones, err = db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 116 ListOptions: db.ListOptions{ 117 Page: page, 118 PageSize: setting.UI.IssuePagingNum, 119 }, 120 RepoID: repo.ID, 121 IsClosed: optional.Some(true), 122 Name: "", 123 SortType: sortType, 124 }) 125 assert.NoError(t, err) 126 assert.Len(t, milestones, repo.NumClosedMilestones) 127 values = make([]int, len(milestones)) 128 for i, milestone := range milestones { 129 values[i] = sortCond(milestone) 130 } 131 assert.True(t, sort.IntsAreSorted(values)) 132 } 133 } 134 test("furthestduedate", func(milestone *issues_model.Milestone) int { 135 return -int(milestone.DeadlineUnix) 136 }) 137 test("leastcomplete", func(milestone *issues_model.Milestone) int { 138 return milestone.Completeness 139 }) 140 test("mostcomplete", func(milestone *issues_model.Milestone) int { 141 return -milestone.Completeness 142 }) 143 test("leastissues", func(milestone *issues_model.Milestone) int { 144 return milestone.NumIssues 145 }) 146 test("mostissues", func(milestone *issues_model.Milestone) int { 147 return -milestone.NumIssues 148 }) 149 test("soonestduedate", func(milestone *issues_model.Milestone) int { 150 return int(milestone.DeadlineUnix) 151 }) 152 } 153 154 func TestCountRepoMilestones(t *testing.T) { 155 assert.NoError(t, unittest.PrepareTestDatabase()) 156 test := func(repoID int64) { 157 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) 158 count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 159 RepoID: repoID, 160 }) 161 assert.NoError(t, err) 162 assert.EqualValues(t, repo.NumMilestones, count) 163 } 164 test(1) 165 test(2) 166 test(3) 167 168 count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 169 RepoID: unittest.NonexistentID, 170 }) 171 assert.NoError(t, err) 172 assert.EqualValues(t, 0, count) 173 } 174 175 func TestCountRepoClosedMilestones(t *testing.T) { 176 assert.NoError(t, unittest.PrepareTestDatabase()) 177 test := func(repoID int64) { 178 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) 179 count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 180 RepoID: repoID, 181 IsClosed: optional.Some(true), 182 }) 183 assert.NoError(t, err) 184 assert.EqualValues(t, repo.NumClosedMilestones, count) 185 } 186 test(1) 187 test(2) 188 test(3) 189 190 count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 191 RepoID: unittest.NonexistentID, 192 IsClosed: optional.Some(true), 193 }) 194 assert.NoError(t, err) 195 assert.EqualValues(t, 0, count) 196 } 197 198 func TestCountMilestonesByRepoIDs(t *testing.T) { 199 assert.NoError(t, unittest.PrepareTestDatabase()) 200 milestonesCount := func(repoID int64) (int, int) { 201 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) 202 return repo.NumOpenMilestones, repo.NumClosedMilestones 203 } 204 repo1OpenCount, repo1ClosedCount := milestonesCount(1) 205 repo2OpenCount, repo2ClosedCount := milestonesCount(2) 206 207 openCounts, err := issues_model.CountMilestonesMap(db.DefaultContext, issues_model.FindMilestoneOptions{ 208 RepoIDs: []int64{1, 2}, 209 IsClosed: optional.Some(false), 210 }) 211 assert.NoError(t, err) 212 assert.EqualValues(t, repo1OpenCount, openCounts[1]) 213 assert.EqualValues(t, repo2OpenCount, openCounts[2]) 214 215 closedCounts, err := issues_model.CountMilestonesMap(db.DefaultContext, 216 issues_model.FindMilestoneOptions{ 217 RepoIDs: []int64{1, 2}, 218 IsClosed: optional.Some(true), 219 }) 220 assert.NoError(t, err) 221 assert.EqualValues(t, repo1ClosedCount, closedCounts[1]) 222 assert.EqualValues(t, repo2ClosedCount, closedCounts[2]) 223 } 224 225 func TestGetMilestonesByRepoIDs(t *testing.T) { 226 assert.NoError(t, unittest.PrepareTestDatabase()) 227 repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 228 repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) 229 test := func(sortType string, sortCond func(*issues_model.Milestone) int) { 230 for _, page := range []int{0, 1} { 231 openMilestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ 232 ListOptions: db.ListOptions{ 233 Page: page, 234 PageSize: setting.UI.IssuePagingNum, 235 }, 236 RepoIDs: []int64{repo1.ID, repo2.ID}, 237 IsClosed: optional.Some(false), 238 SortType: sortType, 239 }) 240 assert.NoError(t, err) 241 assert.Len(t, openMilestones, repo1.NumOpenMilestones+repo2.NumOpenMilestones) 242 values := make([]int, len(openMilestones)) 243 for i, milestone := range openMilestones { 244 values[i] = sortCond(milestone) 245 } 246 assert.True(t, sort.IntsAreSorted(values)) 247 248 closedMilestones, err := db.Find[issues_model.Milestone](db.DefaultContext, 249 issues_model.FindMilestoneOptions{ 250 ListOptions: db.ListOptions{ 251 Page: page, 252 PageSize: setting.UI.IssuePagingNum, 253 }, 254 RepoIDs: []int64{repo1.ID, repo2.ID}, 255 IsClosed: optional.Some(true), 256 SortType: sortType, 257 }) 258 assert.NoError(t, err) 259 assert.Len(t, closedMilestones, repo1.NumClosedMilestones+repo2.NumClosedMilestones) 260 values = make([]int, len(closedMilestones)) 261 for i, milestone := range closedMilestones { 262 values[i] = sortCond(milestone) 263 } 264 assert.True(t, sort.IntsAreSorted(values)) 265 } 266 } 267 test("furthestduedate", func(milestone *issues_model.Milestone) int { 268 return -int(milestone.DeadlineUnix) 269 }) 270 test("leastcomplete", func(milestone *issues_model.Milestone) int { 271 return milestone.Completeness 272 }) 273 test("mostcomplete", func(milestone *issues_model.Milestone) int { 274 return -milestone.Completeness 275 }) 276 test("leastissues", func(milestone *issues_model.Milestone) int { 277 return milestone.NumIssues 278 }) 279 test("mostissues", func(milestone *issues_model.Milestone) int { 280 return -milestone.NumIssues 281 }) 282 test("soonestduedate", func(milestone *issues_model.Milestone) int { 283 return int(milestone.DeadlineUnix) 284 }) 285 } 286 287 func TestNewMilestone(t *testing.T) { 288 assert.NoError(t, unittest.PrepareTestDatabase()) 289 milestone := &issues_model.Milestone{ 290 RepoID: 1, 291 Name: "milestoneName", 292 Content: "milestoneContent", 293 } 294 295 assert.NoError(t, issues_model.NewMilestone(db.DefaultContext, milestone)) 296 unittest.AssertExistsAndLoadBean(t, milestone) 297 unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) 298 } 299 300 func TestChangeMilestoneStatus(t *testing.T) { 301 assert.NoError(t, unittest.PrepareTestDatabase()) 302 milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}) 303 304 assert.NoError(t, issues_model.ChangeMilestoneStatus(db.DefaultContext, milestone, true)) 305 unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=1") 306 unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) 307 308 assert.NoError(t, issues_model.ChangeMilestoneStatus(db.DefaultContext, milestone, false)) 309 unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=0") 310 unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) 311 } 312 313 func TestDeleteMilestoneByRepoID(t *testing.T) { 314 assert.NoError(t, unittest.PrepareTestDatabase()) 315 assert.NoError(t, issues_model.DeleteMilestoneByRepoID(db.DefaultContext, 1, 1)) 316 unittest.AssertNotExistsBean(t, &issues_model.Milestone{ID: 1}) 317 unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: 1}) 318 319 assert.NoError(t, issues_model.DeleteMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)) 320 } 321 322 func TestUpdateMilestone(t *testing.T) { 323 assert.NoError(t, unittest.PrepareTestDatabase()) 324 325 milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}) 326 milestone.Name = " newMilestoneName " 327 milestone.Content = "newMilestoneContent" 328 assert.NoError(t, issues_model.UpdateMilestone(db.DefaultContext, milestone, milestone.IsClosed)) 329 milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}) 330 assert.EqualValues(t, "newMilestoneName", milestone.Name) 331 unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) 332 } 333 334 func TestUpdateMilestoneCounters(t *testing.T) { 335 assert.NoError(t, unittest.PrepareTestDatabase()) 336 issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{MilestoneID: 1}, 337 "is_closed=0") 338 339 issue.IsClosed = true 340 issue.ClosedUnix = timeutil.TimeStampNow() 341 _, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue) 342 assert.NoError(t, err) 343 assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID)) 344 unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) 345 346 issue.IsClosed = false 347 issue.ClosedUnix = 0 348 _, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue) 349 assert.NoError(t, err) 350 assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID)) 351 unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) 352 } 353 354 func TestMigrate_InsertMilestones(t *testing.T) { 355 assert.NoError(t, unittest.PrepareTestDatabase()) 356 reponame := "repo1" 357 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}) 358 name := "milestonetest1" 359 ms := &issues_model.Milestone{ 360 RepoID: repo.ID, 361 Name: name, 362 } 363 err := issues_model.InsertMilestones(db.DefaultContext, ms) 364 assert.NoError(t, err) 365 unittest.AssertExistsAndLoadBean(t, ms) 366 repoModified := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID}) 367 assert.EqualValues(t, repo.NumMilestones+1, repoModified.NumMilestones) 368 369 unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) 370 }