Add comprehensive test suite
This commit is contained in:
372
internal/config/config_test.go
Normal file
372
internal/config/config_test.go
Normal file
@@ -0,0 +1,372 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user