Files
coredns-powerdns-api-wrapper/config_test.go
2026-07-10 23:53:43 +02:00

114 lines
3.2 KiB
Go

// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (C) 2026 Bryan Joshua Pedini
package main
import (
"os"
"os/exec"
"strings"
"testing"
)
// TestLoadConfigDefaults exercises the success path in-process: all required
// vars set, defaults left unset, and asserts the documented defaults.
func TestLoadConfigDefaults(t *testing.T) {
t.Setenv("PDNS_API_KEY", "k")
t.Setenv("MYSQL_DSN", "user:pass@tcp(127.0.0.1:3306)/db")
t.Setenv("PUID", "1000")
t.Setenv("PGID", "1000")
os.Unsetenv("LISTEN_ADDR")
os.Unsetenv("TABLE_PREFIX")
os.Unsetenv("DEFAULT_TTL")
os.Unsetenv("PDNS_SERVER_ID")
os.Unsetenv("LOG_LEVEL")
cfg := LoadConfig()
if cfg.ListenAddr != ":3000" {
t.Errorf("ListenAddr = %q, want :3000", cfg.ListenAddr)
}
if cfg.TablePrefix != "coredns_" {
t.Errorf("TablePrefix = %q, want coredns_", cfg.TablePrefix)
}
if cfg.DefaultTTL != 120 {
t.Errorf("DefaultTTL = %d, want 120", cfg.DefaultTTL)
}
if cfg.PDNSServerID != "localhost" {
t.Errorf("PDNSServerID = %q, want localhost", cfg.PDNSServerID)
}
if cfg.LogLevel != "info" {
t.Errorf("LogLevel = %q, want info", cfg.LogLevel)
}
}
// TestLoadConfigFatal exercises the os.Exit(1) paths of LoadConfig via a
// re-exec subprocess, since LoadConfig calls os.Exit directly and cannot be
// asserted in-process.
func TestLoadConfigFatal(t *testing.T) {
baseEnv := map[string]string{
"PDNS_API_KEY": "k",
"MYSQL_DSN": "user:pass@tcp(127.0.0.1:3306)/db",
"PUID": "1000",
"PGID": "1000",
}
tests := []struct {
name string
mutate map[string]string // overrides/additions to baseEnv; "" value means unset
wantInErr string
}{
{"missing PDNS_API_KEY", map[string]string{"PDNS_API_KEY": ""}, "PDNS_API_KEY"},
{"missing MYSQL_DSN", map[string]string{"MYSQL_DSN": ""}, "MYSQL_DSN"},
{"missing PUID", map[string]string{"PUID": ""}, "PUID"},
{"missing PGID", map[string]string{"PGID": ""}, "PGID"},
{"PUID zero rejected", map[string]string{"PUID": "0"}, "root"},
{"PGID zero rejected", map[string]string{"PGID": "0"}, "root"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
env := map[string]string{}
for k, v := range baseEnv {
env[k] = v
}
for k, v := range tt.mutate {
env[k] = v
}
cmd := exec.Command(os.Args[0], "-test.run=TestConfigFatalHelper")
cmd.Env = []string{"GO_CONFIG_CRASH=1"}
for k, v := range env {
if v != "" {
cmd.Env = append(cmd.Env, k+"="+v)
}
}
out, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("expected non-zero exit, got success. output: %s", out)
}
exitErr, ok := err.(*exec.ExitError)
if !ok {
t.Fatalf("expected *exec.ExitError, got %T: %v", err, err)
}
if exitErr.ExitCode() == 0 {
t.Fatalf("expected non-zero exit code")
}
if !strings.Contains(string(out), tt.wantInErr) {
t.Fatalf("stderr output = %q, want substring %q", out, tt.wantInErr)
}
})
}
}
// TestConfigFatalHelper is not a real test; it's invoked as a subprocess by
// TestLoadConfigFatal via -test.run, guarded by GO_CONFIG_CRASH so it never
// runs (and never touches the DB) under a normal `go test` invocation.
func TestConfigFatalHelper(t *testing.T) {
if os.Getenv("GO_CONFIG_CRASH") == "" {
t.Skip("only runs as a re-exec subprocess")
}
LoadConfig()
}