Files
config/source_test.go

219 lines
5.8 KiB
Go

// File: lixenwraith/config/source_test.go
package config_test
import (
"os"
"path/filepath"
"testing"
"github.com/lixenwraith/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMultiSourceConfiguration(t *testing.T) {
t.Run("Source Precedence", func(t *testing.T) {
cfg := config.New()
cfg.Register("test.value", "default")
// Set values in different sources
cfg.SetSource("test.value", config.SourceFile, "from-file")
cfg.SetSource("test.value", config.SourceEnv, "from-env")
cfg.SetSource("test.value", config.SourceCLI, "from-cli")
// Default precedence: CLI > Env > File > Default
val, _ := cfg.String("test.value")
assert.Equal(t, "from-cli", val)
// Change precedence
opts := config.LoadOptions{
Sources: []config.Source{
config.SourceEnv,
config.SourceCLI,
config.SourceFile,
config.SourceDefault,
},
}
cfg.SetLoadOptions(opts)
// Now env should win
val, _ = cfg.String("test.value")
assert.Equal(t, "from-env", val)
})
t.Run("Source Tracking", func(t *testing.T) {
cfg := config.New()
cfg.Register("server.port", 8080)
// Set from multiple sources
cfg.SetSource("server.port", config.SourceFile, 9090)
cfg.SetSource("server.port", config.SourceEnv, 7070)
// Get all sources
sources := cfg.GetSources("server.port")
// Should have 2 sources
assert.Len(t, sources, 2)
assert.Equal(t, 9090, sources[config.SourceFile])
assert.Equal(t, 7070, sources[config.SourceEnv])
})
t.Run("GetSource", func(t *testing.T) {
cfg := config.New()
cfg.Register("api.key", "default-key")
cfg.SetSource("api.key", config.SourceEnv, "env-key")
// Get from specific source
envVal, exists := cfg.GetSource("api.key", config.SourceEnv)
assert.True(t, exists)
assert.Equal(t, "env-key", envVal)
// Get from missing source
_, exists = cfg.GetSource("api.key", config.SourceFile)
assert.False(t, exists)
})
t.Run("Reset Sources", func(t *testing.T) {
cfg := config.New()
cfg.Register("test1", "default1")
cfg.Register("test2", "default2")
// Set values
cfg.SetSource("test1", config.SourceFile, "file1")
cfg.SetSource("test1", config.SourceEnv, "env1")
cfg.SetSource("test2", config.SourceCLI, "cli2")
// Reset specific source
cfg.ResetSource(config.SourceEnv)
// Env value should be gone
_, exists := cfg.GetSource("test1", config.SourceEnv)
assert.False(t, exists)
// Other sources remain
fileVal, _ := cfg.GetSource("test1", config.SourceFile)
assert.Equal(t, "file1", fileVal)
// Reset all
cfg.Reset()
// All values should be defaults
val1, _ := cfg.String("test1")
val2, _ := cfg.String("test2")
assert.Equal(t, "default1", val1)
assert.Equal(t, "default2", val2)
})
t.Run("LoadWithOptions Integration", func(t *testing.T) {
// Create temp config file
tmpdir := t.TempDir()
configFile := filepath.Join(tmpdir, "test.toml")
configContent := `
[server]
host = "file-host"
port = 8080
[feature]
enabled = true
`
require.NoError(t, os.WriteFile(configFile, []byte(configContent), 0644))
// Set environment
os.Setenv("TEST_SERVER_PORT", "9090")
os.Setenv("TEST_FEATURE_ENABLED", "false")
t.Cleanup(func() {
os.Unsetenv("TEST_SERVER_PORT")
os.Unsetenv("TEST_FEATURE_ENABLED")
})
cfg := config.New()
cfg.Register("server.host", "default-host")
cfg.Register("server.port", 7070)
cfg.Register("feature.enabled", false)
// Load with custom precedence (File highest)
opts := config.LoadOptions{
Sources: []config.Source{
config.SourceFile,
config.SourceEnv,
config.SourceCLI,
config.SourceDefault,
},
EnvPrefix: "TEST_",
}
err := cfg.LoadWithOptions(configFile, []string{"--server.host=cli-host"}, opts)
require.NoError(t, err)
// File should win for all values
host, _ := cfg.String("server.host")
assert.Equal(t, "file-host", host)
port, _ := cfg.Int64("server.port")
assert.Equal(t, int64(8080), port)
enabled, _ := cfg.Bool("feature.enabled")
assert.True(t, enabled)
})
t.Run("ScanSource", func(t *testing.T) {
type ServerConfig struct {
Host string `toml:"host"`
Port int `toml:"port"`
}
cfg := config.New()
cfg.Register("server.host", "default")
cfg.Register("server.port", 8080)
// Set different values in different sources
cfg.SetSource("server.host", config.SourceFile, "file-host")
cfg.SetSource("server.port", config.SourceFile, 8080)
cfg.SetSource("server.host", config.SourceEnv, "env-host")
cfg.SetSource("server.port", config.SourceEnv, 9090)
// Scan from specific source
var fileConfig ServerConfig
err := cfg.ScanSource("server", config.SourceFile, &fileConfig)
require.NoError(t, err)
assert.Equal(t, "file-host", fileConfig.Host)
assert.Equal(t, 8080, fileConfig.Port)
var envConfig ServerConfig
err = cfg.ScanSource("server", config.SourceEnv, &envConfig)
require.NoError(t, err)
assert.Equal(t, "env-host", envConfig.Host)
assert.Equal(t, 9090, envConfig.Port)
})
t.Run("SaveSource", func(t *testing.T) {
cfg := config.New()
cfg.Register("app.name", "myapp")
cfg.Register("app.version", "1.0.0")
cfg.Register("server.port", 8080)
// Set values in different sources
cfg.SetSource("app.name", config.SourceFile, "fileapp")
cfg.SetSource("app.version", config.SourceEnv, "2.0.0")
cfg.SetSource("server.port", config.SourceCLI, 9090)
// Save only env source
tmpfile := filepath.Join(t.TempDir(), "config-source.toml")
err := cfg.SaveSource(tmpfile, config.SourceEnv)
require.NoError(t, err)
// Load saved file and verify
newCfg := config.New()
newCfg.Register("app.version", "")
newCfg.LoadFile(tmpfile)
version, _ := newCfg.String("app.version")
assert.Equal(t, "2.0.0", version)
// Should not have other source values
name, _ := newCfg.String("app.name")
assert.Empty(t, name)
})
}