package config import ( "os" "path/filepath" "testing" "time" ) func TestLoadDefaults(t *testing.T) { clearEnv(t) c, err := Load() if err != nil { t.Fatal(err) } if c.ListenAddr != ":8000" { t.Errorf("ListenAddr = %q", c.ListenAddr) } if c.CacheTTL != 30*time.Second { t.Errorf("CacheTTL = %v", c.CacheTTL) } if c.DefaultTemplate != "almalinux9" { t.Errorf("DefaultTemplate = %q", c.DefaultTemplate) } if c.PuppetServer != "puppet.k8s.syd1.au.unkin.net" || c.PuppetCAServer != "puppetca.k8s.syd1.au.unkin.net" { t.Errorf("puppet servers = %q / %q", c.PuppetServer, c.PuppetCAServer) } if c.PuppetCAURL != "puppetca.k8s.syd1.au.unkin.net" { t.Errorf("PuppetCAURL = %q", c.PuppetCAURL) } if c.UnknownMACFallback != "local" { t.Errorf("UnknownMACFallback = %q", c.UnknownMACFallback) } if len(c.Nameservers) != 1 || c.Nameservers[0] != "198.18.200.7" { t.Errorf("default nameservers = %v, want [198.18.200.7]", c.Nameservers) } if c.TemplateGitInterval != 3*time.Minute { t.Errorf("TemplateGitInterval = %v, want 3m", c.TemplateGitInterval) } if c.ArtifactBaseURL != "https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote" { t.Errorf("ArtifactBaseURL = %q", c.ArtifactBaseURL) } } func TestCallbackBaseDefaultsToBase(t *testing.T) { clearEnv(t) t.Setenv("BOOTAPI_BASE_URL", "http://bootapi.example.net/") c, err := Load() if err != nil { t.Fatal(err) } if c.BaseURL != "http://bootapi.example.net" || c.CallbackBaseURL != "http://bootapi.example.net" { t.Errorf("base=%q callback=%q; callback should default to base", c.BaseURL, c.CallbackBaseURL) } } func TestProvisionTokenFile(t *testing.T) { clearEnv(t) dir := t.TempDir() tf := filepath.Join(dir, "tok") if err := os.WriteFile(tf, []byte(" prov-secret\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("BOOTAPI_PROVISION_TOKEN_FILE", tf) c, err := Load() if err != nil { t.Fatal(err) } if c.ProvisionToken != "prov-secret" { t.Errorf("ProvisionToken = %q", c.ProvisionToken) } } func TestLoadTokenFile(t *testing.T) { clearEnv(t) dir := t.TempDir() tf := filepath.Join(dir, "token") if err := os.WriteFile(tf, []byte(" secret-token\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("BOOTAPI_NETBOX_TOKEN_FILE", tf) c, err := Load() if err != nil { t.Fatal(err) } if c.NetBoxToken != "secret-token" { t.Errorf("token = %q, want trimmed file contents", c.NetBoxToken) } } func TestLoadRejectsBadFallback(t *testing.T) { clearEnv(t) t.Setenv("BOOTAPI_UNKNOWN_MAC_FALLBACK", "bogus") if _, err := Load(); err == nil { t.Fatal("expected error for invalid fallback") } } func TestLoadListsAndTrim(t *testing.T) { clearEnv(t) t.Setenv("BOOTAPI_NAMESERVERS", " 10.0.0.1, 10.0.0.2 ,") t.Setenv("BOOTAPI_NETBOX_URL", "https://netbox.example.net/") c, err := Load() if err != nil { t.Fatal(err) } if len(c.Nameservers) != 2 || c.Nameservers[1] != "10.0.0.2" { t.Errorf("nameservers = %v", c.Nameservers) } if c.NetBoxURL != "https://netbox.example.net" { t.Errorf("NetBoxURL trailing slash not trimmed: %q", c.NetBoxURL) } } // clearEnv unsets every BOOTAPI_* var so a developer's shell can't leak into // the test. t.Setenv restores them after the test. func clearEnv(t *testing.T) { t.Helper() for _, kv := range os.Environ() { if k, _, ok := cut(kv, '='); ok && len(k) > 8 && k[:8] == "BOOTAPI_" { // t.Setenv to "" is enough: Load treats empty as unset, and the // test framework restores the original value on cleanup. t.Setenv(k, "") } } } func cut(s string, sep byte) (before, after string, found bool) { for i := 0; i < len(s); i++ { if s[i] == sep { return s[:i], s[i+1:], true } } return s, "", false }