Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pkg/config/config_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type ConfigHandler interface {
// LoadConfig loads the configuration from the specified path
LoadConfig(path string) error

// LoadConfigString loads the configuration from the provided string content
LoadConfigString(content string) error

// GetString retrieves a string value for the specified key from the configuration
GetString(key string, defaultValue ...string) string

Expand Down Expand Up @@ -78,7 +81,6 @@ type ConfigHandler interface {

// BaseConfigHandler is a base implementation of the ConfigHandler interface
type BaseConfigHandler struct {
ConfigHandler
injector di.Injector
shell shell.Shell
config v1alpha1.Config
Expand Down
9 changes: 9 additions & 0 deletions pkg/config/mock_config_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type MockConfigHandler struct {
InitializeFunc func() error
SetSecretsProviderFunc func(provider secrets.SecretsProvider)
LoadConfigFunc func(path string) error
LoadConfigStringFunc func(content string) error
IsLoadedFunc func() bool
GetStringFunc func(key string, defaultValue ...string) string
GetIntFunc func(key string, defaultValue ...int) int
Expand Down Expand Up @@ -56,6 +57,14 @@ func (m *MockConfigHandler) LoadConfig(path string) error {
return nil
}

// LoadConfigString calls the mock LoadConfigStringFunc if set, otherwise returns nil
func (m *MockConfigHandler) LoadConfigString(content string) error {
if m.LoadConfigStringFunc != nil {
return m.LoadConfigStringFunc(content)
}
return nil
}

// IsLoaded calls the mock IsLoadedFunc if set, otherwise returns false
func (m *MockConfigHandler) IsLoaded() bool {
if m.IsLoadedFunc != nil {
Expand Down
40 changes: 36 additions & 4 deletions pkg/config/mock_config_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"reflect"
"testing"

"github.com/goccy/go-yaml"
"github.com/windsorcli/cli/api/v1alpha1"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/secrets"
Expand All @@ -29,6 +30,9 @@ func setupSafeMocks(injector ...di.Injector) *MockObjects {

// Mock necessary dependencies
mockShell := &shell.MockShell{}
mockShell.GetProjectRootFunc = func() (string, error) {
return "/tmp", nil
}
inj.Register("shell", mockShell)

// Mock secrets provider
Expand All @@ -40,10 +44,8 @@ func setupSafeMocks(injector ...di.Injector) *MockObjects {
return nil, nil
}

// Mock yamlUnmarshal to return an error
yamlUnmarshal = func(data []byte, v interface{}) error {
return nil
}
// Use real YAML unmarshaling
yamlUnmarshal = yaml.Unmarshal

// Mock osReadFile to simulate reading a file
osReadFile = func(filename string) ([]byte, error) {
Expand Down Expand Up @@ -726,3 +728,33 @@ func TestMockConfigHandler_IsLoaded(t *testing.T) {
}
})
}

func TestMockConfigHandler_LoadConfigString(t *testing.T) {
t.Run("WithFuncSet", func(t *testing.T) {
// Given a mock config handler with LoadConfigStringFunc set
handler := NewMockConfigHandler()
mockErr := fmt.Errorf("mock load config string error")
handler.LoadConfigStringFunc = func(content string) error { return mockErr }

// When LoadConfigString is called
err := handler.LoadConfigString("some content")

// Then the error should match the expected mock error
if err != mockErr {
t.Errorf("Expected error = %v, got = %v", mockErr, err)
}
})

t.Run("WithNoFuncSet", func(t *testing.T) {
// Given a mock config handler without LoadConfigStringFunc set
handler := NewMockConfigHandler()

// When LoadConfigString is called
err := handler.LoadConfigString("some content")

// Then no error should be returned
if err != nil {
t.Errorf("Expected error = %v, got = %v", nil, err)
}
})
}
38 changes: 23 additions & 15 deletions pkg/config/yaml_config_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
// YamlConfigHandler implements the ConfigHandler interface using goccy/go-yaml
type YamlConfigHandler struct {
BaseConfigHandler
config v1alpha1.Config
path string
defaultContextConfig v1alpha1.Context
}
Expand All @@ -28,6 +27,28 @@ func NewYamlConfigHandler(injector di.Injector) *YamlConfigHandler {
}
}

// LoadConfigString loads the configuration from the provided string content.
func (y *YamlConfigHandler) LoadConfigString(content string) error {
if content == "" {
return nil
}

if err := yamlUnmarshal([]byte(content), &y.BaseConfigHandler.config); err != nil {
return fmt.Errorf("error unmarshalling yaml: %w", err)
}

// Check and set the config version
if y.BaseConfigHandler.config.Version == "" {
y.BaseConfigHandler.config.Version = "v1alpha1"
} else if y.BaseConfigHandler.config.Version != "v1alpha1" {
return fmt.Errorf("unsupported config version: %s", y.BaseConfigHandler.config.Version)
}

y.BaseConfigHandler.loaded = true

return nil
}

// LoadConfig loads the configuration from the specified path. If the file does not exist, it does nothing.
func (y *YamlConfigHandler) LoadConfig(path string) error {
y.path = path
Expand All @@ -40,20 +61,7 @@ func (y *YamlConfigHandler) LoadConfig(path string) error {
return fmt.Errorf("error reading config file: %w", err)
}

if err := yamlUnmarshal(data, &y.config); err != nil {
return fmt.Errorf("error unmarshalling yaml: %w", err)
}

// Check and set the config version
if y.config.Version == "" {
y.config.Version = "v1alpha1"
} else if y.config.Version != "v1alpha1" {
return fmt.Errorf("unsupported config version: %s", y.config.Version)
}

y.loaded = true

return nil
return y.LoadConfigString(string(data))
}

// SaveConfig saves the current configuration to the specified path. If the path is empty, it uses the previously loaded path.
Expand Down
72 changes: 72 additions & 0 deletions pkg/config/yaml_config_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1672,3 +1672,75 @@ func TestAssignValue(t *testing.T) {
}
})
}

func TestYamlConfigHandler_LoadConfigString(t *testing.T) {
t.Run("Success", func(t *testing.T) {
mocks := setupSafeMocks()
handler := NewYamlConfigHandler(mocks.Injector)
handler.Initialize()
handler.SetContext("test")

yamlContent := `
version: v1alpha1
contexts:
test:
environment:
TEST_VAR: test_value`

err := handler.LoadConfigString(yamlContent)
if err != nil {
t.Fatalf("LoadConfigString() unexpected error: %v", err)
}

value := handler.GetString("environment.TEST_VAR")
if value != "test_value" {
t.Errorf("Expected TEST_VAR = 'test_value', got '%s'", value)
}
})

t.Run("EmptyContent", func(t *testing.T) {
mocks := setupSafeMocks()
handler := NewYamlConfigHandler(mocks.Injector)
handler.Initialize()

err := handler.LoadConfigString("")
if err != nil {
t.Fatalf("LoadConfigString() unexpected error: %v", err)
}
})

t.Run("InvalidYAML", func(t *testing.T) {
mocks := setupSafeMocks()
handler := NewYamlConfigHandler(mocks.Injector)
handler.Initialize()

yamlContent := `invalid: yaml: content: [}`

err := handler.LoadConfigString(yamlContent)
if err == nil {
t.Fatal("LoadConfigString() expected error for invalid YAML")
}
if !strings.Contains(err.Error(), "error unmarshalling yaml") {
t.Errorf("Expected error about invalid YAML, got: %v", err)
}
})

t.Run("UnsupportedVersion", func(t *testing.T) {
mocks := setupSafeMocks()
handler := NewYamlConfigHandler(mocks.Injector)
handler.Initialize()

yamlContent := `
version: v2alpha1
contexts:
test: {}`

err := handler.LoadConfigString(yamlContent)
if err == nil {
t.Fatal("LoadConfigString() expected error for unsupported version")
}
if !strings.Contains(err.Error(), "unsupported config version") {
t.Errorf("Expected error about unsupported version, got: %v", err)
}
})
}
Loading