164 lines
4.5 KiB
Go
164 lines
4.5 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// useConfigFile points Viper at a new YAML config file holding contents.
|
|
func useConfigFile(t *testing.T, contents string) string {
|
|
t.Helper()
|
|
resetViper(t)
|
|
path := filepath.Join(t.TempDir(), "config.yaml")
|
|
if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
viper.SetConfigFile(path)
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
// readConfigFile reads path with a fresh Viper.
|
|
func readConfigFile(t *testing.T, path string) *viper.Viper {
|
|
t.Helper()
|
|
v := viper.New()
|
|
v.SetConfigFile(path)
|
|
if err := v.ReadInConfig(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func TestStoreValue(t *testing.T) {
|
|
tests := []struct {
|
|
key, value string
|
|
want any
|
|
kind reflect.Kind
|
|
}{
|
|
{"logging.level", "debug", "debug", reflect.String},
|
|
{"sources.sleeper.enabled", "true", true, reflect.Bool},
|
|
{"sources.fts_mock.api_root", "http://mock", "http://mock", reflect.String},
|
|
{"sources.fts_mock.interval", "30s", "30s", reflect.String},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.key, func(t *testing.T) {
|
|
path := useConfigFile(t, "logging:\n level: warn\n")
|
|
conf := defaultConfig(t)
|
|
|
|
got, kind, err := StoreValue(&conf, tt.key, tt.value)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != tt.want || kind != tt.kind {
|
|
t.Errorf("got %#v <%s>; want %#v <%s>", got, kind, tt.want, tt.kind)
|
|
}
|
|
if v := readConfigFile(t, path).Get(tt.key); v != tt.want {
|
|
t.Errorf("file has %s = %#v; want %#v", tt.key, v, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStoreValueKeepsOtherKeys(t *testing.T) {
|
|
path := useConfigFile(t, "logging:\n level: warn\n file: /tmp/js.log\n")
|
|
conf := defaultConfig(t)
|
|
if _, _, err := StoreValue(&conf, "logging.level", "error"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v := readConfigFile(t, path)
|
|
if got := v.GetString("logging.file"); got != "/tmp/js.log" {
|
|
t.Errorf("logging.file = %q; want it kept", got)
|
|
}
|
|
}
|
|
|
|
func TestStoreValueErrors(t *testing.T) {
|
|
tests := []struct {
|
|
name, key, value, wantErr string
|
|
}{
|
|
{"unknown key", "nope", "x", ""},
|
|
{"section key", "sources", "x", ""},
|
|
{"bad bool", "sources.sleeper.enabled", "maybe", "expected <bool>"},
|
|
{"invalid log level", "logging.level", "shouty", "invalid log level"},
|
|
{"bad duration", "sources.fts_mock.interval", "eventually", "duration"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
path := useConfigFile(t, "logging:\n level: warn\n")
|
|
before, _ := os.ReadFile(path)
|
|
conf := defaultConfig(t)
|
|
|
|
_, _, err := StoreValue(&conf, tt.key, tt.value)
|
|
if err == nil {
|
|
t.Fatal("expected an error")
|
|
}
|
|
if !strings.Contains(err.Error(), tt.wantErr) {
|
|
t.Errorf("err = %v; want it to mention %q", err, tt.wantErr)
|
|
}
|
|
if after, _ := os.ReadFile(path); string(after) != string(before) {
|
|
t.Errorf("config file changed:\n%s", after)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStoreValueNoConfigFile(t *testing.T) {
|
|
resetViper(t)
|
|
conf := defaultConfig(t)
|
|
if _, _, err := StoreValue(&conf, "logging.level", "debug"); err == nil {
|
|
t.Fatal("expected an error with no config file to write")
|
|
}
|
|
}
|
|
|
|
func TestPurgeValue(t *testing.T) {
|
|
path := useConfigFile(t, "logging:\n level: debug\n file: /tmp/js.log\n")
|
|
conf := defaultConfig(t)
|
|
|
|
if err := PurgeValue(&conf, "logging.file"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v := readConfigFile(t, path)
|
|
if v.IsSet("logging.file") {
|
|
t.Errorf("logging.file still set: %v", v.Get("logging.file"))
|
|
}
|
|
if got := v.GetString("logging.level"); got != "debug" {
|
|
t.Errorf("logging.level = %q; want it kept", got)
|
|
}
|
|
}
|
|
|
|
func TestPurgeValueErrors(t *testing.T) {
|
|
path := useConfigFile(t, "logging:\n level: debug\n")
|
|
before, _ := os.ReadFile(path)
|
|
conf := defaultConfig(t)
|
|
|
|
if err := PurgeValue(&conf, "nope"); err == nil {
|
|
t.Error("expected an error for an unknown key")
|
|
}
|
|
if after, _ := os.ReadFile(path); string(after) != string(before) {
|
|
t.Errorf("config file changed:\n%s", after)
|
|
}
|
|
|
|
resetViper(t)
|
|
if err := PurgeValue(&conf, "logging.level"); err == nil {
|
|
t.Error("expected an error with no config file to write")
|
|
}
|
|
}
|
|
|
|
func TestStoreValueUpdatesConf(t *testing.T) {
|
|
useConfigFile(t, "logging:\n level: warn\n")
|
|
conf := defaultConfig(t)
|
|
if _, _, err := StoreValue(&conf, "sources.fts_mock.interval", "30s"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if conf.Sources.FTSMock.Interval != 30*time.Second {
|
|
t.Errorf("FTSMock.Interval = %v; want 30s", conf.Sources.FTSMock.Interval)
|
|
}
|
|
}
|