package config import ( "os" "testing" ) func TestLoad(t *testing.T) { // Clean up any existing env vars cleanup := cleanEnv() defer cleanup() // Set test values os.Setenv("TECHNITIUM_URL", "https://test.dns.example.com") os.Setenv("TECHNITIUM_TOKEN", "test-token-12345") os.Setenv("BASE_DOMAIN", "test.rip") os.Setenv("SPACE_SUBDOMAIN", "dyn") os.Setenv("RATE_LIMIT_PER_IP", "20") os.Setenv("RATE_LIMIT_PER_TOKEN", "5") cfg := Load() tests := []struct { name string got string expected string }{ {"TechnitiumURL", cfg.TechnitiumURL, "https://test.dns.example.com"}, {"TechnitiumToken", cfg.TechnitiumToken, "test-token-12345"}, {"BaseDomain", cfg.BaseDomain, "test.rip"}, {"SpaceSubdomain", cfg.SpaceSubdomain, "dyn"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.got != tt.expected { t.Errorf("%s = %v, want %v", tt.name, tt.got, tt.expected) } }) } // Test numeric values if cfg.RateLimitPerIP != 20 { t.Errorf("RateLimitPerIP = %v, want 20", cfg.RateLimitPerIP) } if cfg.RateLimitPerToken != 5 { t.Errorf("RateLimitPerToken = %v, want 5", cfg.RateLimitPerToken) } } func TestLoadDefaults(t *testing.T) { cleanup := cleanEnv() defer cleanup() cfg := Load() tests := []struct { name string got string expected string }{ {"ServerPort", cfg.ServerPort, "8080"}, {"DatabasePath", cfg.DatabasePath, "./dyn.db"}, {"BaseDomain", cfg.BaseDomain, "dws.rip"}, {"SpaceSubdomain", cfg.SpaceSubdomain, "space"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.got != tt.expected { t.Errorf("%s = %v, want %v", tt.name, tt.got, tt.expected) } }) } // Test default numeric values if cfg.RateLimitPerIP != 10 { t.Errorf("RateLimitPerIP default = %v, want 10", cfg.RateLimitPerIP) } if cfg.RateLimitPerToken != 1 { t.Errorf("RateLimitPerToken default = %v, want 1", cfg.RateLimitPerToken) } } func TestValidate(t *testing.T) { tests := []struct { name string setupEnv func() wantErrors int }{ { name: "valid config with token", setupEnv: func() { os.Setenv("TECHNITIUM_URL", "https://dns.example.com") os.Setenv("TECHNITIUM_TOKEN", "valid-token") }, wantErrors: 0, }, { name: "valid config with username/password", setupEnv: func() { os.Setenv("TECHNITIUM_URL", "https://dns.example.com") os.Setenv("TECHNITIUM_USERNAME", "admin") os.Setenv("TECHNITIUM_PASSWORD", "secret") os.Unsetenv("TECHNITIUM_TOKEN") }, wantErrors: 0, }, { name: "missing url", setupEnv: func() { os.Unsetenv("TECHNITIUM_URL") os.Setenv("TECHNITIUM_TOKEN", "token") }, wantErrors: 1, }, { name: "missing auth", setupEnv: func() { os.Setenv("TECHNITIUM_URL", "https://dns.example.com") os.Unsetenv("TECHNITIUM_TOKEN") os.Unsetenv("TECHNITIUM_USERNAME") os.Unsetenv("TECHNITIUM_PASSWORD") }, wantErrors: 1, }, { name: "missing everything", setupEnv: func() { os.Unsetenv("TECHNITIUM_URL") os.Unsetenv("TECHNITIUM_TOKEN") os.Unsetenv("TECHNITIUM_USERNAME") os.Unsetenv("TECHNITIUM_PASSWORD") }, wantErrors: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cleanup := cleanEnv() defer cleanup() tt.setupEnv() cfg := Load() errors := cfg.Validate() if len(errors) != tt.wantErrors { t.Errorf("Validate() returned %d errors, want %d: %v", len(errors), tt.wantErrors, errors) } }) } } func TestGetZone(t *testing.T) { tests := []struct { name string base string space string expected string }{ { name: "with space subdomain", base: "dws.rip", space: "space", expected: "space.dws.rip", }, { name: "without space subdomain", base: "dws.rip", space: "", expected: "dws.rip", }, { name: "nested subdomain", base: "example.com", space: "dyn.space", expected: "dyn.space.example.com", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := &Config{ BaseDomain: tt.base, SpaceSubdomain: tt.space, } got := cfg.GetZone() if got != tt.expected { t.Errorf("GetZone() = %v, want %v", got, tt.expected) } }) } } func TestGetEnv(t *testing.T) { tests := []struct { name string key string value string defaultValue string expected string }{ { name: "env set", key: "TEST_KEY", value: "test-value", defaultValue: "default", expected: "test-value", }, { name: "env not set", key: "UNSET_TEST_KEY", value: "", defaultValue: "default", expected: "default", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.value != "" { os.Setenv(tt.key, tt.value) defer os.Unsetenv(tt.key) } got := getEnv(tt.key, tt.defaultValue) if got != tt.expected { t.Errorf("getEnv() = %v, want %v", got, tt.expected) } }) } } func TestGetEnvAsInt(t *testing.T) { tests := []struct { name string value string defaultValue int expected int }{ { name: "valid int", value: "42", defaultValue: 10, expected: 42, }, { name: "invalid int", value: "not-a-number", defaultValue: 10, expected: 10, }, { name: "empty value", value: "", defaultValue: 10, expected: 10, }, { name: "negative int", value: "-5", defaultValue: 10, expected: -5, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { key := "TEST_INT_KEY" if tt.value != "" { os.Setenv(key, tt.value) defer os.Unsetenv(key) } got := getEnvAsInt(key, tt.defaultValue) if got != tt.expected { t.Errorf("getEnvAsInt() = %v, want %v", got, tt.expected) } }) } } func TestGetEnvAsSlice(t *testing.T) { tests := []struct { name string value string defaultValue []string expected []string }{ { name: "single value", value: "10.0.0.0/8", defaultValue: []string{}, expected: []string{"10.0.0.0/8"}, }, { name: "multiple values", value: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16", defaultValue: []string{}, expected: []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}, }, { name: "empty value", value: "", defaultValue: []string{"default"}, expected: []string{"default"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { key := "TEST_SLICE_KEY" if tt.value != "" { os.Setenv(key, tt.value) defer os.Unsetenv(key) } got := getEnvAsSlice(key, tt.defaultValue) if len(got) != len(tt.expected) { t.Errorf("getEnvAsSlice() length = %v, want %v", len(got), len(tt.expected)) return } for i, v := range got { if v != tt.expected[i] { t.Errorf("getEnvAsSlice()[%d] = %v, want %v", i, v, tt.expected[i]) } } }) } } // cleanEnv removes all DYN-related env vars and returns a cleanup function func cleanEnv() func() { vars := []string{ "SERVER_PORT", "DATABASE_PATH", "TECHNITIUM_URL", "TECHNITIUM_USERNAME", "TECHNITIUM_PASSWORD", "TECHNITIUM_TOKEN", "BASE_DOMAIN", "SPACE_SUBDOMAIN", "RATE_LIMIT_PER_IP", "RATE_LIMIT_PER_TOKEN", "TRUSTED_PROXIES", } // Store old values oldValues := make(map[string]string) for _, v := range vars { if val, ok := os.LookupEnv(v); ok { oldValues[v] = val os.Unsetenv(v) } } // Return cleanup function return func() { for _, v := range vars { os.Unsetenv(v) } for v, val := range oldValues { os.Setenv(v, val) } } }