From 22a6f4a60be88c22bb074d90d659eed7198fa925 Mon Sep 17 00:00:00 2001 From: Aline Abler Date: Thu, 27 Jun 2024 12:22:23 +0200 Subject: [PATCH] Add tests for task package --- client/client.go | 32 +++++--- client/mock/client.go | 113 ++++++++++++++++++++++++++++ go.mod | 4 + go.sum | 9 +++ main.go | 2 +- task/task.go | 34 +++++++-- task/task_test.go | 166 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 340 insertions(+), 20 deletions(-) create mode 100644 client/mock/client.go create mode 100644 task/task_test.go diff --git a/client/client.go b/client/client.go index 816e982..880f1c8 100644 --- a/client/client.go +++ b/client/client.go @@ -13,13 +13,21 @@ type GitlabConfig struct { BaseURL string } -type GitlabClient struct { +type GitlabClient interface { + GetConfigFileForMR(mr *gitlab.MergeRequest, filePath string) (*[]byte, error) + ListMrsWithLabel(label string) ([]*gitlab.MergeRequest, error) + RefreshMr(mr *gitlab.MergeRequest) (*gitlab.MergeRequest, error) + MergeMr(mr *gitlab.MergeRequest) error + Comment(mr *gitlab.MergeRequest, comment string) error +} + +type gitlabClientImpl struct { client *gitlab.Client me *gitlab.User config *GitlabConfig } -func NewGitlabClient(config GitlabConfig) (*GitlabClient, error) { +func NewGitlabClient(config GitlabConfig) (GitlabClient, error) { git, err := gitlab.NewClient(config.AccessToken, gitlab.WithBaseURL(config.BaseURL)) if err != nil { return nil, fmt.Errorf("failed to authenticate to GitLab: %w", err) @@ -28,14 +36,14 @@ func NewGitlabClient(config GitlabConfig) (*GitlabClient, error) { if err != nil { return nil, fmt.Errorf("failed to get current user information from GitLab: %w", err) } - return &GitlabClient{ + return &gitlabClientImpl{ client: git, me: me, config: &config, }, nil } -func (g *GitlabClient) GetConfigFileForMR(mr *gitlab.MergeRequest, filePath string) (*[]byte, error) { +func (g *gitlabClientImpl) GetConfigFileForMR(mr *gitlab.MergeRequest, filePath string) (*[]byte, error) { opts := &gitlab.GetRawFileOptions{Ref: &mr.SourceBranch} file, _, err := g.client.RepositoryFiles.GetRawFile(mr.ProjectID, filePath, opts) if err != nil { @@ -44,7 +52,7 @@ func (g *GitlabClient) GetConfigFileForMR(mr *gitlab.MergeRequest, filePath stri return &file, nil } -func (g *GitlabClient) ListMrsWithLabel(label string) ([]*gitlab.MergeRequest, error) { +func (g *gitlabClientImpl) ListMrsWithLabel(label string) ([]*gitlab.MergeRequest, error) { labels := gitlab.LabelOptions{label} opts := &gitlab.ListMergeRequestsOptions{ ListOptions: gitlab.ListOptions{ @@ -72,7 +80,7 @@ func (g *GitlabClient) ListMrsWithLabel(label string) ([]*gitlab.MergeRequest, e return allMrs, nil } -func (g *GitlabClient) RefreshMr(mr *gitlab.MergeRequest) (*gitlab.MergeRequest, error) { +func (g *gitlabClientImpl) RefreshMr(mr *gitlab.MergeRequest) (*gitlab.MergeRequest, error) { opts := &gitlab.GetMergeRequestsOptions{} mr, _, err := g.client.MergeRequests.GetMergeRequest(mr.ProjectID, mr.IID, opts) if err != nil { @@ -82,7 +90,7 @@ func (g *GitlabClient) RefreshMr(mr *gitlab.MergeRequest) (*gitlab.MergeRequest, return mr, nil } -func (g *GitlabClient) MergeMr(mr *gitlab.MergeRequest) error { +func (g *gitlabClientImpl) MergeMr(mr *gitlab.MergeRequest) error { opts := &gitlab.AcceptMergeRequestOptions{ShouldRemoveSourceBranch: gitlab.Ptr(true)} _, _, err := g.client.MergeRequests.AcceptMergeRequest(mr.ProjectID, mr.IID, opts) if err != nil { @@ -91,11 +99,7 @@ func (g *GitlabClient) MergeMr(mr *gitlab.MergeRequest) error { return nil } -func IsMergeable(mr *gitlab.MergeRequest) bool { - return mr.DetailedMergeStatus == MR_MERGE_STATUS_MERGEABLE -} - -func (g *GitlabClient) Comment(mr *gitlab.MergeRequest, comment string) error { +func (g *gitlabClientImpl) Comment(mr *gitlab.MergeRequest, comment string) error { nopts := &gitlab.ListMergeRequestNotesOptions{} notes, _, err := g.client.Notes.ListMergeRequestNotes(mr.ProjectID, mr.IID, nopts) if err != nil { @@ -120,3 +124,7 @@ func (g *GitlabClient) Comment(mr *gitlab.MergeRequest, comment string) error { } return nil } + +func IsMergeable(mr *gitlab.MergeRequest) bool { + return mr.DetailedMergeStatus == MR_MERGE_STATUS_MERGEABLE +} diff --git a/client/mock/client.go b/client/mock/client.go new file mode 100644 index 0000000..6380ec6 --- /dev/null +++ b/client/mock/client.go @@ -0,0 +1,113 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: client/client.go +// +// Generated by this command: +// +// mockgen -source client/client.go -destination client/mock/client.go +// + +// Package mock_client is a generated GoMock package. +package mock_client + +import ( + reflect "reflect" + + go_gitlab "github.com/xanzy/go-gitlab" + gomock "go.uber.org/mock/gomock" +) + +// MockGitlabClient is a mock of GitlabClient interface. +type MockGitlabClient struct { + ctrl *gomock.Controller + recorder *MockGitlabClientMockRecorder +} + +// MockGitlabClientMockRecorder is the mock recorder for MockGitlabClient. +type MockGitlabClientMockRecorder struct { + mock *MockGitlabClient +} + +// NewMockGitlabClient creates a new mock instance. +func NewMockGitlabClient(ctrl *gomock.Controller) *MockGitlabClient { + mock := &MockGitlabClient{ctrl: ctrl} + mock.recorder = &MockGitlabClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGitlabClient) EXPECT() *MockGitlabClientMockRecorder { + return m.recorder +} + +// Comment mocks base method. +func (m *MockGitlabClient) Comment(mr *go_gitlab.MergeRequest, comment string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Comment", mr, comment) + ret0, _ := ret[0].(error) + return ret0 +} + +// Comment indicates an expected call of Comment. +func (mr_2 *MockGitlabClientMockRecorder) Comment(mr, comment any) *gomock.Call { + mr_2.mock.ctrl.T.Helper() + return mr_2.mock.ctrl.RecordCallWithMethodType(mr_2.mock, "Comment", reflect.TypeOf((*MockGitlabClient)(nil).Comment), mr, comment) +} + +// GetConfigFileForMR mocks base method. +func (m *MockGitlabClient) GetConfigFileForMR(mr *go_gitlab.MergeRequest, filePath string) (*[]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetConfigFileForMR", mr, filePath) + ret0, _ := ret[0].(*[]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetConfigFileForMR indicates an expected call of GetConfigFileForMR. +func (mr_2 *MockGitlabClientMockRecorder) GetConfigFileForMR(mr, filePath any) *gomock.Call { + mr_2.mock.ctrl.T.Helper() + return mr_2.mock.ctrl.RecordCallWithMethodType(mr_2.mock, "GetConfigFileForMR", reflect.TypeOf((*MockGitlabClient)(nil).GetConfigFileForMR), mr, filePath) +} + +// ListMrsWithLabel mocks base method. +func (m *MockGitlabClient) ListMrsWithLabel(label string) ([]*go_gitlab.MergeRequest, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListMrsWithLabel", label) + ret0, _ := ret[0].([]*go_gitlab.MergeRequest) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListMrsWithLabel indicates an expected call of ListMrsWithLabel. +func (mr *MockGitlabClientMockRecorder) ListMrsWithLabel(label any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListMrsWithLabel", reflect.TypeOf((*MockGitlabClient)(nil).ListMrsWithLabel), label) +} + +// MergeMr mocks base method. +func (m *MockGitlabClient) MergeMr(mr *go_gitlab.MergeRequest) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MergeMr", mr) + ret0, _ := ret[0].(error) + return ret0 +} + +// MergeMr indicates an expected call of MergeMr. +func (mr_2 *MockGitlabClientMockRecorder) MergeMr(mr any) *gomock.Call { + mr_2.mock.ctrl.T.Helper() + return mr_2.mock.ctrl.RecordCallWithMethodType(mr_2.mock, "MergeMr", reflect.TypeOf((*MockGitlabClient)(nil).MergeMr), mr) +} + +// RefreshMr mocks base method. +func (m *MockGitlabClient) RefreshMr(mr *go_gitlab.MergeRequest) (*go_gitlab.MergeRequest, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RefreshMr", mr) + ret0, _ := ret[0].(*go_gitlab.MergeRequest) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RefreshMr indicates an expected call of RefreshMr. +func (mr_2 *MockGitlabClientMockRecorder) RefreshMr(mr any) *gomock.Call { + mr_2.mock.ctrl.T.Helper() + return mr_2.mock.ctrl.RecordCallWithMethodType(mr_2.mock, "RefreshMr", reflect.TypeOf((*MockGitlabClient)(nil).RefreshMr), mr) +} diff --git a/go.mod b/go.mod index 96ebbb2..06c58e0 100644 --- a/go.mod +++ b/go.mod @@ -5,17 +5,21 @@ go 1.22.2 require ( github.com/robfig/cron/v3 v3.0.1 github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.8.1 github.com/xanzy/go-gitlab v0.106.0 + go.uber.org/mock v0.4.0 go.uber.org/multierr v1.11.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect diff --git a/go.sum b/go.sum index 23a2029..8ad2d8c 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -28,11 +29,18 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/xanzy/go-gitlab v0.106.0 h1:EDfD03K74cIlQo2EducfiupVrip+Oj02bq9ofw5F8sA= github.com/xanzy/go-gitlab v0.106.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -56,5 +64,6 @@ google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62Uo google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index b170b80..e1b21c5 100644 --- a/main.go +++ b/main.go @@ -63,7 +63,7 @@ func main() { } func setupCronTask( - client *client.GitlabClient, + client client.GitlabClient, crontab string, scheduledLabel string, configFilePath string, diff --git a/task/task.go b/task/task.go index 72780f9..940d95e 100644 --- a/task/task.go +++ b/task/task.go @@ -20,7 +20,8 @@ type TaskConfig struct { type Task struct { config TaskConfig - client *client.GitlabClient + client client.GitlabClient + clock Clock } type RepositoryConfig struct { @@ -36,10 +37,29 @@ type MergeSchedule struct { Location string `yaml:"location"` } -func NewTask(client *client.GitlabClient, config TaskConfig) Task { +type Clock interface { + Now() time.Time +} + +type realClock struct{} + +func (realClock) Now() time.Time { + return time.Now() +} + +func NewTask(client client.GitlabClient, config TaskConfig) Task { + return Task{ + config: config, + client: client, + clock: realClock{}, + } +} + +func NewTaskWithClock(client client.GitlabClient, config TaskConfig, clock Clock) Task { return Task{ config: config, client: client, + clock: clock, } } @@ -71,16 +91,16 @@ func (t Task) processMR(mr *gitlab.MergeRequest) error { err = yaml.Unmarshal(*file, &config) if err != nil { - return t.client.Comment(mr, "Failed to schedule merge: error while parsing config file.") + return t.client.Comment(mr, fmt.Sprintf("Failed to schedule merge: Error while parsing config file.\n\n%s", err.Error())) } - now := time.Now() + now := t.clock.Now() var earliestMergeWindow *MergeWindow = nil earliestMergeWindowTime := now.Add(1000000 * time.Hour) for _, w := range config.MergeWindows { nextActiveStartTime, err := w.getNextActiveWindowStartTime(now) if err != nil { - return t.client.Comment(mr, "Failed to schedule merge: error while parsing merge windows.") + return t.client.Comment(mr, fmt.Sprintf("Failed to schedule merge: Error while parsing merge windows.\n\n%s", err.Error())) } if nextActiveStartTime.Before(now) { return t.mergeMR(mr) @@ -113,7 +133,7 @@ func (t Task) mergeMR(mr *gitlab.MergeRequest) error { // We need to recheck MRs - we might in the interim have merged other things that led to conflicts rmr, err := t.client.RefreshMr(mr) if err != nil { - return t.client.Comment(mr, "Failed to merge: error while refreshing merge request data.") + return t.client.Comment(mr, fmt.Sprintf("Failed to merge: error while refreshing merge request data.\n\n%s", err.Error())) } if !client.IsMergeable(rmr) { @@ -122,7 +142,7 @@ func (t Task) mergeMR(mr *gitlab.MergeRequest) error { err = t.client.MergeMr(rmr) if err != nil { - return t.client.Comment(mr, "Failed to merge: error while merging.") + return t.client.Comment(mr, fmt.Sprintf("Failed to merge: Error while merging.\n\n%s", err.Error())) } return nil diff --git a/task/task_test.go b/task/task_test.go new file mode 100644 index 0000000..7c5d51b --- /dev/null +++ b/task/task_test.go @@ -0,0 +1,166 @@ +package task_test + +import ( + "errors" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + mock_client "github.com/vshn/gitlab-scheduled-merge/client/mock" + "github.com/vshn/gitlab-scheduled-merge/task" + "github.com/xanzy/go-gitlab" + "go.uber.org/mock/gomock" +) + +type testClock struct{} + +func (testClock) Now() time.Time { + time, _ := time.Parse(time.RFC3339, "2024-06-27T10:30:00+02:00") + return time +} + +type hasSubstr struct { + values []string +} + +func (m hasSubstr) Matches(arg interface{}) bool { + sarg := arg.(string) + for _, s := range m.values { + if !strings.Contains(sarg, s) { + return false + } + } + return true +} +func (m hasSubstr) String() string { + return strings.Join(m.values, ", ") +} + +func Test_RunTask(t *testing.T) { + mctrl := gomock.NewController(t) + mock := mock_client.NewMockGitlabClient(mctrl) + config := task.TaskConfig{ + MergeRequestScheduledLabel: "scheduled", + ConfigFilePath: ".config-file.yml", + } + + subject := task.NewTaskWithClock(mock, config, testClock{}) + + mrs := mrList() + gomock.InOrder( + mock.EXPECT().ListMrsWithLabel(gomock.Any()).Return(mrs, nil), + mock.EXPECT().GetConfigFileForMR(mrs[0], ".config-file.yml").Return(activeMergeWindow(), nil), + mock.EXPECT().RefreshMr(mrs[0]).Return(mrs[0], nil), + mock.EXPECT().MergeMr(mrs[0]).Return(nil), + mock.EXPECT().GetConfigFileForMR(mrs[1], ".config-file.yml").Return(inactiveMergeWindow(), nil), + mock.EXPECT().Comment(mrs[1], gomock.Not(hasSubstr{[]string{"Failed"}})).Return(nil), + ) + + err := subject.Run() + + require.NoError(t, err) + +} + +func Test_RunTask_TimeZoneShenanigans(t *testing.T) { + mctrl := gomock.NewController(t) + mock := mock_client.NewMockGitlabClient(mctrl) + config := task.TaskConfig{ + MergeRequestScheduledLabel: "scheduled", + ConfigFilePath: ".config-file.yml", + } + + subject := task.NewTaskWithClock(mock, config, testClock{}) + + mrs := mrList() + gomock.InOrder( + mock.EXPECT().ListMrsWithLabel(gomock.Any()).Return(mrs, nil), + mock.EXPECT().GetConfigFileForMR(mrs[0], ".config-file.yml").Return(inactiveMergeWindowWithLocation(), nil), + mock.EXPECT().Comment(mrs[0], gomock.Not(hasSubstr{[]string{"Failed"}})).Return(nil), + mock.EXPECT().GetConfigFileForMR(mrs[1], ".config-file.yml").Return(inactiveMergeWindowWithWeek(), nil), + mock.EXPECT().Comment(mrs[1], gomock.Not(hasSubstr{[]string{"Failed"}})).Return(nil), + ) + + err := subject.Run() + + require.NoError(t, err) + +} + +func Test_RunTask_WithError(t *testing.T) { + mctrl := gomock.NewController(t) + mock := mock_client.NewMockGitlabClient(mctrl) + config := task.TaskConfig{ + MergeRequestScheduledLabel: "scheduled", + ConfigFilePath: ".config-file.yml", + } + + subject := task.NewTaskWithClock(mock, config, testClock{}) + + mrs := mrList() + gomock.InOrder( + mock.EXPECT().ListMrsWithLabel(gomock.Any()).Return(mrs, nil), + mock.EXPECT().GetConfigFileForMR(mrs[0], ".config-file.yml").Return(nil, errors.New("ERROR FAIL HALP")), + mock.EXPECT().Comment(mrs[0], hasSubstr{[]string{"Failed"}}).Return(nil), + mock.EXPECT().GetConfigFileForMR(mrs[1], ".config-file.yml").Return(inactiveMergeWindowWithWeek(), nil), + mock.EXPECT().Comment(mrs[1], gomock.Not(hasSubstr{[]string{"Failed"}})).Return(errors.New("COMMENT FAILED")), + ) + + err := subject.Run() + + require.Error(t, err) + +} + +func mrList() []*gitlab.MergeRequest { + f := &gitlab.MergeRequest{ + IID: 1, + DetailedMergeStatus: "mergeable", + } + g := &gitlab.MergeRequest{ + IID: 2, + } + return []*gitlab.MergeRequest{f, g} +} + +func activeMergeWindow() *[]byte { + yaml := []byte(` +mergeWindows: +- schedule: + cron: '0 10 * * *' + location: 'Europe/Zurich' + maxDelay: '1h'`) + return &yaml +} + +func inactiveMergeWindowWithLocation() *[]byte { + yaml := []byte(` +mergeWindows: +- schedule: + cron: '0 10 * * *' + location: 'America/New_York' + maxDelay: '1h'`) + return &yaml +} + +func inactiveMergeWindowWithWeek() *[]byte { + yaml := []byte(` +mergeWindows: +- schedule: + cron: '0 10 * * *' + isoWeek: '@odd' + location: 'Europe/Zurich' + maxDelay: '1h'`) + return &yaml +} + +func inactiveMergeWindow() *[]byte { + yaml := []byte(` +mergeWindows: +- schedule: + cron: '0 20 * * *' + location: 'Europe/Zurich' + maxDelay: '1h'`) + return &yaml +}