Files
OliveTin/service/internal/config/config_reloader_test.go
2025-10-30 12:52:58 +00:00

153 lines
4.4 KiB
Go

package config
import (
"os"
"testing"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/rawbytes"
"github.com/knadh/koanf/v2"
"github.com/stretchr/testify/assert"
)
var stringEnvConfigYaml = `
PageTitle: ${{ INPUT }}
`
var stringEnvInterpolationConfigYaml = `
PageTitle: Olivetin - ${{ INPUT }}
`
var boolEnvConfigYaml = `
CheckForUpdates: ${{ INPUT }}
`
var numericEnvConfigYaml = `
LogHistoryPageSize: ${{ INPUT }}
`
var argsSyntaxConfigYaml = `
actions:
- title: Ping host
id: ping_host
shell: ping {{ host }} -c ${{ INPUT }}
icon: ping
timeout: 100
popupOnStart: execution-dialog-stdout-only
arguments:
- name: host
title: Host
type: ascii_identifier
default: example.com
description: The host that you want to ping
`
func pageTitleSelector(cfg *Config) any {
return cfg.PageTitle
}
func checkForUpdatesSelector(cfg *Config) any {
return cfg.CheckForUpdates
}
func logHistoryPageSizeSelector(cfg *Config) any {
return cfg.LogHistoryPageSize
}
var envConfigTests = []struct {
yaml string
input string
output any
selector func(*Config) any
}{
// Test that it works for string type config fields, both standalone and as part of a larger string value.
{stringEnvConfigYaml, "A Nice Title", "A Nice Title", pageTitleSelector},
{stringEnvInterpolationConfigYaml, "A Nice Title", "Olivetin - A Nice Title", pageTitleSelector},
// Test that unset variables turn into empty strings.
{stringEnvConfigYaml, "", "", pageTitleSelector},
// Test that it works for bool type config fields for intuitive bool->string conversions.
{boolEnvConfigYaml, "FALSE", false, checkForUpdatesSelector},
{boolEnvConfigYaml, "false", false, checkForUpdatesSelector},
{boolEnvConfigYaml, "False", false, checkForUpdatesSelector},
{boolEnvConfigYaml, "TRUE", true, checkForUpdatesSelector},
{boolEnvConfigYaml, "true", true, checkForUpdatesSelector},
{boolEnvConfigYaml, "True", true, checkForUpdatesSelector},
{boolEnvConfigYaml, "0", false, checkForUpdatesSelector},
{boolEnvConfigYaml, "1", true, checkForUpdatesSelector},
// Test that unset variables turn into false bools.
{boolEnvConfigYaml, "", false, checkForUpdatesSelector},
// Test that it works for numeric type config fields.
{numericEnvConfigYaml, "2048", int64(2048), logHistoryPageSizeSelector},
// Test that unset variables turn into zero numbers.
{numericEnvConfigYaml, "", int64(0), logHistoryPageSizeSelector},
// Test that it doesn't interfere with similar arguments
{argsSyntaxConfigYaml, "5", "ping {{ host }} -c 5", func(cfg *Config) any {
if len(cfg.Actions) > 0 {
return cfg.Actions[0].Shell
}
return ""
}},
}
func TestEnvInConfig(t *testing.T) {
for _, tt := range envConfigTests {
cfg := DefaultConfig()
setIfNotEmpty("INPUT", tt.input)
processed := processYamlWithEnv(tt.yaml)
k, err := loadKoanf(processed)
if err != nil {
t.Errorf("Error loading YAML: %v", err)
continue
}
if err := k.Unmarshal(".", cfg); err != nil {
t.Errorf("Error unmarshalling config: %v", err)
continue
}
manualAssigns(k, cfg)
field := tt.selector(cfg)
assert.Equal(t, tt.output, field, "Unmarshaled config field doesn't match expected value: env=\"%s\"", tt.input)
os.Unsetenv("INPUT")
}
}
func setIfNotEmpty(key, val string) {
if val != "" {
os.Setenv(key, val)
}
}
func processYamlWithEnv(content string) string {
return envRegex.ReplaceAllStringFunc(content, func(match string) string {
submatches := envRegex.FindStringSubmatch(match)
key := submatches[1]
val, _ := os.LookupEnv(key)
return val
})
}
func loadKoanf(processed string) (*koanf.Koanf, error) {
k := koanf.New(".")
if err := k.Load(rawbytes.Provider([]byte(processed)), yaml.Parser()); err != nil {
return nil, err
}
return k, nil
}
func manualAssigns(k *koanf.Koanf, cfg *Config) {
if k.Exists("PageTitle") {
cfg.PageTitle = k.String("PageTitle")
}
if k.Exists("CheckForUpdates") {
cfg.CheckForUpdates = k.Bool("CheckForUpdates")
}
if k.Exists("LogHistoryPageSize") {
cfg.LogHistoryPageSize = k.Int64("LogHistoryPageSize")
}
if k.Exists("actions") {
var actions []*Action
if err := k.Unmarshal("actions", &actions); err == nil {
cfg.Actions = actions
}
}
}