1 parent
cc7466823b
commit
e8c5b414d9
44 files changed
+7649
-344
No files matched your search
@@ -0,0 +1,116 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// loadYAML writes body to a temp file and loads it, failing the test if Load
|
||||
// errors. Every test here wants "Load accepted this", so the helper asserts it
|
||||
// once instead of repeating the check.
|
||||
func loadYAML(t *testing.T, body string) *Config {
|
||||
t.Helper()
|
||||
path := filepath.Join(t.TempDir(), "config.yaml")
|
||||
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Load rejected the config: %v", err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// TestLoadIgnoresUnknownKeys pins a decision STATUS.md lists as open: Load
|
||||
// unmarshals leniently, so a key the struct does not know is silently dropped
|
||||
// instead of rejected. yaml.KnownFields(true) would catch it, and was
|
||||
// deliberately NOT adopted: it is a behaviour change for anyone carrying extra
|
||||
// keys in their config.
|
||||
//
|
||||
// The reason this is pinned rather than left alone is that leniency has already
|
||||
// cost this project twice (two test fixtures were caught by a stray key, not by
|
||||
// what they were testing). If the decision ever flips, the failure below is the
|
||||
// reminder to update STATUS.md, AGENTS.md and the README together.
|
||||
func TestLoadIgnoresUnknownKeys(t *testing.T) {
|
||||
t.Run("a misspelled top-level block is dropped", func(t *testing.T) {
|
||||
// The real trap from the field: the struct tag is "blocklist", so
|
||||
// writing "block:" produces a plausible-looking server that has
|
||||
// quietly kept every blocklist default. The correctly spelled
|
||||
// block below disables the blocklist so the config validates at all
|
||||
// — which is itself the sharper half of the pin: if the typo were
|
||||
// honoured, block: {enabled: true} with no sources would fail
|
||||
// Validate and the server would refuse to start.
|
||||
cfg := loadYAML(t, `
|
||||
blocklist:
|
||||
enabled: false
|
||||
block:
|
||||
enabled: true
|
||||
response_ip: 10.0.0.1
|
||||
response_ttl: 5s
|
||||
`)
|
||||
if cfg.Block.Enabled {
|
||||
t.Fatal("the misspelled block: block was applied; Load is no longer lenient " +
|
||||
"and this pin is stale")
|
||||
}
|
||||
if cfg.Block.ResponseIP != Default().Block.ResponseIP {
|
||||
t.Fatalf("blocklist.response_ip = %q, want the default %q",
|
||||
cfg.Block.ResponseIP, Default().Block.ResponseIP)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("the same block spelled correctly is applied", func(t *testing.T) {
|
||||
// The control for the case above: the lenient path is the
|
||||
// difference between these two keys, not Load ignoring the file.
|
||||
cfg := loadYAML(t, `
|
||||
blocklist:
|
||||
enabled: false
|
||||
response_ip: 10.0.0.1
|
||||
response_ttl: 5s
|
||||
`)
|
||||
if cfg.Block.Enabled {
|
||||
t.Fatal("blocklist.enabled: false was not applied")
|
||||
}
|
||||
if cfg.Block.ResponseIP != "10.0.0.1" {
|
||||
t.Fatalf("blocklist.response_ip = %q, want 10.0.0.1", cfg.Block.ResponseIP)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("a misspelled nested key is dropped", func(t *testing.T) {
|
||||
cfg := loadYAML(t, `
|
||||
blocklist:
|
||||
enabled: false
|
||||
api:
|
||||
basepath: /console
|
||||
`)
|
||||
if cfg.API.BasePath != Default().API.BasePath {
|
||||
t.Fatalf("api.base_path = %q, want the default %q — leniency changed",
|
||||
cfg.API.BasePath, Default().API.BasePath)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("an unknown top-level key is dropped", func(t *testing.T) {
|
||||
cfg := loadYAML(t, `
|
||||
blocklist:
|
||||
enabled: false
|
||||
telemetry:
|
||||
endpoint: https://example.invalid
|
||||
`)
|
||||
if cfg.API.Enabled != Default().API.Enabled {
|
||||
t.Fatalf("an unknown key changed the config: api.enabled = %v", cfg.API.Enabled)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("a known key with a bad TYPE is still fatal", func(t *testing.T) {
|
||||
// The asymmetry is the point: a typo is invisible, a wrong value type
|
||||
// is a startup failure. Leniency covers unknown keys only.
|
||||
path := filepath.Join(t.TempDir(), "config.yaml")
|
||||
if err := os.WriteFile(path, []byte("blocklist:\n enabled: false\nserver:\n max_concurrent: many\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := Load(path); err == nil {
|
||||
t.Fatal("Load accepted a string where a number belongs; the strictness " +
|
||||
"contrast this pin documents is gone")
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user