package config
import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"sync"
"testing"
"time"
)
type referenceConfig struct {
Port int `json:"port"`
BatchSize int `json:"batchSize"`
Enabled bool `json:"enabled"`
Password string `json:"password"`
Address string `json:"address"`
Timeout time.Duration `json:"timeout"`
Optional *int `json:"optional"`
Labels map[string]string `json:"labels"`
Numbers []int64 `json:"numbers"`
Dynamic any `json:"dynamic"`
}
func (c referenceConfig) Validate() error {
if c.Port < 0 {
return Invalid("port", "must not be negative")
}
return nil
}
func TestExplicitReferencesAcrossFormats(t *testing.T) {
// Delimiters, quotes and newlines belong to the credential, never file syntax.
secret := " DDSFLKJlsf:saf:-123\"\n${DO_NOT_READ}&<>\\ "
sources := map[string]string{
"json": `{"port":"${PORT:-3306}","enabled":"${ENABLED:-true}","password":"${PASSWORD}","address":"https://${HOST}:${PORT:-3306}/v1","timeout":"${TIMEOUT:-3s}","optional":"${ZERO}","numbers":["${BIG}"],"dynamic":"${ZERO}","labels":{"literal":"$${NOT_READ}","empty":"${OPTIONAL:-}","dollars":"$$cost"}}`,
"yaml": "port: \"${PORT:-3306}\"\nenabled: \"${ENABLED:-true}\"\npassword: \"${PASSWORD}\"\naddress: \"https://${HOST}:${PORT:-3306}/v1\"\ntimeout: \"${TIMEOUT:-3s}\"\noptional: \"${ZERO}\"\nnumbers: [\"${BIG}\"]\ndynamic: \"${ZERO}\"\nlabels:\n literal: \"$${NOT_READ}\"\n empty: \"${OPTIONAL:-}\"\n dollars: \"$$cost\"\n",
"xml": `${PORT:-3306}${ENABLED:-true}${PASSWORD}https://${HOST}:${PORT:-3306}/v1${TIMEOUT:-3s}${ZERO}- ${BIG}
${ZERO}$${NOT_READ}${OPTIONAL:-}$$cost`,
}
for ext, source := range sources {
t.Run(ext, func(t *testing.T) {
calls := map[string]int{}
values := map[string]string{"PORT": "0", "ENABLED": "false", "PASSWORD": secret, "HOST": "example.test", "ZERO": "0", "BIG": "9007199254740993"}
l := testLoader(func(name string) (string, bool) { calls[name]++; v, ok := values[name]; return v, ok })
got, err := LoadProject(configFile(t, ext, source), referenceConfig{Port: 77, BatchSize: 100, Enabled: true, Password: "old"}, l)
if err != nil {
t.Fatal(err)
}
if got.Port != 0 || got.BatchSize != 100 || got.Enabled || got.Password != secret || got.Address != "https://example.test:0/v1" || got.Timeout != 3*time.Second || got.Optional == nil || *got.Optional != 0 || got.Numbers[0] != 9007199254740993 || got.Dynamic != "0" {
t.Fatal("binding changed values")
}
if got.Labels["literal"] != "${NOT_READ}" || got.Labels["empty"] != "" || got.Labels["dollars"] != "$cost" {
t.Fatal(got.Labels)
}
for name, n := range calls {
if n != 1 {
t.Fatal("not cached", name, n)
}
}
if calls["DO_NOT_READ"] != 0 || calls["NOT_READ"] != 0 {
t.Fatal("recursive expansion")
}
})
}
}
func TestReferenceFallbackAndFixedValues(t *testing.T) {
for _, present := range []bool{false, true} {
l := testLoader(func(string) (string, bool) { return "", present })
got, err := LoadProject(configFile(t, "yaml", "port: \"${PORT:-3306}\"\npassword: \"${PASSWORD:-DDSFLKJlsf:saf:-123}\""), referenceConfig{}, l)
if err != nil || got.Port != 3306 || got.Password != "DDSFLKJlsf:saf:-123" {
t.Fatal(got, err)
}
for _, ext := range []string{"json", "yaml", "xml"} {
raw := `{"password":"${PASSWORD}"}`
if ext == "yaml" {
raw = "password: '${PASSWORD}'"
}
if ext == "xml" {
raw = "${PASSWORD}"
}
path := configFile(t, ext, raw)
_, err = LoadProject(path, referenceConfig{Password: "initial"}, l)
var d *Error
if !errors.As(err, &d) || d.Env != "PASSWORD" || d.File != path || d.Field != "password" || d.Line < 1 {
t.Fatal("missing required-reference diagnostic", err)
}
}
}
calls := 0
l := testLoader(func(string) (string, bool) { calls++; return "999", true })
got, err := LoadProject(configFile(t, "json", `{"port":0,"enabled":false,"password":""}`), referenceConfig{Port: 77, BatchSize: 100, Enabled: true, Password: "old"}, l)
if err != nil || got.Port != 0 || got.BatchSize != 100 || got.Enabled || got.Password != "" || calls != 0 {
t.Fatal(got, err, calls)
}
got, err = LoadProject(configFile(t, "yaml", "password: '${VALUE}'"), referenceConfig{}, testLoader(func(string) (string, bool) { return " ", true }))
if err != nil || got.Password != " " {
t.Fatal("trimmed credential", err)
}
}
func TestReferenceDiagnosticsAndScalarLimits(t *testing.T) {
for _, value := range []string{"private-not-int", "92233720368547758080", "false", "null", "-1"} {
path := configFile(t, "yaml", "port: '${PORT}'")
_, err := LoadProject(path, referenceConfig{}, testLoader(func(string) (string, bool) { return value, true }))
var d *Error
if !errors.As(err, &d) || d.File != path || d.Env != "PORT" || d.Field != "port" || d.Line != 1 || d.Column < 1 || strings.Contains(err.Error(), value) {
t.Fatalf("invalid diagnostic: %v", err)
}
}
for _, raw := range []string{"${BAD-123}", "${}", "${X", "${1BAD}", "${BAD NAME}", "${X:-${Y}}"} {
encoded, _ := json.Marshal(map[string]string{"password": raw})
if _, err := LoadProject(configFile(t, "json", string(encoded)), referenceConfig{}, testLoader(noEnvironment)); err == nil {
t.Fatal("invalid syntax accepted", raw)
}
}
for _, value := range []string{string([]byte{0xff}), strings.Repeat("x", maxConfigBytes+1)} {
if _, err := LoadProject(configFile(t, "yaml", "password: '${SECRET}'"), referenceConfig{}, testLoader(func(string) (string, bool) { return value, true })); err == nil {
t.Fatal("unbounded/invalid environment")
}
}
path := configFile(t, "yaml", "labels:\n a: '${BIG}'\n b: '${BIG}'\n c: '${BIG}'\n d: '${BIG}'\n e: '${BIG}'")
if _, err := LoadProject(path, referenceConfig{}, testLoader(func(string) (string, bool) { return strings.Repeat("x", maxConfigBytes), true })); err == nil {
t.Fatal("expansion budget bypassed")
}
// An environment value cannot inject an array/object or coerce dynamic null.
for _, raw := range []string{"[1,2]", "{\"x\":1}", "null"} {
_, err := LoadProject(configFile(t, "yaml", "numbers: '${VALUE}'"), referenceConfig{}, testLoader(func(string) (string, bool) { return raw, true }))
if err == nil {
t.Fatal("structure injected")
}
}
}
func TestReferenceCacheIsolationAndRemovedRules(t *testing.T) {
path := configFile(t, "yaml", "password: '${TOKEN}'")
for i := 0; i < 24; i++ {
t.Run(fmt.Sprint(i), func(t *testing.T) {
t.Parallel()
v := fmt.Sprint(i)
l := testLoader(func(string) (string, bool) { return v, true })
for n := 0; n < 4; n++ {
got, err := LoadProject(path, referenceConfig{}, l)
if err != nil || got.Password != v {
t.Fatal("load leaked", err)
}
}
})
}
}
func TestReferenceLookupPerLoadAndConcurrentLoader(t *testing.T) {
var mu sync.Mutex
reads := 0
l := testLoader(func(string) (string, bool) { mu.Lock(); defer mu.Unlock(); reads++; return "secret", true })
path := configFile(t, "yaml", "password: '${TOKEN}${TOKEN}'")
var wg sync.WaitGroup
for i := 0; i < 24; i++ {
wg.Add(1)
go func() {
defer wg.Done()
got, err := LoadProject(path, referenceConfig{}, l)
if err != nil || got.Password != "secretsecret" {
t.Error(err)
}
}()
}
wg.Wait()
if reads != 24 {
t.Fatal(reads)
}
}
func TestRemovedEnvTagsAndFieldSuggestion(t *testing.T) {
type old struct {
Value int `json:"value" envDefault:"9"`
}
if _, err := LoadProject("", old{}); err == nil || !strings.Contains(err.Error(), "removed") {
t.Fatal(err)
}
type oldSecret struct {
Token string `json:"-" env:"TOKEN"`
}
if _, err := DecodeFile(configFile(t, "json", `{}`), oldSecret{}); err == nil {
t.Fatal("old env tag ignored")
}
_, err := LoadProject(configFile(t, "yaml", "batchSiz: 10"), referenceConfig{})
if err == nil || !strings.Contains(err.Error(), `did you mean "batchSize"`) {
t.Fatal(err)
}
t.Setenv("GO_OMNI_BATCH_SIZE", "999")
got, err := LoadProject("", referenceConfig{BatchSize: 100})
if err != nil || got.BatchSize != 100 {
t.Fatal("implicit overlay", err)
}
}
func FuzzConfigurationReferences(f *testing.F) {
for _, s := range []string{"${X}", "$${X}", "${X:-DDS:abc:-123}", "${X:-}", "https://${X}/${X}", "${X:-${Y}}"} {
f.Add(s, " secret${NO}\n\" ")
}
f.Fuzz(func(t *testing.T, raw, value string) {
if len(raw) > 8192 || len(value) > 8192 {
return
}
doc := Document{Value: map[string]any{"password": raw}}
resolved, err := resolveDocument(doc, func(string) (string, bool) { return value, true })
if err != nil {
return
}
_, _ = bindDocument(resolved, referenceConfig{})
})
}
func BenchmarkExplicitConfiguration(b *testing.B) {
for _, refs := range []bool{false, true} {
b.Run(fmt.Sprint(refs), func(b *testing.B) {
source := `{"port":3306,"enabled":false,"password":"fixed","batchSize":0}`
if refs {
source = `{"port":"${PORT:-3306}","enabled":"${ENABLED:-false}","password":"${PASSWORD}","batchSize":0}`
}
path := b.TempDir() + "/config.json"
if err := os.WriteFile(path, []byte(source), 0600); err != nil {
b.Fatal(err)
}
l := testLoader(func(k string) (string, bool) { return "private", k == "PASSWORD" })
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := LoadProject(path, referenceConfig{BatchSize: 100}, l); err != nil {
b.Fatal(err)
}
}
})
}
}