1package config
  2
  3import (
  4	"context"
  5	"errors"
  6	"testing"
  7
  8	"github.com/charmbracelet/crush/pkg/env"
  9	"github.com/stretchr/testify/assert"
 10)
 11
 12// mockShell implements the Shell interface for testing
 13type mockShell struct {
 14	execFunc func(ctx context.Context, command string) (stdout, stderr string, err error)
 15}
 16
 17func (m *mockShell) Exec(ctx context.Context, command string) (stdout, stderr string, err error) {
 18	if m.execFunc != nil {
 19		return m.execFunc(ctx, command)
 20	}
 21	return "", "", nil
 22}
 23
 24func TestShellVariableResolver_ResolveValue(t *testing.T) {
 25	tests := []struct {
 26		name        string
 27		value       string
 28		envVars     map[string]string
 29		shellFunc   func(ctx context.Context, command string) (stdout, stderr string, err error)
 30		expected    string
 31		expectError bool
 32	}{
 33		{
 34			name:     "non-variable string returns as-is",
 35			value:    "plain-string",
 36			expected: "plain-string",
 37		},
 38		{
 39			name:     "environment variable resolution",
 40			value:    "$HOME",
 41			envVars:  map[string]string{"HOME": "/home/user"},
 42			expected: "/home/user",
 43		},
 44		{
 45			name:        "missing environment variable returns error",
 46			value:       "$MISSING_VAR",
 47			envVars:     map[string]string{},
 48			expectError: true,
 49		},
 50		{
 51			name:  "shell command execution",
 52			value: "$(echo hello)",
 53			shellFunc: func(ctx context.Context, command string) (stdout, stderr string, err error) {
 54				if command == "echo hello" {
 55					return "hello\n", "", nil
 56				}
 57				return "", "", errors.New("unexpected command")
 58			},
 59			expected: "hello",
 60		},
 61		{
 62			name:  "shell command with whitespace trimming",
 63			value: "$(echo '  spaced  ')",
 64			shellFunc: func(ctx context.Context, command string) (stdout, stderr string, err error) {
 65				if command == "echo '  spaced  '" {
 66					return "  spaced  \n", "", nil
 67				}
 68				return "", "", errors.New("unexpected command")
 69			},
 70			expected: "spaced",
 71		},
 72		{
 73			name:  "shell command execution error",
 74			value: "$(false)",
 75			shellFunc: func(ctx context.Context, command string) (stdout, stderr string, err error) {
 76				return "", "", errors.New("command failed")
 77			},
 78			expectError: true,
 79		},
 80		{
 81			name:        "invalid format returns error",
 82			value:       "$",
 83			expectError: true,
 84		},
 85	}
 86
 87	for _, tt := range tests {
 88		t.Run(tt.name, func(t *testing.T) {
 89			testEnv := env.NewFromMap(tt.envVars)
 90			resolver := &shellVariableResolver{
 91				shell: &mockShell{execFunc: tt.shellFunc},
 92				env:   testEnv,
 93			}
 94
 95			result, err := resolver.ResolveValue(tt.value)
 96
 97			if tt.expectError {
 98				assert.Error(t, err)
 99			} else {
100				assert.NoError(t, err)
101				assert.Equal(t, tt.expected, result)
102			}
103		})
104	}
105}
106
107func TestEnvironmentVariableResolver_ResolveValue(t *testing.T) {
108	tests := []struct {
109		name        string
110		value       string
111		envVars     map[string]string
112		expected    string
113		expectError bool
114	}{
115		{
116			name:     "non-variable string returns as-is",
117			value:    "plain-string",
118			expected: "plain-string",
119		},
120		{
121			name:     "environment variable resolution",
122			value:    "$HOME",
123			envVars:  map[string]string{"HOME": "/home/user"},
124			expected: "/home/user",
125		},
126		{
127			name:     "environment variable with complex value",
128			value:    "$PATH",
129			envVars:  map[string]string{"PATH": "/usr/bin:/bin:/usr/local/bin"},
130			expected: "/usr/bin:/bin:/usr/local/bin",
131		},
132		{
133			name:        "missing environment variable returns error",
134			value:       "$MISSING_VAR",
135			envVars:     map[string]string{},
136			expectError: true,
137		},
138		{
139			name:        "empty environment variable returns error",
140			value:       "$EMPTY_VAR",
141			envVars:     map[string]string{"EMPTY_VAR": ""},
142			expectError: true,
143		},
144	}
145
146	for _, tt := range tests {
147		t.Run(tt.name, func(t *testing.T) {
148			testEnv := env.NewFromMap(tt.envVars)
149			resolver := NewEnvironmentVariableResolver(testEnv)
150
151			result, err := resolver.ResolveValue(tt.value)
152
153			if tt.expectError {
154				assert.Error(t, err)
155			} else {
156				assert.NoError(t, err)
157				assert.Equal(t, tt.expected, result)
158			}
159		})
160	}
161}
162
163func TestNewShellVariableResolver(t *testing.T) {
164	testEnv := env.NewFromMap(map[string]string{"TEST": "value"})
165	resolver := NewShellVariableResolver(testEnv)
166
167	assert.NotNil(t, resolver)
168	assert.Implements(t, (*VariableResolver)(nil), resolver)
169}
170
171func TestNewEnvironmentVariableResolver(t *testing.T) {
172	testEnv := env.NewFromMap(map[string]string{"TEST": "value"})
173	resolver := NewEnvironmentVariableResolver(testEnv)
174
175	assert.NotNil(t, resolver)
176	assert.Implements(t, (*VariableResolver)(nil), resolver)
177}