config updated to improve setting values, writing changes and test coverage
This commit is contained in:
1 parent
8f0e72821f
commit
169c6c6b64
8 files changed
+453
-59
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -165,3 +166,143 @@ func TestStoreValueUpdatesConf(t *testing.T) {
|
||||
t.Errorf("FTSMock.Interval = %v; want 30s", conf.Sources.FTSMock.Interval)
|
||||
}
|
||||
}
|
||||
|
||||
// parseValue parses the kinds a key can have; no current key is an integer
|
||||
// or float, so StoreValue can't reach those cases yet.
|
||||
func TestParseValue(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
kind reflect.Kind
|
||||
value string
|
||||
want any
|
||||
}{
|
||||
{reflect.Bool, "true", true},
|
||||
{reflect.String, "text", "text"},
|
||||
{reflect.Int, "-42", int64(-42)},
|
||||
{reflect.Int8, "7", int64(7)},
|
||||
{reflect.Uint16, "65535", uint64(65535)},
|
||||
{reflect.Float32, "1.5", 1.5},
|
||||
} {
|
||||
got, err := parseValue(tt.kind, tt.value)
|
||||
if err != nil || got != tt.want {
|
||||
t.Errorf("parseValue(%v, %q) = %#v, %v; want %#v", tt.kind, tt.value, got, err, tt.want)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
kind reflect.Kind
|
||||
value string
|
||||
}{
|
||||
{reflect.Bool, "maybe"},
|
||||
{reflect.Int, "1.5"},
|
||||
{reflect.Uint, "-1"},
|
||||
{reflect.Float64, "x"},
|
||||
} {
|
||||
if got, err := parseValue(tt.kind, tt.value); err == nil {
|
||||
t.Errorf("parseValue(%v, %q) = %#v; want an error", tt.kind, tt.value, got)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := parseValue(reflect.Slice, "a,b"); !errors.Is(err, errors.ErrUnsupported) {
|
||||
t.Errorf("parseValue(slice) err = %v; want ErrUnsupported", err)
|
||||
}
|
||||
}
|
||||
|
||||
// numberSection has the numeric kinds JobSearchConfig doesn't use yet, to
|
||||
// test storeValue with them.
|
||||
type numberSection struct {
|
||||
Count int `cfg:"count"`
|
||||
Small int8 `cfg:"small"`
|
||||
Big uint16 `cfg:"big"`
|
||||
Ratio float32 `cfg:"ratio"`
|
||||
}
|
||||
|
||||
func (c numberSection) Map() map[string]any { return mapOf(c) }
|
||||
func (c numberSection) Flatten() map[string]any { return flattenOf("", c) }
|
||||
func (c numberSection) TypeOf(key string) (reflect.Kind, error) { return typeOf(c, key) }
|
||||
func (c numberSection) ValueOf(key string) (any, error) { return valueOf(c, key) }
|
||||
func (c *numberSection) SetValue(key string, value any) error { return setValue(c, key, value) }
|
||||
|
||||
// Numbers are stored as the field's own type, not as the int64, uint64 or
|
||||
// float64 that parsing produces.
|
||||
func TestStoreValueNumbers(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
key, value string
|
||||
want any
|
||||
kind reflect.Kind
|
||||
}{
|
||||
{"count", "-42", -42, reflect.Int},
|
||||
{"small", "7", int8(7), reflect.Int8},
|
||||
{"big", "65535", uint16(65535), reflect.Uint16},
|
||||
{"ratio", "1.5", float32(1.5), reflect.Float32},
|
||||
} {
|
||||
t.Run(tt.key, func(t *testing.T) {
|
||||
path := useConfigFile(t, "count: 1\n")
|
||||
var s numberSection
|
||||
|
||||
got, kind, err := storeValue(&s, 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, _ := s.ValueOf(tt.key); v != tt.want {
|
||||
t.Errorf("section has %s = %#v; want %#v", tt.key, v, tt.want)
|
||||
}
|
||||
if v := readConfigFile(t, path).GetString(tt.key); v != tt.value {
|
||||
t.Errorf("file has %s = %q; want %q", tt.key, v, tt.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for key, value := range map[string]string{
|
||||
"count": "1.5", // not an integer
|
||||
"small": "300", // too big for an int8
|
||||
"big": "-1", // negative
|
||||
"ratio": "lots", // not a number
|
||||
} {
|
||||
useConfigFile(t, "count: 1\n")
|
||||
if _, _, err := storeValue(&numberSection{}, key, value); err == nil {
|
||||
t.Errorf("storeValue(%s, %q) succeeded", key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only the file's own settings are written back: defaults (and environment
|
||||
// variables) loaded into Viper stay out of the file.
|
||||
func TestStoreAndPurgeWriteOnlyFileSettings(t *testing.T) {
|
||||
path := useConfigFile(t, "logging:\n level: warn\n file: /tmp/js.log\n")
|
||||
LoadDefaultConfig()
|
||||
conf := defaultConfig(t)
|
||||
|
||||
if _, _, err := StoreValue(&conf, "sources.sleeper.enabled", "true"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := map[string]any{
|
||||
"logging.level": "warn",
|
||||
"logging.file": "/tmp/js.log",
|
||||
"sources.sleeper.enabled": true,
|
||||
}
|
||||
checkFileSettings(t, path, want)
|
||||
|
||||
if err := PurgeValue(&conf, "logging.file"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
delete(want, "logging.file")
|
||||
checkFileSettings(t, path, want)
|
||||
}
|
||||
|
||||
// checkFileSettings checks the config file at path holds exactly want.
|
||||
func checkFileSettings(t *testing.T, path string, want map[string]any) {
|
||||
t.Helper()
|
||||
v := readConfigFile(t, path)
|
||||
got := map[string]any{}
|
||||
for _, k := range v.AllKeys() {
|
||||
got[k] = v.Get(k)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
//nolint:gosec // G304: reading a file the test created in t.TempDir
|
||||
b, _ := os.ReadFile(path)
|
||||
t.Errorf("file settings = %v; want %v\n%s", got, want, b)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user