package config import ( "fmt" "os" "path/filepath" "testing" "time" ) // testConfig embeds Base and adds a service-specific section. type testConfig struct { Base MyService myServiceConfig `toml:"my_service"` } type myServiceConfig struct { Name string `toml:"name"` Enabled bool `toml:"enabled"` Items []string `toml:"items"` } // validatingConfig implements the Validator interface. type validatingConfig struct { Base Custom customSection `toml:"custom"` } type customSection struct { Required string `toml:"required"` } func (c *validatingConfig) Validate() error { if c.Custom.Required == "" { return fmt.Errorf("custom.required is missing") } return nil } const minimalTOML = ` [server] listen_addr = ":8443" tls_cert = "/tmp/cert.pem" tls_key = "/tmp/key.pem" [database] path = "/tmp/test.db" [mcias] server_url = "https://mcias.example.com" service_name = "test" [log] level = "debug" ` const fullTOML = ` [server] listen_addr = ":8443" tls_cert = "/tmp/cert.pem" tls_key = "/tmp/key.pem" grpc_addr = ":9443" read_timeout = "10s" write_timeout = "15s" idle_timeout = "60s" shutdown_timeout = "30s" [database] path = "/tmp/test.db" [mcias] server_url = "https://mcias.example.com" ca_cert = "/tmp/ca.pem" service_name = "myservice" tags = ["env:test", "tier:dev"] [log] level = "warn" [my_service] name = "hello" enabled = true items = ["a", "b", "c"] ` func writeTOML(t *testing.T, content string) string { t.Helper() dir := t.TempDir() path := filepath.Join(dir, "test.toml") if err := os.WriteFile(path, []byte(content), 0600); err != nil { t.Fatalf("write config: %v", err) } return path } func TestLoadMinimal(t *testing.T) { path := writeTOML(t, minimalTOML) cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ListenAddr != ":8443" { t.Fatalf("ListenAddr = %q, want %q", cfg.Server.ListenAddr, ":8443") } if cfg.Log.Level != "debug" { t.Fatalf("Log.Level = %q, want %q", cfg.Log.Level, "debug") } if cfg.MCIAS.ServerURL != "https://mcias.example.com" { t.Fatalf("MCIAS.ServerURL = %q", cfg.MCIAS.ServerURL) } } func TestLoadFull(t *testing.T) { path := writeTOML(t, fullTOML) cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.GRPCAddr != ":9443" { t.Fatalf("GRPCAddr = %q, want %q", cfg.Server.GRPCAddr, ":9443") } if cfg.Server.ReadTimeout.Duration != 10*time.Second { t.Fatalf("ReadTimeout = %v, want 10s", cfg.Server.ReadTimeout) } if cfg.Server.WriteTimeout.Duration != 15*time.Second { t.Fatalf("WriteTimeout = %v, want 15s", cfg.Server.WriteTimeout) } if cfg.MCIAS.CACert != "/tmp/ca.pem" { t.Fatalf("CACert = %q", cfg.MCIAS.CACert) } if len(cfg.MCIAS.Tags) != 2 { t.Fatalf("Tags = %v, want 2 items", cfg.MCIAS.Tags) } if cfg.MyService.Name != "hello" { t.Fatalf("MyService.Name = %q, want %q", cfg.MyService.Name, "hello") } if !cfg.MyService.Enabled { t.Fatal("MyService.Enabled = false, want true") } if len(cfg.MyService.Items) != 3 { t.Fatalf("MyService.Items = %v, want 3 items", cfg.MyService.Items) } } func TestDefaults(t *testing.T) { path := writeTOML(t, minimalTOML) cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ReadTimeout.Duration != 30*time.Second { t.Fatalf("ReadTimeout = %v, want 30s (default)", cfg.Server.ReadTimeout) } if cfg.Server.WriteTimeout.Duration != 30*time.Second { t.Fatalf("WriteTimeout = %v, want 30s (default)", cfg.Server.WriteTimeout) } if cfg.Server.IdleTimeout.Duration != 120*time.Second { t.Fatalf("IdleTimeout = %v, want 120s (default)", cfg.Server.IdleTimeout) } if cfg.Server.ShutdownTimeout.Duration != 60*time.Second { t.Fatalf("ShutdownTimeout = %v, want 60s (default)", cfg.Server.ShutdownTimeout) } } func TestDefaultsNotOverrideExplicit(t *testing.T) { path := writeTOML(t, fullTOML) cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } // fullTOML sets read_timeout = "10s"; default is 30s. if cfg.Server.ReadTimeout.Duration != 10*time.Second { t.Fatalf("ReadTimeout = %v, want 10s (explicit, not default)", cfg.Server.ReadTimeout) } } func TestDefaultLogLevel(t *testing.T) { toml := ` [server] listen_addr = ":8443" tls_cert = "/tmp/cert.pem" tls_key = "/tmp/key.pem" [database] path = "/tmp/test.db" [mcias] server_url = "https://mcias.example.com" ` path := writeTOML(t, toml) cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Log.Level != "info" { t.Fatalf("Log.Level = %q, want %q (default)", cfg.Log.Level, "info") } } func TestMissingRequiredField(t *testing.T) { tests := []struct { name string toml string }{ { "missing listen_addr", `[server] tls_cert = "/tmp/cert.pem" tls_key = "/tmp/key.pem" [database] path = "/tmp/test.db"`, }, { "missing tls_cert", `[server] listen_addr = ":8443" tls_key = "/tmp/key.pem" [database] path = "/tmp/test.db"`, }, { "missing tls_key", `[server] listen_addr = ":8443" tls_cert = "/tmp/cert.pem" [database] path = "/tmp/test.db"`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { path := writeTOML(t, tt.toml) _, err := Load[testConfig](path, "TEST") if err == nil { t.Fatal("expected error for missing required field") } }) } } func TestEnvOverrideString(t *testing.T) { path := writeTOML(t, minimalTOML) t.Setenv("TEST_SERVER_LISTEN_ADDR", ":9999") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ListenAddr != ":9999" { t.Fatalf("ListenAddr = %q, want %q (from env)", cfg.Server.ListenAddr, ":9999") } } func TestEnvOverrideDuration(t *testing.T) { path := writeTOML(t, minimalTOML) t.Setenv("TEST_SERVER_READ_TIMEOUT", "5s") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ReadTimeout.Duration != 5*time.Second { t.Fatalf("ReadTimeout = %v, want 5s (from env)", cfg.Server.ReadTimeout) } } func TestEnvOverrideSlice(t *testing.T) { path := writeTOML(t, minimalTOML) t.Setenv("TEST_MCIAS_TAGS", "env:prod, tier:api") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if len(cfg.MCIAS.Tags) != 2 { t.Fatalf("Tags = %v, want 2 items", cfg.MCIAS.Tags) } if cfg.MCIAS.Tags[0] != "env:prod" { t.Fatalf("Tags[0] = %q, want %q", cfg.MCIAS.Tags[0], "env:prod") } if cfg.MCIAS.Tags[1] != "tier:api" { t.Fatalf("Tags[1] = %q, want %q", cfg.MCIAS.Tags[1], "tier:api") } } func TestEnvOverrideServiceSpecific(t *testing.T) { path := writeTOML(t, fullTOML) t.Setenv("TEST_MY_SERVICE_NAME", "overridden") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.MyService.Name != "overridden" { t.Fatalf("MyService.Name = %q, want %q (from env)", cfg.MyService.Name, "overridden") } } func TestEnvOverrideBool(t *testing.T) { path := writeTOML(t, minimalTOML) t.Setenv("TEST_MY_SERVICE_ENABLED", "true") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if !cfg.MyService.Enabled { t.Fatal("MyService.Enabled = false, want true (from env)") } } func TestValidatorCalled(t *testing.T) { toml := ` [server] listen_addr = ":8443" tls_cert = "/tmp/cert.pem" tls_key = "/tmp/key.pem" [database] path = "/tmp/test.db" [mcias] server_url = "https://mcias.example.com" ` path := writeTOML(t, toml) // custom.required is missing → Validate should fail. _, err := Load[validatingConfig](path, "TEST") if err == nil { t.Fatal("expected validation error for missing custom.required") } } func TestValidatorPasses(t *testing.T) { toml := ` [server] listen_addr = ":8443" tls_cert = "/tmp/cert.pem" tls_key = "/tmp/key.pem" [database] path = "/tmp/test.db" [mcias] server_url = "https://mcias.example.com" [custom] required = "present" ` path := writeTOML(t, toml) cfg, err := Load[validatingConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Custom.Required != "present" { t.Fatalf("Custom.Required = %q, want %q", cfg.Custom.Required, "present") } } func TestLoadNonexistentFile(t *testing.T) { _, err := Load[testConfig]("/nonexistent/path.toml", "TEST") if err == nil { t.Fatal("expected error for nonexistent file") } } func TestLoadInvalidTOML(t *testing.T) { path := writeTOML(t, "this is not valid toml [[[") _, err := Load[testConfig](path, "TEST") if err == nil { t.Fatal("expected error for invalid TOML") } } func TestEmptyEnvPrefix(t *testing.T) { path := writeTOML(t, minimalTOML) // Should work fine with no env prefix (no overrides applied). cfg, err := Load[testConfig](path, "") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ListenAddr != ":8443" { t.Fatalf("ListenAddr = %q, want %q", cfg.Server.ListenAddr, ":8443") } } // directServerConfig embeds ServerConfig without Base (Metacrypt pattern). type directServerConfig struct { Server ServerConfig `toml:"server"` Extra string `toml:"extra"` } func TestPortEnvOverridesListenAddr(t *testing.T) { path := writeTOML(t, minimalTOML) t.Setenv("PORT", "9999") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ListenAddr != ":9999" { t.Fatalf("ListenAddr = %q, want %q", cfg.Server.ListenAddr, ":9999") } } func TestPortGRPCEnvOverridesGRPCAddr(t *testing.T) { path := writeTOML(t, minimalTOML) t.Setenv("PORT_GRPC", "9998") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.GRPCAddr != ":9998" { t.Fatalf("GRPCAddr = %q, want %q", cfg.Server.GRPCAddr, ":9998") } } func TestPortEnvOverridesTOMLValue(t *testing.T) { // fullTOML sets listen_addr = ":8443" and grpc_addr = ":9443". path := writeTOML(t, fullTOML) t.Setenv("PORT", "9999") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ListenAddr != ":9999" { t.Fatalf("ListenAddr = %q, want %q ($PORT should override TOML)", cfg.Server.ListenAddr, ":9999") } } func TestPortEnvOverridesGenericEnv(t *testing.T) { path := writeTOML(t, minimalTOML) t.Setenv("TEST_SERVER_LISTEN_ADDR", ":7777") t.Setenv("PORT", "9999") cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ListenAddr != ":9999" { t.Fatalf("ListenAddr = %q, want %q ($PORT should override generic env)", cfg.Server.ListenAddr, ":9999") } } func TestNoPortEnvNoChange(t *testing.T) { path := writeTOML(t, minimalTOML) cfg, err := Load[testConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } // minimalTOML sets listen_addr = ":8443", no $PORT set. if cfg.Server.ListenAddr != ":8443" { t.Fatalf("ListenAddr = %q, want %q (TOML value preserved without $PORT)", cfg.Server.ListenAddr, ":8443") } } func TestPortEnvDirectServerConfig(t *testing.T) { // Test the Metacrypt pattern: ServerConfig embedded without Base. toml := ` [server] listen_addr = ":8443" tls_cert = "/tmp/cert.pem" tls_key = "/tmp/key.pem" grpc_addr = ":9443" extra = "value" ` path := writeTOML(t, toml) t.Setenv("PORT", "5555") t.Setenv("PORT_GRPC", "5556") cfg, err := Load[directServerConfig](path, "TEST") if err != nil { t.Fatalf("Load: %v", err) } if cfg.Server.ListenAddr != ":5555" { t.Fatalf("ListenAddr = %q, want %q ($PORT on direct ServerConfig)", cfg.Server.ListenAddr, ":5555") } if cfg.Server.GRPCAddr != ":5556" { t.Fatalf("GRPCAddr = %q, want %q ($PORT_GRPC on direct ServerConfig)", cfg.Server.GRPCAddr, ":5556") } }