package smartconfig import ( "os" "strings" "testing" ) func TestTypedInterpolation(t *testing.T) { testCases := []struct { name string yaml string env map[string]string checks []struct { key string expectedVal interface{} expectedType string } }{ { name: "unquoted number interpolation", yaml: `port: ${ENV:PORT}`, env: map[string]string{"PORT": "8080"}, checks: []struct { key string expectedVal interface{} expectedType string }{ {key: "port", expectedVal: 8080, expectedType: "int"}, }, }, { name: "force string with prefix", yaml: `port: "port-${ENV:PORT}"`, env: map[string]string{"PORT": "8080"}, checks: []struct { key string expectedVal interface{} expectedType string }{ {key: "port", expectedVal: "port-8080", expectedType: "string"}, }, }, { name: "boolean interpolation", yaml: `enabled: ${ENV:ENABLED}`, env: map[string]string{"ENABLED": "true"}, checks: []struct { key string expectedVal interface{} expectedType string }{ {key: "enabled", expectedVal: true, expectedType: "bool"}, }, }, { name: "float interpolation", yaml: `timeout: ${ENV:TIMEOUT}`, env: map[string]string{"TIMEOUT": "30.5"}, checks: []struct { key string expectedVal interface{} expectedType string }{ {key: "timeout", expectedVal: 30.5, expectedType: "float64"}, }, }, { name: "mixed content stays string", yaml: `message: "Hello ${ENV:NAME}!"`, env: map[string]string{"NAME": "World"}, checks: []struct { key string expectedVal interface{} expectedType string }{ {key: "message", expectedVal: "Hello World!", expectedType: "string"}, }, }, { name: "nested interpolation with type", yaml: `value: ${ENV:PREFIX_${ENV:SUFFIX}}`, env: map[string]string{"SUFFIX": "VAL", "PREFIX_VAL": "42"}, checks: []struct { key string expectedVal interface{} expectedType string }{ {key: "value", expectedVal: 42, expectedType: "int"}, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Set environment variables for k, v := range tc.env { _ = os.Setenv(k, v) defer func(key string) { _ = os.Unsetenv(key) }(k) } // Load config config, err := NewFromReader(strings.NewReader(tc.yaml)) if err != nil { t.Fatalf("Failed to load config: %v", err) } // Check values and types for _, check := range tc.checks { val, exists := config.Get(check.key) if !exists { t.Errorf("Key %s not found", check.key) continue } // Check type actualType := "" switch val.(type) { case string: actualType = "string" case int: actualType = "int" case float64: actualType = "float64" case bool: actualType = "bool" default: actualType = "unknown" } if actualType != check.expectedType { t.Errorf("Key %s: expected type %s, got %s (value: %v)", check.key, check.expectedType, actualType, val) } // Check value if val != check.expectedVal { t.Errorf("Key %s: expected value %v, got %v", check.key, check.expectedVal, val) } } }) } }