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  }