mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 06:51:49 +08:00

* Spike: Extras * Attempt to wire it up * Hack * Fix issue with jobs * Wire more things up * Fix more wiring stuff * Remove webhook secret key from main registration * Move secret encryption also outside register * Add TODOs in code * Add more explanations * Move connectors to different package * Move pull request job into webhooks * Separate registration * Remove duplicate files * Fix missing function * Extract webhook repository logic out of the core github repository * Use status patcher in webhook connector * Fix change in go mod * Change hooks signature * Remove TODOs * Remove Webhook methos from go-git * Remove leftover * Fix mistake in OpenAPI spec * Fix some tests * Fix some issues * Fix linting
1484 lines
40 KiB
Go
1484 lines
40 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
field "k8s.io/apimachinery/pkg/util/validation/field"
|
|
|
|
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
|
|
)
|
|
|
|
func TestLocalResolver(t *testing.T) {
|
|
// Create a temporary directory structure
|
|
tempDir := t.TempDir()
|
|
|
|
// Create directory structure with multiple levels
|
|
dirs := []string{
|
|
"level1",
|
|
"level1/level2",
|
|
"level1/level2/level3",
|
|
"another/path",
|
|
}
|
|
|
|
for _, dir := range dirs {
|
|
dirPath := filepath.Join(tempDir, dir)
|
|
err := os.MkdirAll(dirPath, 0750)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create some files at different levels
|
|
files := map[string]string{
|
|
"root.txt": "root content",
|
|
"level1/file1.txt": "level 1 content",
|
|
"level1/level2/file2.txt": "level 2 content",
|
|
"level1/level2/level3/file3.txt": "level 3 content",
|
|
"another/path/file.txt": "another path content",
|
|
}
|
|
|
|
for path, content := range files {
|
|
filePath := filepath.Join(tempDir, path)
|
|
err := os.WriteFile(filePath, []byte(content), 0644)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create resolver with the temp directory as permitted prefix
|
|
resolver := &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
HomePath: "./",
|
|
}
|
|
|
|
// Test resolving paths
|
|
for _, dir := range dirs {
|
|
fullPath, err := resolver.LocalPath(filepath.Join(tempDir, dir))
|
|
require.NoError(t, err)
|
|
require.Equal(t, filepath.Join(tempDir, dir), fullPath)
|
|
}
|
|
|
|
// Test repository with the temp directory
|
|
repo := NewLocal(&provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
}, resolver)
|
|
|
|
// Verify we can read the tree
|
|
tree, err := repo.ReadTree(context.Background(), "")
|
|
require.NoError(t, err)
|
|
|
|
// Collect all paths from the tree
|
|
paths := make([]string, 0, len(tree))
|
|
for _, item := range tree {
|
|
paths = append(paths, item.Path)
|
|
}
|
|
|
|
// Sort paths for consistent comparison
|
|
sort.Strings(paths)
|
|
|
|
// Verify all directories and files are present
|
|
expectedPaths := []string{
|
|
"another",
|
|
"another/path",
|
|
"another/path/file.txt",
|
|
"level1",
|
|
"level1/file1.txt",
|
|
"level1/level2",
|
|
"level1/level2/file2.txt",
|
|
"level1/level2/level3",
|
|
"level1/level2/level3/file3.txt",
|
|
"root.txt",
|
|
}
|
|
require.Equal(t, expectedPaths, paths)
|
|
|
|
// Test reading a specific file
|
|
file, err := repo.Read(context.Background(), "level1/level2/file2.txt", "")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "level1/level2/file2.txt", file.Path)
|
|
require.Equal(t, []byte("level 2 content"), file.Data)
|
|
}
|
|
|
|
func TestLocal(t *testing.T) {
|
|
// Valid paths test cases
|
|
for _, tc := range []struct {
|
|
Name string
|
|
Path string
|
|
PermittedPrefixes []string
|
|
ExpectedPath string
|
|
}{
|
|
{"relative path", "devenv/test", []string{"/home/grafana"}, "/home/grafana/devenv/test/"},
|
|
{"absolute path", "/devenv/test", []string{"/devenv"}, "/devenv/test/"},
|
|
{"relative path with multiple prefixes", "devenv/test", []string{"/home/grafana", "/devenv"}, "/home/grafana/devenv/test/"},
|
|
{"absolute path with multiple prefixes", "/devenv/test", []string{"/home/grafana", "/devenv"}, "/devenv/test/"},
|
|
} {
|
|
t.Run("valid: "+tc.Name, func(t *testing.T) {
|
|
r := NewLocal(&provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tc.Path,
|
|
},
|
|
},
|
|
}, &LocalFolderResolver{PermittedPrefixes: tc.PermittedPrefixes, HomePath: "/home/grafana"})
|
|
|
|
assert.Equal(t, tc.ExpectedPath, r.path, "expected path to be resolved")
|
|
for _, err := range r.Validate() {
|
|
assert.Fail(t, "unexpected validation failure", "unexpected validation error on field %s: %s", err.Field, err.ErrorBody())
|
|
}
|
|
})
|
|
}
|
|
|
|
// Invalid paths test cases
|
|
for _, tc := range []struct {
|
|
Name string
|
|
Path string
|
|
PermittedPrefixes []string
|
|
}{
|
|
{"no configured paths", "invalid/path", nil},
|
|
{"path traversal escape", "../../etc/passwd", []string{"/home/grafana"}},
|
|
{"unconfigured prefix", "invalid/path", []string{"devenv", "/tmp", "test"}},
|
|
} {
|
|
t.Run("invalid: "+tc.Name, func(t *testing.T) {
|
|
r := NewLocal(&provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tc.Path,
|
|
},
|
|
},
|
|
}, &LocalFolderResolver{PermittedPrefixes: tc.PermittedPrefixes, HomePath: "/home/grafana"})
|
|
|
|
require.Empty(t, r.path, "no path should be resolved")
|
|
|
|
errs := r.Validate()
|
|
require.NotEmpty(t, errs, "expected validation errors")
|
|
for _, err := range errs {
|
|
if !assert.Equal(t, "spec.local.path", err.Field) {
|
|
assert.FailNow(t, "unexpected validation failure", "unexpected validation error on field %s: %s", err.Field, err.ErrorBody())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Test(t *testing.T) {
|
|
// Test cases for the Test method
|
|
testCases := []struct {
|
|
name string
|
|
path string
|
|
pathExists bool
|
|
expectedCode int
|
|
expectedResult bool
|
|
}{
|
|
{
|
|
name: "valid path that exists",
|
|
path: "valid/path/",
|
|
pathExists: true,
|
|
expectedCode: http.StatusOK,
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
name: "valid path that doesn't exist",
|
|
path: "valid/nonexistent",
|
|
pathExists: false,
|
|
expectedCode: http.StatusBadRequest,
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
name: "invalid path with path traversal",
|
|
path: "../../../etc/passwd",
|
|
pathExists: false,
|
|
expectedCode: http.StatusBadRequest,
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
name: "invalid path with special characters",
|
|
path: "path/with/*/wildcards",
|
|
pathExists: false,
|
|
expectedCode: http.StatusBadRequest,
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
name: "empty path",
|
|
path: "",
|
|
pathExists: false,
|
|
expectedCode: http.StatusBadRequest,
|
|
expectedResult: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Create a temporary directory for testing
|
|
tempDir := t.TempDir()
|
|
|
|
// Setup the test directory if needed
|
|
testPath := filepath.Join(tempDir, tc.path)
|
|
if tc.pathExists {
|
|
err := os.MkdirAll(testPath, 0750)
|
|
require.NoError(t, err, "Failed to create test directory")
|
|
}
|
|
|
|
// Create a resolver that permits the temp directory
|
|
resolver := &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
HomePath: tempDir,
|
|
}
|
|
|
|
// Create the repository with the test path
|
|
repo := NewLocal(&provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tc.path,
|
|
},
|
|
},
|
|
}, resolver)
|
|
|
|
// If we're testing a valid path, set it to our test path
|
|
if tc.path != "" {
|
|
repo.path = testPath
|
|
}
|
|
|
|
// Call the Test method
|
|
results, err := repo.Test(context.Background())
|
|
|
|
// Verify results
|
|
require.NoError(t, err, "Test method should not return an error")
|
|
assert.Equal(t, tc.expectedResult, results.Success, "Success flag should match expected")
|
|
assert.Equal(t, tc.expectedCode, results.Code, "Status code should match expected")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Validate(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
config *provisioning.LocalRepositoryConfig
|
|
permittedPath string
|
|
expectedErrs []field.Error
|
|
}{
|
|
{
|
|
name: "valid configuration",
|
|
config: &provisioning.LocalRepositoryConfig{Path: "valid/path"},
|
|
permittedPath: "valid",
|
|
expectedErrs: nil,
|
|
},
|
|
{
|
|
name: "missing local config",
|
|
config: nil,
|
|
permittedPath: "valid",
|
|
expectedErrs: []field.Error{
|
|
{
|
|
Type: field.ErrorTypeRequired,
|
|
Field: "spec.local",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "empty path",
|
|
config: &provisioning.LocalRepositoryConfig{Path: ""},
|
|
permittedPath: "valid",
|
|
expectedErrs: []field.Error{
|
|
{
|
|
Type: field.ErrorTypeRequired,
|
|
Field: "spec.local.path",
|
|
Detail: "must enter a path to local file",
|
|
BadValue: "",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "path not in permitted prefixes",
|
|
config: &provisioning.LocalRepositoryConfig{Path: "invalid/path"},
|
|
permittedPath: "valid",
|
|
expectedErrs: []field.Error{
|
|
{
|
|
Type: field.ErrorTypeInvalid,
|
|
Field: "spec.local.path",
|
|
BadValue: "invalid/path",
|
|
Detail: "the path given ('invalid/path') is invalid for a local repository (the path matches no permitted prefix)",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unsafe path with directory traversal",
|
|
config: &provisioning.LocalRepositoryConfig{Path: "../../../etc/passwd"},
|
|
permittedPath: "valid",
|
|
expectedErrs: []field.Error{
|
|
{
|
|
Type: field.ErrorTypeInvalid,
|
|
Field: "spec.local.path",
|
|
BadValue: "../../../etc/passwd",
|
|
Detail: "path contains traversal attempt (./ or ../)",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Create a temporary directory for testing
|
|
tempDir := t.TempDir()
|
|
permittedPath := filepath.Join(tempDir, tc.permittedPath)
|
|
|
|
// Create the permitted directory
|
|
if tc.permittedPath != "" {
|
|
err := os.MkdirAll(permittedPath, 0750)
|
|
require.NoError(t, err, "Failed to create permitted directory")
|
|
}
|
|
|
|
// Create a resolver that permits the specific path
|
|
resolver := &LocalFolderResolver{
|
|
PermittedPrefixes: []string{permittedPath},
|
|
HomePath: tempDir,
|
|
}
|
|
|
|
// Create repository config
|
|
repoConfig := &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: tc.config,
|
|
},
|
|
}
|
|
|
|
// Create the repository
|
|
repo := NewLocal(repoConfig, resolver)
|
|
|
|
// Call the Validate method
|
|
errors := repo.Validate()
|
|
|
|
// Verify results
|
|
if tc.expectedErrs == nil {
|
|
assert.Empty(t, errors, "Expected no validation errors")
|
|
} else {
|
|
assert.Len(t, errors, len(tc.expectedErrs), "Number of validation errors should match expected")
|
|
for i, expectedErr := range tc.expectedErrs {
|
|
assert.Equal(t, expectedErr.Type, errors[i].Type, "Error type should match")
|
|
assert.Equal(t, expectedErr.Field, errors[i].Field, "Error field should match")
|
|
assert.Equal(t, expectedErr.Detail, errors[i].Detail, "Error detail should match")
|
|
assert.Equal(t, expectedErr.BadValue, errors[i].BadValue, "Error bad value should match")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInvalidLocalFolderError(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
path string
|
|
additionalInfo string
|
|
expectedMsg string
|
|
expectedStatus metav1.Status
|
|
}{
|
|
{
|
|
name: "basic error",
|
|
path: "/invalid/path",
|
|
additionalInfo: "not allowed",
|
|
expectedMsg: "the path given ('/invalid/path') is invalid for a local repository (not allowed)",
|
|
expectedStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: http.StatusBadRequest,
|
|
Reason: metav1.StatusReasonBadRequest,
|
|
Message: "the path given ('/invalid/path') is invalid for a local repository (not allowed)",
|
|
},
|
|
},
|
|
{
|
|
name: "empty path",
|
|
path: "",
|
|
additionalInfo: "path cannot be empty",
|
|
expectedMsg: "the path given ('') is invalid for a local repository (path cannot be empty)",
|
|
expectedStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: http.StatusBadRequest,
|
|
Reason: metav1.StatusReasonBadRequest,
|
|
Message: "the path given ('') is invalid for a local repository (path cannot be empty)",
|
|
},
|
|
},
|
|
{
|
|
name: "no permitted prefixes",
|
|
path: "/some/path",
|
|
additionalInfo: "no permitted prefixes were configured",
|
|
expectedMsg: "the path given ('/some/path') is invalid for a local repository (no permitted prefixes were configured)",
|
|
expectedStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: http.StatusBadRequest,
|
|
Reason: metav1.StatusReasonBadRequest,
|
|
Message: "the path given ('/some/path') is invalid for a local repository (no permitted prefixes were configured)",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Create the error
|
|
err := &InvalidLocalFolderError{
|
|
Path: tc.path,
|
|
AdditionalInfo: tc.additionalInfo,
|
|
}
|
|
|
|
// Test Error() method
|
|
assert.Equal(t, tc.expectedMsg, err.Error(), "Error message should match expected")
|
|
|
|
// Test Status() method
|
|
status := err.Status()
|
|
assert.Equal(t, tc.expectedStatus.Status, status.Status, "Status should match")
|
|
assert.Equal(t, tc.expectedStatus.Code, status.Code, "Status code should match")
|
|
assert.Equal(t, tc.expectedStatus.Reason, status.Reason, "Status reason should match")
|
|
assert.Equal(t, tc.expectedStatus.Message, status.Message, "Status message should match")
|
|
|
|
// Verify it implements the expected interfaces
|
|
var apiStatus apierrors.APIStatus
|
|
assert.True(t, errors.As(err, &apiStatus), "Should implement APIStatus interface")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Delete(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
setup func(t *testing.T) (string, *localRepository)
|
|
path string
|
|
ref string
|
|
comment string
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "delete existing file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
// Create a temporary directory for testing
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a test file
|
|
testFilePath := filepath.Join(tempDir, "test-file.txt")
|
|
err := os.WriteFile(testFilePath, []byte("test content"), 0600)
|
|
require.NoError(t, err)
|
|
|
|
// Create repository with the temp directory as permitted prefix
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-file.txt",
|
|
ref: "",
|
|
comment: "test delete",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "delete non-existent file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
// Create a temporary directory for testing
|
|
tempDir := t.TempDir()
|
|
|
|
// Create repository with the temp directory as permitted prefix
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "non-existent-file.txt",
|
|
ref: "",
|
|
comment: "test delete non-existent",
|
|
expectedErr: os.ErrNotExist,
|
|
},
|
|
{
|
|
name: "delete with ref not supported",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
// Create a temporary directory for testing
|
|
tempDir := t.TempDir()
|
|
|
|
// Create repository with the temp directory as permitted prefix
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-file.txt",
|
|
ref: "main",
|
|
comment: "test delete with ref",
|
|
expectedErr: apierrors.NewBadRequest("local repository does not support ref"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup test environment
|
|
_, repo := tc.setup(t)
|
|
|
|
// Execute the delete operation
|
|
err := repo.Delete(context.Background(), tc.path, tc.ref, tc.comment)
|
|
|
|
// Verify results
|
|
if tc.expectedErr != nil {
|
|
require.Error(t, err)
|
|
if errors.Is(tc.expectedErr, os.ErrNotExist) {
|
|
assert.True(t, errors.Is(err, os.ErrNotExist), "Expected os.ErrNotExist error")
|
|
} else {
|
|
assert.Equal(t, tc.expectedErr.Error(), err.Error(), "Error message should match expected")
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
// Verify the file was actually deleted
|
|
_, statErr := os.Stat(filepath.Join(repo.path, tc.path))
|
|
assert.True(t, errors.Is(statErr, os.ErrNotExist), "File should be deleted")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Update(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
setup func(t *testing.T) (string, *localRepository)
|
|
path string
|
|
ref string
|
|
data []byte
|
|
comment string
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "update existing file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a file to update
|
|
filePath := filepath.Join(tempDir, "existing-file.txt")
|
|
require.NoError(t, os.WriteFile(filePath, []byte("initial content"), 0600))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "existing-file.txt",
|
|
ref: "",
|
|
data: []byte("updated content"),
|
|
comment: "",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "update existing directory as a file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a directory
|
|
dirPath := filepath.Join(tempDir, "existing-dir")
|
|
require.NoError(t, os.MkdirAll(dirPath, 0700))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "existing-dir",
|
|
ref: "",
|
|
data: []byte("file content"),
|
|
comment: "",
|
|
expectedErr: apierrors.NewBadRequest("path exists but it is a directory"),
|
|
},
|
|
{
|
|
name: "update non-existent file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "non-existent-file.txt",
|
|
ref: "",
|
|
data: []byte("content"),
|
|
comment: "",
|
|
expectedErr: ErrFileNotFound,
|
|
},
|
|
{
|
|
name: "update directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a directory
|
|
dirPath := filepath.Join(tempDir, "test-dir")
|
|
require.NoError(t, os.MkdirAll(dirPath, 0700))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-dir/",
|
|
ref: "",
|
|
data: []byte("content"),
|
|
comment: "",
|
|
expectedErr: apierrors.NewBadRequest("cannot update a directory"),
|
|
},
|
|
{
|
|
name: "update with ref",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a file to update
|
|
filePath := filepath.Join(tempDir, "test-file.txt")
|
|
require.NoError(t, os.WriteFile(filePath, []byte("initial content"), 0600))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-file.txt",
|
|
ref: "main",
|
|
data: []byte("updated content"),
|
|
comment: "test update with ref",
|
|
expectedErr: apierrors.NewBadRequest("local repository does not support ref"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup test environment
|
|
_, repo := tc.setup(t)
|
|
|
|
// Execute the update operation
|
|
err := repo.Update(context.Background(), tc.path, tc.ref, tc.data, tc.comment)
|
|
|
|
// Verify results
|
|
if tc.expectedErr != nil {
|
|
require.Error(t, err)
|
|
assert.Equal(t, tc.expectedErr.Error(), err.Error(), "Error message should match expected")
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
// Verify the file was actually updated
|
|
updatedContent, readErr := os.ReadFile(filepath.Join(repo.path, tc.path))
|
|
require.NoError(t, readErr)
|
|
assert.Equal(t, tc.data, updatedContent, "File content should be updated")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Write(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
setup func(t *testing.T) (string, *localRepository)
|
|
path string
|
|
ref string
|
|
data []byte
|
|
comment string
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "write new file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "new-file.txt",
|
|
data: []byte("new content"),
|
|
comment: "test write new file",
|
|
},
|
|
{
|
|
name: "overwrite existing file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a file to be overwritten
|
|
existingFilePath := filepath.Join(tempDir, "existing-file.txt")
|
|
require.NoError(t, os.WriteFile(existingFilePath, []byte("original content"), 0600))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "existing-file.txt",
|
|
data: []byte("updated content"),
|
|
comment: "test overwrite existing file",
|
|
},
|
|
{
|
|
name: "create directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "new-dir/",
|
|
data: nil,
|
|
comment: "test create directory",
|
|
},
|
|
{
|
|
name: "create file in nested directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "nested/dir/file.txt",
|
|
data: []byte("nested file content"),
|
|
comment: "test create file in nested directory",
|
|
},
|
|
{
|
|
name: "write with ref should fail",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-file.txt",
|
|
ref: "main",
|
|
data: []byte("content with ref"),
|
|
comment: "test write with ref",
|
|
expectedErr: apierrors.NewBadRequest("local repository does not support ref"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup test environment
|
|
_, repo := tc.setup(t)
|
|
|
|
// Execute the write operation
|
|
err := repo.Write(context.Background(), tc.path, tc.ref, tc.data, tc.comment)
|
|
|
|
// Verify results
|
|
if tc.expectedErr != nil {
|
|
require.Error(t, err)
|
|
assert.Equal(t, tc.expectedErr.Error(), err.Error(), "Error message should match expected")
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
// Verify the file or directory was created
|
|
targetPath := filepath.Join(repo.path, tc.path)
|
|
|
|
// Check if it's a directory
|
|
if strings.HasSuffix(tc.path, "/") || tc.data == nil {
|
|
info, statErr := os.Stat(targetPath)
|
|
require.NoError(t, statErr)
|
|
assert.True(t, info.IsDir(), "Path should be a directory")
|
|
} else {
|
|
// Verify file content
|
|
//nolint:gosec
|
|
content, readErr := os.ReadFile(targetPath)
|
|
require.NoError(t, readErr)
|
|
assert.Equal(t, tc.data, content, "File content should match written data")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Create(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
setup func(t *testing.T) (string, *localRepository)
|
|
path string
|
|
ref string
|
|
data []byte
|
|
comment string
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "create new file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "new-file.txt",
|
|
data: []byte("new content"),
|
|
comment: "test create new file",
|
|
},
|
|
{
|
|
name: "create file in nested directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "nested/dir/new-file.txt",
|
|
data: []byte("nested content"),
|
|
comment: "test create file in nested directory",
|
|
},
|
|
{
|
|
name: "create directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "new-dir/",
|
|
data: nil,
|
|
comment: "test create directory",
|
|
},
|
|
{
|
|
name: "create file that already exists",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a file that will conflict
|
|
existingFilePath := filepath.Join(tempDir, "existing-file.txt")
|
|
require.NoError(t, os.WriteFile(existingFilePath, []byte("original content"), 0600))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "existing-file.txt",
|
|
data: []byte("new content"),
|
|
comment: "test create existing file",
|
|
expectedErr: apierrors.NewAlreadyExists(schema.GroupResource{}, "existing-file.txt"),
|
|
},
|
|
{
|
|
name: "create directory with data",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "invalid-dir/",
|
|
data: []byte("directory with data"),
|
|
comment: "test create directory with data",
|
|
expectedErr: apierrors.NewBadRequest("data cannot be provided for a directory"),
|
|
},
|
|
{
|
|
name: "create with ref",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "file-with-ref.txt",
|
|
ref: "main",
|
|
data: []byte("content with ref"),
|
|
comment: "test create with ref",
|
|
expectedErr: apierrors.NewBadRequest("local repository does not support ref"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup test environment
|
|
_, repo := tc.setup(t)
|
|
|
|
// Execute the create operation
|
|
err := repo.Create(context.Background(), tc.path, tc.ref, tc.data, tc.comment)
|
|
|
|
// Verify results
|
|
if tc.expectedErr != nil {
|
|
require.Error(t, err)
|
|
assert.Equal(t, tc.expectedErr.Error(), err.Error(), "Error message should match expected")
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
// Verify the file or directory was created
|
|
targetPath := filepath.Join(repo.path, tc.path)
|
|
|
|
// Check if it's a directory
|
|
if strings.HasSuffix(tc.path, "/") || tc.data == nil {
|
|
info, statErr := os.Stat(targetPath)
|
|
require.NoError(t, statErr)
|
|
assert.True(t, info.IsDir(), "Path should be a directory")
|
|
} else {
|
|
// Verify file content
|
|
//nolint:gosec
|
|
content, readErr := os.ReadFile(targetPath)
|
|
require.NoError(t, readErr)
|
|
assert.Equal(t, tc.data, content, "File content should match written data")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Read(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
setup func(t *testing.T) (string, *localRepository)
|
|
path string
|
|
ref string
|
|
expectedErr error
|
|
expected *FileInfo
|
|
}{
|
|
{
|
|
name: "read existing file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a file to read
|
|
filePath := filepath.Join(tempDir, "test-file.txt")
|
|
fileContent := []byte("test content")
|
|
require.NoError(t, os.WriteFile(filePath, fileContent, 0600))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-file.txt",
|
|
expected: &FileInfo{
|
|
Path: "test-file.txt",
|
|
Modified: &metav1.Time{Time: time.Now()},
|
|
Data: []byte("test content"),
|
|
Hash: "1eebdf4fdc9fc7bf283031b93f9aef3338de9052",
|
|
},
|
|
},
|
|
{
|
|
name: "read non-existent file",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "non-existent-file.txt",
|
|
expectedErr: ErrFileNotFound,
|
|
},
|
|
{
|
|
name: "read with ref should fail",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a file to read
|
|
filePath := filepath.Join(tempDir, "test-file.txt")
|
|
fileContent := []byte("test content")
|
|
require.NoError(t, os.WriteFile(filePath, fileContent, 0600))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-file.txt",
|
|
ref: "main",
|
|
expectedErr: apierrors.NewBadRequest("local repository does not support ref"),
|
|
},
|
|
{
|
|
name: "read existing directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a directory to read
|
|
dirPath := filepath.Join(tempDir, "test-dir")
|
|
require.NoError(t, os.Mkdir(dirPath, 0750))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
path: "test-dir",
|
|
expected: &FileInfo{
|
|
Path: "test-dir",
|
|
Modified: &metav1.Time{Time: time.Now()},
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup test environment
|
|
_, repo := tc.setup(t)
|
|
|
|
// Execute the read operation
|
|
data, err := repo.Read(context.Background(), tc.path, tc.ref)
|
|
|
|
// Verify results
|
|
if tc.expectedErr != nil {
|
|
require.Error(t, err)
|
|
assert.Equal(t, tc.expectedErr.Error(), err.Error(), "Error message should match expected")
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.expected.Path, data.Path, "Path should match expected")
|
|
assert.NotNil(t, data.Modified, "Modified time should not be nil")
|
|
assert.Equal(t, tc.expected.Data, data.Data, "Data should match expected")
|
|
assert.Equal(t, tc.expected.Hash, data.Hash, "Hash should match expected")
|
|
assert.Empty(t, data.Ref, "Ref should be empty")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_ReadTree(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
setup func(t *testing.T) (string, *localRepository)
|
|
ref string
|
|
expectedErr error
|
|
expected []FileTreeEntry
|
|
}{
|
|
{
|
|
name: "read empty directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
expected: []FileTreeEntry{},
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "read directory with files",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Create a file structure
|
|
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "file1.txt"), []byte("content1"), 0600))
|
|
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "file2.txt"), []byte("content2"), 0600))
|
|
require.NoError(t, os.MkdirAll(filepath.Join(tempDir, "subdir"), 0700))
|
|
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "subdir", "file3.txt"), []byte("content3"), 0600))
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
expected: []FileTreeEntry{
|
|
{Path: "file1.txt", Blob: true, Size: 8},
|
|
{Path: "file2.txt", Blob: true, Size: 8},
|
|
{Path: "subdir", Blob: false},
|
|
{Path: "subdir/file3.txt", Blob: true, Size: 8},
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "read with ref",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: tempDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: tempDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
ref: "main",
|
|
expectedErr: apierrors.NewBadRequest("local repository does not support ref"),
|
|
},
|
|
{
|
|
name: "read non-existent directory",
|
|
setup: func(t *testing.T) (string, *localRepository) {
|
|
tempDir := t.TempDir()
|
|
nonExistentDir := filepath.Join(tempDir, "non-existent")
|
|
|
|
repo := &localRepository{
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: nonExistentDir,
|
|
},
|
|
},
|
|
},
|
|
resolver: &LocalFolderResolver{
|
|
PermittedPrefixes: []string{tempDir},
|
|
},
|
|
path: nonExistentDir,
|
|
}
|
|
|
|
return tempDir, repo
|
|
},
|
|
expected: []FileTreeEntry{},
|
|
expectedErr: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup test environment
|
|
_, repo := tc.setup(t)
|
|
|
|
// Execute the readTree operation
|
|
entries, err := repo.ReadTree(context.Background(), tc.ref)
|
|
|
|
// Verify results
|
|
if tc.expectedErr != nil {
|
|
require.Error(t, err)
|
|
assert.Equal(t, tc.expectedErr.Error(), err.Error(), "Error message should match expected")
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
if len(tc.expected) == 0 {
|
|
assert.Empty(t, entries, "Expected empty entries")
|
|
} else {
|
|
// Sort both expected and actual entries by path for comparison
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
return entries[i].Path < entries[j].Path
|
|
})
|
|
|
|
// We need to verify each entry individually since hash values will be different
|
|
assert.Equal(t, len(tc.expected), len(entries), "Number of entries should match")
|
|
|
|
for i, expected := range tc.expected {
|
|
if i < len(entries) {
|
|
assert.Equal(t, expected.Path, entries[i].Path, "Path should match")
|
|
assert.Equal(t, expected.Blob, entries[i].Blob, "Blob flag should match")
|
|
|
|
if expected.Blob {
|
|
assert.Equal(t, expected.Size, entries[i].Size, "Size should match")
|
|
assert.NotEmpty(t, entries[i].Hash, "Hash should not be empty for files")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLocalRepository_Config(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
config *provisioning.Repository
|
|
}{
|
|
{
|
|
name: "returns the same config that was provided",
|
|
config: &provisioning.Repository{
|
|
Spec: provisioning.RepositorySpec{
|
|
Local: &provisioning.LocalRepositoryConfig{
|
|
Path: "/some/path",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "returns nil config",
|
|
config: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Create repository with the test config
|
|
repo := &localRepository{
|
|
config: tc.config,
|
|
}
|
|
|
|
// Call the Config method
|
|
result := repo.Config()
|
|
|
|
// Verify the result is the same as the input config
|
|
assert.Equal(t, tc.config, result, "Config() should return the same config that was provided")
|
|
})
|
|
}
|
|
}
|