1010 lines
37 KiB
Go
1010 lines
37 KiB
Go
package move
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
|
|
"github.com/grafana/grafana/apps/provisioning/pkg/repository"
|
|
"github.com/grafana/grafana/apps/provisioning/pkg/safepath"
|
|
"github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs"
|
|
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type mockReaderWriter struct {
|
|
*repository.MockRepository
|
|
}
|
|
|
|
func (m *mockReaderWriter) Move(ctx context.Context, oldPath, newPath, ref, message string) error {
|
|
args := m.Called(ctx, oldPath, newPath, ref, message)
|
|
return args.Error(0)
|
|
}
|
|
|
|
func TestMoveWorker_IsSupported(t *testing.T) {
|
|
metrics := jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry())
|
|
tests := []struct {
|
|
name string
|
|
job provisioning.Job
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "move action is supported",
|
|
job: provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
},
|
|
},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "delete action is not supported",
|
|
job: provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionDelete,
|
|
},
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "pull action is not supported",
|
|
job: provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionPull,
|
|
},
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "push action is not supported",
|
|
job: provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionPush,
|
|
},
|
|
},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
worker := NewWorker(nil, nil, nil, metrics)
|
|
result := worker.IsSupported(context.Background(), tt.job)
|
|
require.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMoveWorker_ProcessMissingMoveSettings(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
},
|
|
}
|
|
|
|
worker := NewWorker(nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), nil, job, nil)
|
|
require.EqualError(t, err, "missing move settings")
|
|
}
|
|
|
|
func TestMoveWorker_ProcessMissingTargetPath(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path"},
|
|
},
|
|
},
|
|
}
|
|
|
|
worker := NewWorker(nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), nil, job, nil)
|
|
require.EqualError(t, err, "target path is required for move operation")
|
|
}
|
|
|
|
func TestMoveWorker_ProcessInvalidTargetPath(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path"},
|
|
TargetPath: "target.txt", // This is not a directory path
|
|
},
|
|
},
|
|
}
|
|
|
|
worker := NewWorker(nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), nil, job, nil)
|
|
require.EqualError(t, err, "target path must be a directory (should end with '/')")
|
|
}
|
|
|
|
func TestMoveWorker_ProcessNotReaderWriter(t *testing.T) {
|
|
job := provisioning.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-job",
|
|
},
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path"},
|
|
TargetPath: "new/location/",
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := repository.NewMockRepository(t)
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.MatchedBy(func(opts repository.StageOptions) bool {
|
|
return !opts.PushOnWrites && opts.Timeout == 10*time.Minute &&
|
|
opts.Mode == repository.StageModeCommitOnlyOnce &&
|
|
opts.CommitOnlyOnceMessage == "Move files from Grafana test-job"
|
|
}), mock.Anything).Return(errors.New("move job submitted targeting repository that is not a ReaderWriter"))
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Return()
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
|
|
worker := NewWorker(nil, mockWrapFn.Execute, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.EqualError(t, err, "move files in repository: move job submitted targeting repository that is not a ReaderWriter")
|
|
}
|
|
|
|
func TestMoveWorker_ProcessWrapFnError(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path"},
|
|
TargetPath: "new/location/",
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := repository.NewMockRepository(t)
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(errors.New("stage failed"))
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Return()
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
|
|
worker := NewWorker(nil, mockWrapFn.Execute, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.EqualError(t, err, "move files in repository: stage failed")
|
|
}
|
|
|
|
func TestMoveWorker_ProcessMoveFilesSuccess(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path1", "test/path2"},
|
|
TargetPath: "new/location/",
|
|
Ref: "main",
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.MatchedBy(func(opts repository.StageOptions) bool {
|
|
return !opts.PushOnWrites && opts.Timeout == 10*time.Minute &&
|
|
opts.Mode == repository.StageModeCommitOnlyOnce &&
|
|
opts.CommitOnlyOnceMessage != "" && opts.Ref == "main"
|
|
}), mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockRepo, false)
|
|
})
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 2).Return()
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test/path1 to new/location/path1").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test/path2 to new/location/path2").Return()
|
|
mockProgress.On("TooManyErrors").Return(nil).Twice()
|
|
mockProgress.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{})
|
|
|
|
mockRepo.On("Move", mock.Anything, "test/path1", "new/location/path1", "main", "Move test/path1 to new/location/path1").Return(nil)
|
|
mockRepo.On("Move", mock.Anything, "test/path2", "new/location/path2", "main", "Move test/path2 to new/location/path2").Return(nil)
|
|
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Path == "test/path1" && result.Action == repository.FileActionRenamed && result.Error == nil
|
|
})).Return()
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Path == "test/path2" && result.Action == repository.FileActionRenamed && result.Error == nil
|
|
})).Return()
|
|
|
|
worker := NewWorker(nil, mockWrapFn.Execute, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestMoveWorker_ProcessMoveFilesWithError(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path1", "test/path2"},
|
|
TargetPath: "new/location/",
|
|
Ref: "main",
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockRepo, false)
|
|
})
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 2).Return()
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test/path1 to new/location/path1").Return()
|
|
|
|
moveError := errors.New("move failed")
|
|
mockRepo.On("Move", mock.Anything, "test/path1", "new/location/path1", "main", "Move test/path1 to new/location/path1").Return(moveError)
|
|
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Path == "test/path1" && result.Action == repository.FileActionRenamed && errors.Is(result.Error, moveError)
|
|
})).Return()
|
|
mockProgress.On("TooManyErrors").Return(errors.New("too many errors"))
|
|
|
|
worker := NewWorker(nil, mockWrapFn.Execute, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.EqualError(t, err, "move files in repository: too many errors")
|
|
}
|
|
|
|
func TestMoveWorker_ProcessWithSyncWorker(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path"},
|
|
TargetPath: "new/location/",
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockSyncWorker := jobs.NewMockWorker(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockRepo, false)
|
|
})
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Return()
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test/path to new/location/path").Return()
|
|
mockProgress.On("TooManyErrors").Return(nil)
|
|
|
|
mockRepo.On("Move", mock.Anything, "test/path", "new/location/path", "", "Move test/path to new/location/path").Return(nil)
|
|
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Path == "test/path" && result.Action == repository.FileActionRenamed && result.Error == nil
|
|
})).Return()
|
|
|
|
mockProgress.On("ResetResults").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "pull resources").Return()
|
|
mockProgress.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{})
|
|
|
|
mockSyncWorker.On("Process", mock.Anything, mockRepo, mock.MatchedBy(func(syncJob provisioning.Job) bool {
|
|
return syncJob.Spec.Pull != nil && !syncJob.Spec.Pull.Incremental
|
|
}), mockProgress).Return(nil)
|
|
|
|
worker := NewWorker(mockSyncWorker, mockWrapFn.Execute, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestMoveWorker_ProcessSyncWorkerError(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path"},
|
|
TargetPath: "new/location/",
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockSyncWorker := jobs.NewMockWorker(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockRepo, false)
|
|
})
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Return()
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test/path to new/location/path").Return()
|
|
mockProgress.On("TooManyErrors").Return(nil)
|
|
|
|
mockRepo.On("Move", mock.Anything, "test/path", "new/location/path", "", "Move test/path to new/location/path").Return(nil)
|
|
|
|
mockProgress.On("Record", mock.Anything, mock.Anything).Return()
|
|
mockProgress.On("ResetResults").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "pull resources").Return()
|
|
|
|
syncError := errors.New("sync failed")
|
|
mockSyncWorker.On("Process", mock.Anything, mockRepo, mock.Anything, mockProgress).Return(syncError)
|
|
|
|
worker := NewWorker(mockSyncWorker, mockWrapFn.Execute, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.EqualError(t, err, "pull resources: sync failed")
|
|
}
|
|
|
|
func TestMoveWorker_moveFiles(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
paths []string
|
|
moveResults []error
|
|
tooManyErrors error
|
|
expectedError string
|
|
expectedCalls int
|
|
}{
|
|
{
|
|
name: "single file success",
|
|
paths: []string{"test/file1.yaml"},
|
|
moveResults: []error{nil},
|
|
expectedCalls: 1,
|
|
},
|
|
{
|
|
name: "multiple files success",
|
|
paths: []string{"test/file1.yaml", "test/file2.yaml", "test/file3.yaml"},
|
|
moveResults: []error{nil, nil, nil},
|
|
expectedCalls: 3,
|
|
},
|
|
{
|
|
name: "mixed files and folders",
|
|
paths: []string{"file.json", "folder/", "nested/file.yaml"},
|
|
moveResults: []error{nil, nil, nil},
|
|
expectedCalls: 3,
|
|
},
|
|
{
|
|
name: "single file with error continues",
|
|
paths: []string{"test/file1.yaml", "test/file2.yaml"},
|
|
moveResults: []error{errors.New("move failed"), nil},
|
|
expectedCalls: 2,
|
|
},
|
|
{
|
|
name: "too many errors stops processing",
|
|
paths: []string{"test/file1.yaml", "test/file2.yaml", "test/file3.yaml"},
|
|
moveResults: []error{errors.New("move failed")},
|
|
tooManyErrors: errors.New("too many errors"),
|
|
expectedError: "too many errors",
|
|
expectedCalls: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
|
|
opts := provisioning.MoveJobOptions{
|
|
TargetPath: "new/location/",
|
|
Ref: "main",
|
|
}
|
|
|
|
for i, path := range tt.paths {
|
|
if i < len(tt.moveResults) {
|
|
// Use the same logic as constructTargetPath to build expected target
|
|
expectedTarget := "new/location/" + filepath.Base(path)
|
|
if safepath.IsDir(path) {
|
|
expectedTarget += "/"
|
|
}
|
|
mockRepo.On("Move", mock.Anything, path, expectedTarget, "main", "Move "+path+" to "+expectedTarget).Return(tt.moveResults[i]).Once()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving "+path+" to "+expectedTarget).Return().Once()
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Path == path && result.Action == repository.FileActionRenamed
|
|
})).Return().Once()
|
|
|
|
if tt.tooManyErrors != nil && i == 0 {
|
|
mockProgress.On("TooManyErrors").Return(tt.tooManyErrors).Once()
|
|
} else {
|
|
mockProgress.On("TooManyErrors").Return(nil).Once()
|
|
}
|
|
}
|
|
}
|
|
|
|
worker := NewWorker(nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.moveFiles(context.Background(), mockRepo, mockProgress, opts, tt.paths...)
|
|
|
|
if tt.expectedError != "" {
|
|
require.EqualError(t, err, tt.expectedError)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
mockRepo.AssertExpectations(t)
|
|
mockProgress.AssertExpectations(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMoveWorker_constructTargetPath(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
jobTargetPath string
|
|
sourcePath string
|
|
expectedTarget string
|
|
}{
|
|
{
|
|
name: "simple file in directory",
|
|
jobTargetPath: "moved/",
|
|
sourcePath: "dashboard.json",
|
|
expectedTarget: "moved/dashboard.json",
|
|
},
|
|
{
|
|
name: "nested file in directory",
|
|
jobTargetPath: "archived/",
|
|
sourcePath: "folder/dashboard.json",
|
|
expectedTarget: "archived/dashboard.json",
|
|
},
|
|
{
|
|
name: "deeply nested directory target",
|
|
jobTargetPath: "deep/nested/target/",
|
|
sourcePath: "source/file.yaml",
|
|
expectedTarget: "deep/nested/target/file.yaml",
|
|
},
|
|
{
|
|
name: "folder to folder",
|
|
jobTargetPath: "new-location/",
|
|
sourcePath: "old-folder/",
|
|
expectedTarget: "new-location/old-folder/",
|
|
},
|
|
{
|
|
name: "nested folder move",
|
|
jobTargetPath: "archive/",
|
|
sourcePath: "deep/nested/folder/",
|
|
expectedTarget: "archive/folder/",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
worker := NewWorker(nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
result := worker.constructTargetPath(tt.jobTargetPath, tt.sourcePath)
|
|
require.Equal(t, tt.expectedTarget, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMoveWorker_ProcessWithResourceReferences(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Paths: []string{"test/path1"},
|
|
TargetPath: "new/location/",
|
|
Resources: []provisioning.ResourceRef{
|
|
{
|
|
Name: "dashboard-uid",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
mockResourcesFactory := resources.NewMockRepositoryResourcesFactory(t)
|
|
mockRepoResources := resources.NewMockRepositoryResources(t)
|
|
mockSyncWorker := jobs.NewMockWorker(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockRepo, false)
|
|
})
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 2).Return() // 1 path + 1 resource
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Resolving resource paths").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Finding path for resource dashboard.grafana.app/Dashboard/dashboard-uid").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test/path1 to new/location/path1").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving dashboard/file.yaml to new/location/file.yaml").Return()
|
|
mockProgress.On("TooManyErrors").Return(nil).Times(2)
|
|
mockProgress.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{})
|
|
mockResourcesFactory.On("Client", mock.Anything, mockRepo).Return(mockRepoResources, nil)
|
|
mockRepoResources.On("FindResourcePath", mock.Anything, "dashboard-uid", schema.GroupVersionKind{
|
|
Group: "dashboard.grafana.app",
|
|
Kind: "Dashboard",
|
|
}).Return("dashboard/file.yaml", nil)
|
|
|
|
mockRepo.On("Move", mock.Anything, "test/path1", "new/location/path1", "", "Move test/path1 to new/location/path1").Return(nil)
|
|
mockRepo.On("Move", mock.Anything, "dashboard/file.yaml", "new/location/file.yaml", "", "Move dashboard/file.yaml to new/location/file.yaml").Return(nil)
|
|
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Path == "test/path1" && result.Action == repository.FileActionRenamed && result.Error == nil
|
|
})).Return()
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Path == "dashboard/file.yaml" && result.Action == repository.FileActionRenamed && result.Error == nil
|
|
})).Return()
|
|
|
|
// Add expectations for sync worker (called when ref is empty)
|
|
mockProgress.On("ResetResults").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "pull resources").Return()
|
|
mockSyncWorker.On("Process", mock.Anything, mockRepo, mock.MatchedBy(func(syncJob provisioning.Job) bool {
|
|
return syncJob.Spec.Pull != nil && !syncJob.Spec.Pull.Incremental
|
|
}), mockProgress).Return(nil)
|
|
|
|
worker := NewWorker(mockSyncWorker, mockWrapFn.Execute, mockResourcesFactory, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestMoveWorker_ProcessResourceReferencesError(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
TargetPath: "new/location/",
|
|
Resources: []provisioning.ResourceRef{
|
|
{
|
|
Name: "non-existent-uid",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
mockResourcesFactory := resources.NewMockRepositoryResourcesFactory(t)
|
|
mockRepoResources := resources.NewMockRepositoryResources(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockRepo, false)
|
|
})
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Return() // 1 resource
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Resolving resource paths").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Finding path for resource dashboard.grafana.app/Dashboard/non-existent-uid").Return()
|
|
mockProgress.On("TooManyErrors").Return(nil)
|
|
mockProgress.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{})
|
|
|
|
mockResourcesFactory.On("Client", mock.Anything, mockRepo).Return(mockRepoResources, nil)
|
|
resourceError := errors.New("resource not found")
|
|
mockRepoResources.On("FindResourcePath", mock.Anything, "non-existent-uid", schema.GroupVersionKind{
|
|
Group: "dashboard.grafana.app",
|
|
Kind: "Dashboard",
|
|
}).Return("", resourceError)
|
|
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Name == "non-existent-uid" && result.Group == "dashboard.grafana.app" &&
|
|
result.Action == repository.FileActionRenamed &&
|
|
result.Error != nil && result.Error.Error() == "find path for resource dashboard.grafana.app/Dashboard/non-existent-uid: resource not found"
|
|
})).Return()
|
|
|
|
// Add expectations for sync worker (called when ref is empty)
|
|
mockSyncWorker := jobs.NewMockWorker(t)
|
|
mockProgress.On("ResetResults").Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "pull resources").Return()
|
|
mockSyncWorker.On("Process", mock.Anything, mockRepo, mock.MatchedBy(func(syncJob provisioning.Job) bool {
|
|
return syncJob.Spec.Pull != nil && !syncJob.Spec.Pull.Incremental
|
|
}), mockProgress).Return(nil)
|
|
|
|
worker := NewWorker(mockSyncWorker, mockWrapFn.Execute, mockResourcesFactory, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.NoError(t, err) // Should continue despite individual resource errors
|
|
}
|
|
|
|
func TestMoveWorker_ProcessResourcesFactoryError(t *testing.T) {
|
|
job := provisioning.Job{
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
TargetPath: "new/location/",
|
|
Resources: []provisioning.ResourceRef{
|
|
{
|
|
Name: "dashboard-uid",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
mockResourcesFactory := resources.NewMockRepositoryResourcesFactory(t)
|
|
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockRepo, false)
|
|
})
|
|
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Return() // 1 resource
|
|
mockProgress.On("StrictMaxErrors", 1).Return()
|
|
mockProgress.On("SetMessage", mock.Anything, "Resolving resource paths").Return()
|
|
|
|
factoryError := errors.New("failed to create resources client")
|
|
mockResourcesFactory.On("Client", mock.Anything, mockRepo).Return(nil, factoryError)
|
|
|
|
worker := NewWorker(nil, mockWrapFn.Execute, mockResourcesFactory, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.EqualError(t, err, "move files in repository: create repository resources client: failed to create resources client")
|
|
}
|
|
|
|
func TestMoveWorker_resolveResourcesToPaths(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
resources []provisioning.ResourceRef
|
|
resourcePaths map[string]string
|
|
resourceErrors map[string]error
|
|
tooManyErrors error
|
|
expectedPaths []string
|
|
expectedError string
|
|
expectContinue bool
|
|
}{
|
|
{
|
|
name: "single resource success",
|
|
resources: []provisioning.ResourceRef{
|
|
{
|
|
Name: "dashboard-uid",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
},
|
|
resourcePaths: map[string]string{
|
|
"dashboard-uid": "dashboards/test.json",
|
|
},
|
|
expectedPaths: []string{"dashboards/test.json"},
|
|
},
|
|
{
|
|
name: "multiple resources success",
|
|
resources: []provisioning.ResourceRef{
|
|
{
|
|
Name: "dashboard1",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
{
|
|
Name: "dashboard2",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
},
|
|
resourcePaths: map[string]string{
|
|
"dashboard1": "dashboards/dash1.json",
|
|
"dashboard2": "dashboards/dash2.json",
|
|
},
|
|
expectedPaths: []string{"dashboards/dash1.json", "dashboards/dash2.json"},
|
|
},
|
|
{
|
|
name: "resource not found continues",
|
|
resources: []provisioning.ResourceRef{
|
|
{
|
|
Name: "non-existent",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
{
|
|
Name: "existing",
|
|
Kind: "Dashboard",
|
|
Group: "dashboard.grafana.app",
|
|
},
|
|
},
|
|
resourcePaths: map[string]string{
|
|
"existing": "dashboards/existing.json",
|
|
},
|
|
resourceErrors: map[string]error{
|
|
"non-existent": errors.New("not found"),
|
|
},
|
|
expectedPaths: []string{"dashboards/existing.json"},
|
|
expectContinue: true,
|
|
},
|
|
{
|
|
name: "empty resources list",
|
|
resources: []provisioning.ResourceRef{},
|
|
expectedPaths: nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mockRepo := &mockReaderWriter{
|
|
MockRepository: repository.NewMockRepository(t),
|
|
}
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockResourcesFactory := resources.NewMockRepositoryResourcesFactory(t)
|
|
mockRepoResources := resources.NewMockRepositoryResources(t)
|
|
|
|
if len(tt.resources) > 0 {
|
|
mockProgress.On("SetMessage", mock.Anything, "Resolving resource paths").Return()
|
|
mockResourcesFactory.On("Client", mock.Anything, mockRepo).Return(mockRepoResources, nil)
|
|
|
|
for _, resource := range tt.resources {
|
|
mockProgress.On("SetMessage", mock.Anything, fmt.Sprintf("Finding path for resource %s/%s/%s", resource.Group, resource.Kind, resource.Name)).Return()
|
|
|
|
gvk := schema.GroupVersionKind{
|
|
Group: resource.Group,
|
|
Kind: resource.Kind,
|
|
}
|
|
|
|
if path, ok := tt.resourcePaths[resource.Name]; ok {
|
|
mockRepoResources.On("FindResourcePath", mock.Anything, resource.Name, gvk).Return(path, nil)
|
|
} else if err, ok := tt.resourceErrors[resource.Name]; ok {
|
|
mockRepoResources.On("FindResourcePath", mock.Anything, resource.Name, gvk).Return("", err)
|
|
mockProgress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool {
|
|
return result.Name == resource.Name && result.Group == resource.Group &&
|
|
result.Action == repository.FileActionRenamed && result.Error != nil
|
|
})).Return()
|
|
if tt.tooManyErrors != nil {
|
|
mockProgress.On("TooManyErrors").Return(tt.tooManyErrors)
|
|
} else {
|
|
mockProgress.On("TooManyErrors").Return(nil)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
worker := NewWorker(nil, nil, mockResourcesFactory, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
paths, err := worker.resolveResourcesToPaths(context.Background(), mockRepo, mockProgress, tt.resources)
|
|
|
|
if tt.expectedError != "" {
|
|
require.EqualError(t, err, tt.expectedError)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
require.Equal(t, tt.expectedPaths, paths)
|
|
|
|
mockResourcesFactory.AssertExpectations(t)
|
|
if len(tt.resources) > 0 {
|
|
mockRepoResources.AssertExpectations(t)
|
|
}
|
|
mockProgress.AssertExpectations(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMoveWorker_deduplicatePaths(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input []string
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "no duplicates",
|
|
input: []string{"path1", "path2", "path3"},
|
|
expected: []string{"path1", "path2", "path3"},
|
|
},
|
|
{
|
|
name: "with duplicates",
|
|
input: []string{"path1", "path2", "path1", "path3", "path2"},
|
|
expected: []string{"path1", "path2", "path3"},
|
|
},
|
|
{
|
|
name: "empty slice",
|
|
input: []string{},
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "single item",
|
|
input: []string{"path1"},
|
|
expected: []string{"path1"},
|
|
},
|
|
{
|
|
name: "all duplicates",
|
|
input: []string{"path1", "path1", "path1"},
|
|
expected: []string{"path1"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := deduplicatePaths(tt.input)
|
|
require.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMoveWorker_RefURLsSetWithRef(t *testing.T) {
|
|
mockRepoWithURLs := repository.NewMockRepositoryWithURLs(t)
|
|
config := &provisioning.Repository{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-repo",
|
|
Namespace: "test-namespace",
|
|
},
|
|
Spec: provisioning.RepositorySpec{
|
|
Type: provisioning.GitHubRepositoryType,
|
|
},
|
|
}
|
|
mockRepoWithURLs.On("Config").Return(config).Maybe() // Config may be called multiple times
|
|
|
|
// Mock RefURLs method to return expected URLs
|
|
expectedRefURLs := &provisioning.RepositoryURLs{
|
|
SourceURL: "https://github.com/grafana/grafana/tree/feature-branch",
|
|
CompareURL: "https://github.com/grafana/grafana/compare/main...feature-branch",
|
|
NewPullRequestURL: "https://github.com/grafana/grafana/compare/main...feature-branch?quick_pull=1&labels=grafana",
|
|
}
|
|
mockRepoWithURLs.On("RefURLs", mock.Anything, "feature-branch").Return(expectedRefURLs, nil)
|
|
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Once()
|
|
mockProgress.On("StrictMaxErrors", 1).Once()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test.json to target/test.json").Once()
|
|
mockProgress.On("Record", mock.Anything, mock.Anything).Once()
|
|
mockProgress.On("TooManyErrors").Return(nil).Once()
|
|
mockProgress.On("SetRefURLs", mock.Anything, expectedRefURLs).Once()
|
|
mockProgress.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{})
|
|
mockReaderWriter := repository.NewMockReaderWriter(t)
|
|
mockReaderWriter.On("Move", mock.Anything, "test.json", "target/test.json", "feature-branch", "Move test.json to target/test.json").Return(nil)
|
|
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepoWithURLs, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, opts repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockReaderWriter, true)
|
|
})
|
|
|
|
mockResourcesFactory := resources.NewMockRepositoryResourcesFactory(t)
|
|
|
|
job := provisioning.Job{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "test-job"},
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Ref: "feature-branch",
|
|
Paths: []string{"test.json"},
|
|
TargetPath: "target/",
|
|
},
|
|
},
|
|
}
|
|
|
|
worker := NewWorker(nil, mockWrapFn.Execute, mockResourcesFactory, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepoWithURLs, job, mockProgress)
|
|
require.NoError(t, err)
|
|
|
|
// Verify that SetRefURLs was called with the expected RefURLs
|
|
mockProgress.AssertExpectations(t)
|
|
mockRepoWithURLs.AssertExpectations(t)
|
|
}
|
|
|
|
func TestMoveWorker_RefURLsNotSetWithoutRef(t *testing.T) {
|
|
mockRepoWithURLs := repository.NewMockRepositoryWithURLs(t)
|
|
config := &provisioning.Repository{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-repo",
|
|
Namespace: "test-namespace",
|
|
},
|
|
Spec: provisioning.RepositorySpec{
|
|
Type: provisioning.GitHubRepositoryType,
|
|
},
|
|
}
|
|
mockRepoWithURLs.On("Config").Return(config).Maybe() // Config may be called multiple times
|
|
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Once()
|
|
mockProgress.On("StrictMaxErrors", 1).Once()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test.json to target/test.json").Once()
|
|
mockProgress.On("Record", mock.Anything, mock.Anything).Once()
|
|
mockProgress.On("TooManyErrors").Return(nil).Once()
|
|
mockProgress.On("ResetResults").Once()
|
|
mockProgress.On("SetMessage", mock.Anything, "pull resources").Once()
|
|
mockProgress.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{})
|
|
// SetRefURLs should NOT be called since no ref is specified
|
|
|
|
mockReaderWriter := repository.NewMockReaderWriter(t)
|
|
mockReaderWriter.On("Move", mock.Anything, "test.json", "target/test.json", "", "Move test.json to target/test.json").Return(nil)
|
|
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepoWithURLs, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, opts repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockReaderWriter, true)
|
|
})
|
|
|
|
mockSyncWorker := jobs.NewMockWorker(t)
|
|
mockSyncWorker.On("Process", mock.Anything, mockRepoWithURLs, mock.Anything, mockProgress).Return(nil)
|
|
|
|
mockResourcesFactory := resources.NewMockRepositoryResourcesFactory(t)
|
|
|
|
job := provisioning.Job{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "test-job"},
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
// No ref specified
|
|
Paths: []string{"test.json"},
|
|
TargetPath: "target/",
|
|
},
|
|
},
|
|
}
|
|
|
|
worker := NewWorker(mockSyncWorker, mockWrapFn.Execute, mockResourcesFactory, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepoWithURLs, job, mockProgress)
|
|
require.NoError(t, err)
|
|
|
|
// Verify that SetRefURLs was NOT called since no ref was specified
|
|
mockProgress.AssertExpectations(t)
|
|
}
|
|
|
|
func TestMoveWorker_RefURLsNotSetForNonURLRepository(t *testing.T) {
|
|
mockRepo := repository.NewMockRepository(t)
|
|
config := &provisioning.Repository{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-repo",
|
|
Namespace: "test-namespace",
|
|
},
|
|
Spec: provisioning.RepositorySpec{
|
|
Type: provisioning.GitRepositoryType, // Regular git repo, not GitHub
|
|
},
|
|
}
|
|
mockRepo.On("Config").Return(config).Maybe() // Config may be called multiple times
|
|
|
|
mockProgress := jobs.NewMockJobProgressRecorder(t)
|
|
mockProgress.On("SetTotal", mock.Anything, 1).Once()
|
|
mockProgress.On("StrictMaxErrors", 1).Once()
|
|
mockProgress.On("SetMessage", mock.Anything, "Moving test.json to target/test.json").Once()
|
|
mockProgress.On("Record", mock.Anything, mock.Anything).Once()
|
|
mockProgress.On("TooManyErrors").Return(nil).Once()
|
|
mockProgress.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{})
|
|
// SetRefURLs should NOT be called since repo doesn't support URLs
|
|
|
|
mockReaderWriter := repository.NewMockReaderWriter(t)
|
|
mockReaderWriter.On("Move", mock.Anything, "test.json", "target/test.json", "feature-branch", "Move test.json to target/test.json").Return(nil)
|
|
|
|
mockWrapFn := repository.NewMockWrapWithStageFn(t)
|
|
mockWrapFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, opts repository.StageOptions, fn func(repository.Repository, bool) error) error {
|
|
return fn(mockReaderWriter, true)
|
|
})
|
|
|
|
mockResourcesFactory := resources.NewMockRepositoryResourcesFactory(t)
|
|
|
|
job := provisioning.Job{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "test-job"},
|
|
Spec: provisioning.JobSpec{
|
|
Action: provisioning.JobActionMove,
|
|
Move: &provisioning.MoveJobOptions{
|
|
Ref: "feature-branch",
|
|
Paths: []string{"test.json"},
|
|
TargetPath: "target/",
|
|
},
|
|
},
|
|
}
|
|
|
|
worker := NewWorker(nil, mockWrapFn.Execute, mockResourcesFactory, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
|
|
err := worker.Process(context.Background(), mockRepo, job, mockProgress)
|
|
require.NoError(t, err)
|
|
|
|
// Verify that SetRefURLs was NOT called since repo doesn't support URLs
|
|
mockProgress.AssertExpectations(t)
|
|
}
|