mirror of https://github.com/grafana/grafana.git
Add some unit test coverage
This commit is contained in:
parent
1f9f2b2df1
commit
420c9674d2
|
@ -0,0 +1,52 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAuthorSignature(t *testing.T) {
|
||||
t.Run("should store and retrieve author signature", func(t *testing.T) {
|
||||
expected := CommitSignature{
|
||||
Name: "John Doe",
|
||||
Email: "john@example.com",
|
||||
When: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC),
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = WithAuthorSignature(ctx, expected)
|
||||
|
||||
result := GetAuthorSignature(ctx)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, expected.Name, result.Name)
|
||||
require.Equal(t, expected.Email, result.Email)
|
||||
require.Equal(t, expected.When, result.When)
|
||||
})
|
||||
|
||||
t.Run("should return nil when no signature is set", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
result := GetAuthorSignature(ctx)
|
||||
require.Nil(t, result)
|
||||
})
|
||||
|
||||
t.Run("should return copy of signature", func(t *testing.T) {
|
||||
original := CommitSignature{
|
||||
Name: "John Doe",
|
||||
Email: "john@example.com",
|
||||
When: time.Now(),
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = WithAuthorSignature(ctx, original)
|
||||
|
||||
result1 := GetAuthorSignature(ctx)
|
||||
result2 := GetAuthorSignature(ctx)
|
||||
|
||||
require.NotNil(t, result1)
|
||||
require.NotNil(t, result2)
|
||||
require.NotSame(t, result1, result2)
|
||||
})
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.49.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.50.0. DO NOT EDIT.
|
||||
|
||||
package github
|
||||
|
||||
|
@ -202,9 +202,17 @@ func (_m *MockClient) CreateWebhook(ctx context.Context, owner string, repositor
|
|||
var r0 WebhookConfig
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, WebhookConfig) (WebhookConfig, error)); ok {
|
||||
r0, r1 = rf(ctx, owner, repository, cfg)
|
||||
return rf(ctx, owner, repository, cfg)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, WebhookConfig) WebhookConfig); ok {
|
||||
r0 = rf(ctx, owner, repository, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(WebhookConfig)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, WebhookConfig) error); ok {
|
||||
r1 = rf(ctx, owner, repository, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
|
@ -526,8 +534,7 @@ func (_m *MockClient) UpdateFile(ctx context.Context, owner string, repository s
|
|||
func NewMockClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
},
|
||||
) *MockClient {
|
||||
}) *MockClient {
|
||||
mock := &MockClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v66/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
func TestIsAuthenticated(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
statusCode int
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
statusCode: http.StatusOK,
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "unauthorized",
|
||||
statusCode: http.StatusUnauthorized,
|
||||
wantErr: apierrors.NewUnauthorized("token is invalid or expired"),
|
||||
},
|
||||
{
|
||||
name: "forbidden",
|
||||
statusCode: http.StatusForbidden,
|
||||
wantErr: apierrors.NewUnauthorized("token is revoked or has insufficient permissions"),
|
||||
},
|
||||
{
|
||||
name: "service unavailable",
|
||||
statusCode: http.StatusServiceUnavailable,
|
||||
wantErr: ErrServiceUnavailable,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client := newMockClient(t, mockClientOpts{
|
||||
statusCode: tt.statusCode,
|
||||
})
|
||||
impl := NewRealClient(client)
|
||||
|
||||
err := impl.IsAuthenticated(context.Background())
|
||||
if tt.wantErr != nil {
|
||||
assert.Equal(t, tt.wantErr, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoExists(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
statusCode int
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "repo exists",
|
||||
statusCode: http.StatusOK,
|
||||
want: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "repo does not exist",
|
||||
statusCode: http.StatusNotFound,
|
||||
want: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "error response",
|
||||
statusCode: http.StatusInternalServerError,
|
||||
want: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client := newMockClient(t, mockClientOpts{
|
||||
statusCode: tt.statusCode,
|
||||
})
|
||||
impl := NewRealClient(client)
|
||||
|
||||
got, err := impl.RepoExists(context.Background(), "owner", "repo")
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetContents(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
mockResponse interface{}
|
||||
statusCode int
|
||||
wantFile bool
|
||||
wantDir bool
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "get file contents",
|
||||
path: "path/to/file.txt",
|
||||
mockResponse: &github.RepositoryContent{
|
||||
Type: github.String("file"),
|
||||
Content: github.String("content"),
|
||||
},
|
||||
statusCode: http.StatusOK,
|
||||
wantFile: true,
|
||||
wantDir: false,
|
||||
},
|
||||
{
|
||||
name: "get directory contents",
|
||||
path: "path/to/dir",
|
||||
mockResponse: []*github.RepositoryContent{
|
||||
{
|
||||
Type: github.String("dir"),
|
||||
Path: github.String("subdir"),
|
||||
},
|
||||
{
|
||||
Type: github.String("file"),
|
||||
Path: github.String("file.txt"),
|
||||
},
|
||||
},
|
||||
statusCode: http.StatusOK,
|
||||
wantFile: false,
|
||||
wantDir: true,
|
||||
},
|
||||
{
|
||||
name: "path traversal attempt",
|
||||
path: "../secret",
|
||||
wantErr: ErrPathTraversalDisallowed,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
path: "nonexistent",
|
||||
statusCode: http.StatusNotFound,
|
||||
wantErr: ErrResourceNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client := newMockClient(t, mockClientOpts{
|
||||
statusCode: tt.statusCode,
|
||||
mockResponse: tt.mockResponse,
|
||||
})
|
||||
impl := NewRealClient(client)
|
||||
|
||||
file, dir, err := impl.GetContents(context.Background(), "owner", "repo", tt.path, "main")
|
||||
if tt.wantErr != nil {
|
||||
assert.Equal(t, tt.wantErr, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.wantFile {
|
||||
assert.NotNil(t, file)
|
||||
assert.Nil(t, dir)
|
||||
}
|
||||
if tt.wantDir {
|
||||
assert.Nil(t, file)
|
||||
assert.NotNil(t, dir)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Mock client implementation
|
||||
type mockClientOpts struct {
|
||||
statusCode int
|
||||
mockResponse interface{}
|
||||
}
|
||||
|
||||
func newMockClient(_ *testing.T, _ mockClientOpts) *github.Client {
|
||||
// Create a mock client that returns the specified status code and response
|
||||
client := github.NewClient(nil)
|
||||
// TODO: Configure mock client with opts.statusCode and opts.mockResponse
|
||||
return client
|
||||
}
|
|
@ -47,6 +47,7 @@ type FileTreeEntry struct {
|
|||
Blob bool
|
||||
}
|
||||
|
||||
//go:generate mockery --name Repository --structname MockRepository --inpackage --filename repository_mock.go
|
||||
type Repository interface {
|
||||
// The saved Kubernetes object.
|
||||
Config() *provisioning.Repository
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
// Code generated by mockery v2.50.0. DO NOT EDIT.
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
context "context"
|
||||
http "net/http"
|
||||
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
|
||||
)
|
||||
|
||||
// MockRepository is an autogenerated mock type for the Repository type
|
||||
type MockRepository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Config provides a mock function with no fields
|
||||
func (_m *MockRepository) Config() *v0alpha1.Repository {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Config")
|
||||
}
|
||||
|
||||
var r0 *v0alpha1.Repository
|
||||
if rf, ok := ret.Get(0).(func() *v0alpha1.Repository); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0alpha1.Repository)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, path, ref, data, message
|
||||
func (_m *MockRepository) Create(ctx context.Context, path string, ref string, data []byte, message string) error {
|
||||
ret := _m.Called(ctx, path, ref, data, message)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Create")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, []byte, string) error); ok {
|
||||
r0 = rf(ctx, path, ref, data, message)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, path, ref, message
|
||||
func (_m *MockRepository) Delete(ctx context.Context, path string, ref string, message string) error {
|
||||
ret := _m.Called(ctx, path, ref, message)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Delete")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
|
||||
r0 = rf(ctx, path, ref, message)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// History provides a mock function with given fields: ctx, path, ref
|
||||
func (_m *MockRepository) History(ctx context.Context, path string, ref string) ([]v0alpha1.HistoryItem, error) {
|
||||
ret := _m.Called(ctx, path, ref)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for History")
|
||||
}
|
||||
|
||||
var r0 []v0alpha1.HistoryItem
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]v0alpha1.HistoryItem, error)); ok {
|
||||
return rf(ctx, path, ref)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) []v0alpha1.HistoryItem); ok {
|
||||
r0 = rf(ctx, path, ref)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]v0alpha1.HistoryItem)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
|
||||
r1 = rf(ctx, path, ref)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Read provides a mock function with given fields: ctx, path, ref
|
||||
func (_m *MockRepository) Read(ctx context.Context, path string, ref string) (*FileInfo, error) {
|
||||
ret := _m.Called(ctx, path, ref)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Read")
|
||||
}
|
||||
|
||||
var r0 *FileInfo
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) (*FileInfo, error)); ok {
|
||||
return rf(ctx, path, ref)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) *FileInfo); ok {
|
||||
r0 = rf(ctx, path, ref)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*FileInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
|
||||
r1 = rf(ctx, path, ref)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ReadTree provides a mock function with given fields: ctx, ref
|
||||
func (_m *MockRepository) ReadTree(ctx context.Context, ref string) ([]FileTreeEntry, error) {
|
||||
ret := _m.Called(ctx, ref)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ReadTree")
|
||||
}
|
||||
|
||||
var r0 []FileTreeEntry
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) ([]FileTreeEntry, error)); ok {
|
||||
return rf(ctx, ref)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []FileTreeEntry); ok {
|
||||
r0 = rf(ctx, ref)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]FileTreeEntry)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, ref)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Test provides a mock function with given fields: ctx
|
||||
func (_m *MockRepository) Test(ctx context.Context) (*v0alpha1.TestResults, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Test")
|
||||
}
|
||||
|
||||
var r0 *v0alpha1.TestResults
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) (*v0alpha1.TestResults, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context) *v0alpha1.TestResults); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0alpha1.TestResults)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, path, ref, data, message
|
||||
func (_m *MockRepository) Update(ctx context.Context, path string, ref string, data []byte, message string) error {
|
||||
ret := _m.Called(ctx, path, ref, data, message)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Update")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, []byte, string) error); ok {
|
||||
r0 = rf(ctx, path, ref, data, message)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Validate provides a mock function with no fields
|
||||
func (_m *MockRepository) Validate() field.ErrorList {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Validate")
|
||||
}
|
||||
|
||||
var r0 field.ErrorList
|
||||
if rf, ok := ret.Get(0).(func() field.ErrorList); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(field.ErrorList)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Webhook provides a mock function with given fields: ctx, req
|
||||
func (_m *MockRepository) Webhook(ctx context.Context, req *http.Request) (*v0alpha1.WebhookResponse, error) {
|
||||
ret := _m.Called(ctx, req)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Webhook")
|
||||
}
|
||||
|
||||
var r0 *v0alpha1.WebhookResponse
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *http.Request) (*v0alpha1.WebhookResponse, error)); ok {
|
||||
return rf(ctx, req)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *http.Request) *v0alpha1.WebhookResponse); ok {
|
||||
r0 = rf(ctx, req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0alpha1.WebhookResponse)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *http.Request) error); ok {
|
||||
r1 = rf(ctx, req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Write provides a mock function with given fields: ctx, path, ref, data, message
|
||||
func (_m *MockRepository) Write(ctx context.Context, path string, ref string, data []byte, message string) error {
|
||||
ret := _m.Called(ctx, path, ref, data, message)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Write")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, []byte, string) error); ok {
|
||||
r0 = rf(ctx, path, ref, data, message)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewMockRepository creates a new instance of MockRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockRepository(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockRepository {
|
||||
mock := &MockRepository{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestValidateRepository(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repository *MockRepository
|
||||
expectedErrs int
|
||||
validateError func(t *testing.T, errors field.ErrorList)
|
||||
}{
|
||||
{
|
||||
name: "valid repository",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 0,
|
||||
},
|
||||
{
|
||||
name: "missing title",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 1,
|
||||
validateError: func(t *testing.T, errors field.ErrorList) {
|
||||
require.Contains(t, errors.ToAggregate().Error(), "spec.title: Required value")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sync enabled without target",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
Sync: provisioning.SyncOptions{
|
||||
Enabled: true,
|
||||
IntervalSeconds: 10,
|
||||
},
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 1,
|
||||
validateError: func(t *testing.T, errors field.ErrorList) {
|
||||
require.Contains(t, errors.ToAggregate().Error(), "spec.sync.target: Required value")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sync interval too low",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
Sync: provisioning.SyncOptions{
|
||||
Enabled: true,
|
||||
Target: "test",
|
||||
IntervalSeconds: 5,
|
||||
},
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 1,
|
||||
validateError: func(t *testing.T, errors field.ErrorList) {
|
||||
require.Contains(t, errors.ToAggregate().Error(), "spec.sync.intervalSeconds: Invalid value")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reserved name",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sql",
|
||||
},
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 1,
|
||||
validateError: func(t *testing.T, errors field.ErrorList) {
|
||||
require.Contains(t, errors.ToAggregate().Error(), "metadata.name: Invalid value")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mismatched local config",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
Type: provisioning.GitHubRepositoryType,
|
||||
Local: &provisioning.LocalRepositoryConfig{},
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 1,
|
||||
validateError: func(t *testing.T, errors field.ErrorList) {
|
||||
require.Contains(t, errors.ToAggregate().Error(), "spec.local: Invalid value")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mismatched github config",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
Type: provisioning.LocalRepositoryType,
|
||||
GitHub: &provisioning.GitHubRepositoryConfig{},
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 1,
|
||||
validateError: func(t *testing.T, errors field.ErrorList) {
|
||||
require.Contains(t, errors.ToAggregate().Error(), "spec.github: Invalid value")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mismatched s3 config",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
Type: provisioning.LocalRepositoryType,
|
||||
S3: &provisioning.S3RepositoryConfig{},
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 1,
|
||||
validateError: func(t *testing.T, errors field.ErrorList) {
|
||||
require.Contains(t, errors.ToAggregate().Error(), "spec.s3: Invalid value")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple validation errors",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sql",
|
||||
},
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Sync: provisioning.SyncOptions{
|
||||
Enabled: true,
|
||||
IntervalSeconds: 5,
|
||||
},
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedErrs: 4, // Updated from 3 to 4 to match actual errors:
|
||||
// 1. missing title
|
||||
// 2. sync target missing
|
||||
// 3. sync interval too low
|
||||
// 4. reserved name
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
errors := ValidateRepository(tt.repository)
|
||||
require.Len(t, errors, tt.expectedErrs)
|
||||
if tt.validateError != nil {
|
||||
tt.validateError(t, errors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestRepository(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repository *MockRepository
|
||||
expectedCode int
|
||||
expectedErrs []string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "validation fails",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
// Missing required title
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
return m
|
||||
}(),
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
expectedErrs: []string{"spec.title: Required value: a repository title must be given"},
|
||||
},
|
||||
{
|
||||
name: "test passes",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
m.On("Test", mock.Anything).Return(&provisioning.TestResults{
|
||||
Code: http.StatusOK,
|
||||
Success: true,
|
||||
}, nil)
|
||||
return m
|
||||
}(),
|
||||
expectedCode: http.StatusOK,
|
||||
expectedErrs: nil,
|
||||
},
|
||||
{
|
||||
name: "test fails with error",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
m.On("Test", mock.Anything).Return(nil, fmt.Errorf("test error"))
|
||||
return m
|
||||
}(),
|
||||
expectedError: fmt.Errorf("test error"),
|
||||
},
|
||||
{
|
||||
name: "test fails with results",
|
||||
repository: func() *MockRepository {
|
||||
m := NewMockRepository(t)
|
||||
m.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
},
|
||||
})
|
||||
m.On("Validate").Return(field.ErrorList{})
|
||||
m.On("Test", mock.Anything).Return(&provisioning.TestResults{
|
||||
Code: http.StatusBadRequest,
|
||||
Success: false,
|
||||
Errors: []string{"test failed"},
|
||||
}, nil)
|
||||
return m
|
||||
}(),
|
||||
expectedCode: http.StatusBadRequest,
|
||||
expectedErrs: []string{"test failed"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
results, err := TestRepository(context.Background(), tt.repository)
|
||||
|
||||
if tt.expectedError != nil {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.expectedError.Error(), err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, results)
|
||||
require.Equal(t, tt.expectedCode, results.Code)
|
||||
|
||||
if tt.expectedErrs != nil {
|
||||
require.Equal(t, tt.expectedErrs, results.Errors)
|
||||
require.False(t, results.Success)
|
||||
} else {
|
||||
require.True(t, results.Success)
|
||||
require.Empty(t, results.Errors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTester_TestRepository(t *testing.T) {
|
||||
tester := &Tester{}
|
||||
|
||||
// Test that it properly delegates to TestRepository
|
||||
repository := NewMockRepository(t)
|
||||
repository.On("Config").Return(&provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Title: "Test Repo",
|
||||
},
|
||||
})
|
||||
repository.On("Validate").Return(field.ErrorList{})
|
||||
repository.On("Test", mock.Anything).Return(&provisioning.TestResults{
|
||||
Code: http.StatusOK,
|
||||
Success: true,
|
||||
}, nil)
|
||||
|
||||
results, err := tester.TestRepository(context.Background(), repository)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, results)
|
||||
require.Equal(t, http.StatusOK, results.Code)
|
||||
require.True(t, results.Success)
|
||||
}
|
Loading…
Reference in New Issue