394 lines
8.7 KiB
Go
394 lines
8.7 KiB
Go
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
|
// Use of this source code is governed by the Drone Non-Commercial License
|
|
// that can be found in the LICENSE file.
|
|
|
|
package reaper
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/drone/drone/core"
|
|
"github.com/drone/drone/mock"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
)
|
|
|
|
var nocontext = context.Background()
|
|
|
|
//
|
|
// reap tests
|
|
//
|
|
|
|
// this test confirms that pending builds that
|
|
// exceed the deadline are canceled, and pending
|
|
// builds that do not exceed the deadline are
|
|
// ignored.
|
|
func TestReapPending(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
defer func() {
|
|
now = time.Now
|
|
}()
|
|
now = func() time.Time {
|
|
return mustParse("2006-01-02T15:00:00")
|
|
}
|
|
|
|
mockRepo := &core.Repository{
|
|
ID: 2,
|
|
}
|
|
mockBuild := &core.Build{
|
|
ID: 1,
|
|
RepoID: mockRepo.ID,
|
|
Status: core.StatusPending,
|
|
Created: mustParse("2006-01-01T00:00:00").Unix(), // expire > 24 hours, must cancel
|
|
}
|
|
mockPending := []*core.Build{
|
|
mockBuild,
|
|
{
|
|
ID: 2,
|
|
RepoID: mockRepo.ID,
|
|
Status: core.StatusPending,
|
|
Created: mustParse("2006-01-02T14:30:00").Unix(), // expire < 1 hours, must ignore
|
|
},
|
|
}
|
|
|
|
repos := mock.NewMockRepositoryStore(controller)
|
|
repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil).Times(1)
|
|
|
|
builds := mock.NewMockBuildStore(controller)
|
|
builds.EXPECT().Pending(gomock.Any()).Return(mockPending, nil)
|
|
builds.EXPECT().Running(gomock.Any()).Return(nil, nil)
|
|
|
|
canceler := mock.NewMockCanceler(controller)
|
|
canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild)
|
|
|
|
r := New(
|
|
repos,
|
|
builds,
|
|
nil,
|
|
canceler,
|
|
time.Hour*24,
|
|
time.Hour*24,
|
|
)
|
|
|
|
r.reap(nocontext)
|
|
}
|
|
|
|
// this test confirms that running builds that
|
|
// exceed the deadline are canceled, and running
|
|
// builds that do not exceed the deadline are
|
|
// ignored.
|
|
func TestReapRunning(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
defer func() {
|
|
now = time.Now
|
|
}()
|
|
now = func() time.Time {
|
|
return mustParse("2006-01-02T15:00:00")
|
|
}
|
|
|
|
mockRepo := &core.Repository{
|
|
ID: 2,
|
|
Timeout: 60,
|
|
}
|
|
mockBuild := &core.Build{
|
|
ID: 1,
|
|
RepoID: mockRepo.ID,
|
|
Status: core.StatusRunning,
|
|
Started: mustParse("2006-01-01T00:00:00").Unix(), // expire > 24 hours, must cancel
|
|
}
|
|
mockRunning := []*core.Build{
|
|
mockBuild,
|
|
{
|
|
ID: 2,
|
|
RepoID: mockRepo.ID,
|
|
Status: core.StatusRunning,
|
|
Started: mustParse("2006-01-02T14:30:00").Unix(), // expire < 1 hours, must ignore
|
|
},
|
|
}
|
|
mockStages := []*core.Stage{
|
|
{
|
|
BuildID: mockBuild.ID,
|
|
Status: core.StatusPending,
|
|
Started: 0,
|
|
},
|
|
}
|
|
|
|
repos := mock.NewMockRepositoryStore(controller)
|
|
repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil).Times(1)
|
|
|
|
builds := mock.NewMockBuildStore(controller)
|
|
builds.EXPECT().Pending(gomock.Any()).Return(nil, nil)
|
|
builds.EXPECT().Running(gomock.Any()).Return(mockRunning, nil)
|
|
|
|
stages := mock.NewMockStageStore(controller)
|
|
stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil)
|
|
|
|
canceler := mock.NewMockCanceler(controller)
|
|
canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild)
|
|
|
|
r := New(
|
|
repos,
|
|
builds,
|
|
stages,
|
|
canceler,
|
|
time.Hour*24,
|
|
time.Hour*24,
|
|
)
|
|
|
|
r.reap(nocontext)
|
|
}
|
|
|
|
//
|
|
// reap maybe tests
|
|
//
|
|
|
|
// this test confirms that the build is cancelled
|
|
// if the build status is pending.
|
|
func TestReapPendingMaybe(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockBuild := &core.Build{
|
|
ID: 1,
|
|
RepoID: 2,
|
|
Status: core.StatusPending,
|
|
}
|
|
mockRepo := &core.Repository{
|
|
ID: 2,
|
|
}
|
|
|
|
repos := mock.NewMockRepositoryStore(controller)
|
|
repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil)
|
|
|
|
canceler := mock.NewMockCanceler(controller)
|
|
canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild)
|
|
|
|
r := &Reaper{
|
|
Repos: repos,
|
|
Stages: nil,
|
|
Canceler: canceler,
|
|
}
|
|
|
|
r.reapMaybe(nocontext, mockBuild)
|
|
}
|
|
|
|
// this test confirms that the build is cancelled
|
|
// if the build status is running, and the stage
|
|
// started date is greater than the expiry date.
|
|
func TestReapRunningMaybe(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
defer func() {
|
|
now = time.Now
|
|
}()
|
|
now = func() time.Time {
|
|
return mustParse("2006-01-02T15:00:00")
|
|
}
|
|
|
|
mockBuild := &core.Build{
|
|
ID: 1,
|
|
RepoID: 2,
|
|
Status: core.StatusRunning,
|
|
}
|
|
mockRepo := &core.Repository{
|
|
ID: 2,
|
|
Timeout: 60,
|
|
}
|
|
mockStages := []*core.Stage{
|
|
{
|
|
Status: core.StatusRunning,
|
|
Started: mustParse("2006-01-02T13:00:00").Unix(), // running 2 hours, 1 hour longer than timeout
|
|
},
|
|
}
|
|
|
|
repos := mock.NewMockRepositoryStore(controller)
|
|
repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil)
|
|
|
|
stages := mock.NewMockStageStore(controller)
|
|
stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil)
|
|
|
|
canceler := mock.NewMockCanceler(controller)
|
|
canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild)
|
|
|
|
r := &Reaper{
|
|
Repos: repos,
|
|
Stages: stages,
|
|
Canceler: canceler,
|
|
}
|
|
|
|
r.reapMaybe(nocontext, mockBuild)
|
|
}
|
|
|
|
// this test confirms that if the build status is
|
|
// running, but all stages have a pending status,
|
|
// the build is cancelled (this likely points to some
|
|
// sort of race condition, and should not happen).
|
|
func TestReapRunningMaybe_AllStagesPending(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
defer func() {
|
|
now = time.Now
|
|
}()
|
|
now = func() time.Time {
|
|
return mustParse("2006-01-02T15:00:00")
|
|
}
|
|
|
|
mockBuild := &core.Build{
|
|
ID: 1,
|
|
RepoID: 2,
|
|
Status: core.StatusRunning,
|
|
}
|
|
mockRepo := &core.Repository{
|
|
ID: 2,
|
|
Timeout: 60,
|
|
}
|
|
mockStages := []*core.Stage{
|
|
{
|
|
Status: core.StatusPending,
|
|
Started: 0,
|
|
},
|
|
{
|
|
Status: core.StatusPending,
|
|
Started: 0,
|
|
},
|
|
}
|
|
|
|
repos := mock.NewMockRepositoryStore(controller)
|
|
repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil)
|
|
|
|
stages := mock.NewMockStageStore(controller)
|
|
stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil)
|
|
|
|
canceler := mock.NewMockCanceler(controller)
|
|
canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild)
|
|
|
|
r := &Reaper{
|
|
Repos: repos,
|
|
Stages: stages,
|
|
Canceler: canceler,
|
|
}
|
|
|
|
r.reapMaybe(nocontext, mockBuild)
|
|
}
|
|
|
|
// this test confirms that if the build status is
|
|
// running, but all stages have a finished status,
|
|
// the build is cancelled (this likely points to some
|
|
// sort of race condition, and should not happen).
|
|
func TestReapRunningMaybe_AllStagesFinished(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
defer func() {
|
|
now = time.Now
|
|
}()
|
|
now = func() time.Time {
|
|
return mustParse("2006-01-02T15:00:00")
|
|
}
|
|
|
|
mockBuild := &core.Build{
|
|
ID: 1,
|
|
RepoID: 2,
|
|
Status: core.StatusRunning,
|
|
}
|
|
mockRepo := &core.Repository{
|
|
ID: 2,
|
|
Timeout: 60,
|
|
}
|
|
mockStages := []*core.Stage{
|
|
{
|
|
Status: core.StatusPassing,
|
|
Started: mustParse("2006-01-02T14:40:00").Unix(),
|
|
},
|
|
{
|
|
Status: core.StatusPassing,
|
|
Started: mustParse("2006-01-02T14:50:00").Unix(),
|
|
},
|
|
}
|
|
|
|
repos := mock.NewMockRepositoryStore(controller)
|
|
repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil)
|
|
|
|
stages := mock.NewMockStageStore(controller)
|
|
stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil)
|
|
|
|
canceler := mock.NewMockCanceler(controller)
|
|
canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild)
|
|
|
|
r := &Reaper{
|
|
Repos: repos,
|
|
Stages: stages,
|
|
Canceler: canceler,
|
|
}
|
|
|
|
r.reapMaybe(nocontext, mockBuild)
|
|
}
|
|
|
|
// this test confirms that if the build status is
|
|
// running, but the stage start time has not exceeded
|
|
// the timeout period, the build is NOT cancelled.
|
|
func TestReapRunningMaybe_NotExpired(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
defer func() {
|
|
now = time.Now
|
|
}()
|
|
now = func() time.Time {
|
|
return mustParse("2006-01-02T15:00:00")
|
|
}
|
|
|
|
mockBuild := &core.Build{
|
|
ID: 1,
|
|
RepoID: 2,
|
|
Status: core.StatusRunning,
|
|
}
|
|
mockRepo := &core.Repository{
|
|
ID: 2,
|
|
Timeout: 60,
|
|
}
|
|
mockStages := []*core.Stage{
|
|
{
|
|
Status: core.StatusPassing,
|
|
Started: mustParse("2006-01-02T14:50:00").Unix(),
|
|
},
|
|
{
|
|
Status: core.StatusRunning,
|
|
Started: mustParse("2006-01-02T14:55:00").Unix(),
|
|
},
|
|
}
|
|
|
|
repos := mock.NewMockRepositoryStore(controller)
|
|
repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil)
|
|
|
|
stages := mock.NewMockStageStore(controller)
|
|
stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil)
|
|
|
|
r := &Reaper{
|
|
Repos: repos,
|
|
Stages: stages,
|
|
Canceler: nil,
|
|
}
|
|
|
|
r.reapMaybe(nocontext, mockBuild)
|
|
}
|
|
|
|
//
|
|
// Failure Scenarios
|
|
//
|
|
|
|
func TestReapRunningMaybe_ErrorGetRepo(t *testing.T) {
|
|
|
|
}
|
|
|
|
func TestReapRunningMaybe_ErrorListStages(t *testing.T) {
|
|
|
|
}
|