Files
Agent-System/internal/config/instance_settings_test.go
T
agent-bot 28a2c8ca2b
CI / go-vet (pull_request) Successful in 14s
CI / short-tests-report (pull_request) Failing after 36s
CI / backup-restore-drill (pull_request) Successful in 26s
CI / web (pull_request) Successful in 31s
feat(config): instance-settings fifth layer (U10b core 2, KTD16/17)
- <config_root>/instance-settings.json is the final config layer:
  default > env-file(s) > process-env > cli > instance-settings
- LoadForServe wires the CLI mutation hook BEFORE instance settings;
  serve/check/explain all use it; plain Load keeps legacy behavior
- bootstrap denylist (env) fails closed (no self-locating file loop);
  SETTINGS_MODE=ignore skips the layer as the recovery path
- reflection-based typed application: string/int/bool/duration(string or
  seconds)/string-array; unknown fields and type mismatches fail closed
- group/world-writable settings file rejected; absent key = inherit
- sources table gains the instance-settings layer; e2e smoke verified
  (override applies; ignore-mode reverts to default)
2026-09-29 08:17:21 +00:00

80 lines
2.4 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
)
func writeInstanceSettings(t *testing.T, root, body string) {
t.Helper()
if err := os.MkdirAll(root, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, "instance-settings.json"), []byte(body), 0o600); err != nil {
t.Fatal(err)
}
}
func TestInstanceSettingsFinalLayerWins(t *testing.T) {
root := t.TempDir()
t.Setenv("CONFIG_ROOT", root)
writeInstanceSettings(t, root, `{"revision":3,"settings":{"http_port":9000,"agent_backend":"stub","agent_timeout":"15m"}}`)
cfg, src, err := LoadWithSources("", func(c *Config) { c.HTTPPort = 8888 }) // CLI 也被实例设置覆盖
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.HTTPPort != 9000 || cfg.AgentBackend != "stub" || cfg.AgentTimeout.String() != "15m0s" {
t.Fatalf("instance settings not applied: port=%d backend=%s timeout=%s", cfg.HTTPPort, cfg.AgentBackend, cfg.AgentTimeout)
}
if src["http_port"].Layer != "instance-settings" {
t.Fatalf("http_port layer=%s want instance-settings", src["http_port"].Layer)
}
if src["agent_timeout"].Layer != "instance-settings" {
t.Fatalf("agent_timeout layer=%s", src["agent_timeout"].Layer)
}
}
func TestInstanceSettingsIgnoredMode(t *testing.T) {
root := t.TempDir()
t.Setenv("CONFIG_ROOT", root)
t.Setenv("SETTINGS_MODE", "ignore")
writeInstanceSettings(t, root, `{"settings":{"http_port":9000}}`)
cfg, err := Load("")
if err != nil {
t.Fatal(err)
}
if cfg.HTTPPort == 9000 {
t.Fatal("SETTINGS_MODE=ignore must skip the instance settings layer")
}
}
func TestInstanceSettingsBootstrapDeny(t *testing.T) {
root := t.TempDir()
t.Setenv("CONFIG_ROOT", root)
writeInstanceSettings(t, root, `{"settings":{"env":"main"}}`)
if _, err := Load(""); err == nil {
t.Fatal("bootstrap field override must fail closed")
}
}
func TestInstanceSettingsTypeMismatchFails(t *testing.T) {
root := t.TempDir()
t.Setenv("CONFIG_ROOT", root)
writeInstanceSettings(t, root, `{"settings":{"http_port":"not-a-number"}}`)
if _, err := Load(""); err == nil {
t.Fatal("type mismatch must fail closed")
}
}
func TestInstanceSettingsPermissiveFileRejected(t *testing.T) {
root := t.TempDir()
t.Setenv("CONFIG_ROOT", root)
p := filepath.Join(root, "instance-settings.json")
_ = os.WriteFile(p, []byte(`{"settings":{"http_port":9000}}`), 0o666)
if _, err := Load(""); err == nil {
t.Fatal("group/world writable instance settings must be rejected")
}
}