Files
pdns-admin/internal/config/config_test.go
2026-06-18 22:32:42 -03:00

286 lines
6.9 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
)
func TestLoadFileReadsYAML(t *testing.T) {
clearConfigEnv(t)
path := writeConfig(t, `
addr: ":9000"
pdns_api_url: "http://pdns.example.test:8081"
pdns_api_key: "from-file"
pdns_server_id: "authoritative"
auth:
disabled: true
`)
cfg, err := LoadFile(path)
if err != nil {
t.Fatalf("LoadFile returned error: %v", err)
}
if cfg.Addr != ":9000" {
t.Fatalf("unexpected addr: %q", cfg.Addr)
}
if cfg.PDNSAPIURL != "http://pdns.example.test:8081" {
t.Fatalf("unexpected api url: %q", cfg.PDNSAPIURL)
}
if cfg.PDNSAPIKey != "from-file" {
t.Fatalf("unexpected api key: %q", cfg.PDNSAPIKey)
}
if cfg.PDNSServerID != "authoritative" {
t.Fatalf("unexpected server id: %q", cfg.PDNSServerID)
}
}
func TestLoadFileEnvironmentOverridesYAML(t *testing.T) {
clearConfigEnv(t)
t.Setenv("PDNS_API_KEY", "from-env")
t.Setenv("PDNS_API_URL", "http://env.example.test:8081")
path := writeConfig(t, `
pdns_api_url: "http://file.example.test:8081"
pdns_api_key: "from-file"
pdns_server_id: "from-file"
auth:
disabled: true
`)
cfg, err := LoadFile(path)
if err != nil {
t.Fatalf("LoadFile returned error: %v", err)
}
if cfg.PDNSAPIKey != "from-env" {
t.Fatalf("expected env api key to win, got %q", cfg.PDNSAPIKey)
}
if cfg.PDNSAPIURL != "http://env.example.test:8081" {
t.Fatalf("expected env api url to win, got %q", cfg.PDNSAPIURL)
}
if cfg.PDNSServerID != "from-file" {
t.Fatalf("expected yaml server id, got %q", cfg.PDNSServerID)
}
}
func TestLoadFileUsesDefaultsWithoutYAML(t *testing.T) {
clearConfigEnv(t)
t.Setenv("PDNS_API_KEY", "secret")
t.Setenv("AUTH_DISABLED", "true")
cfg, err := LoadFile("")
if err != nil {
t.Fatalf("LoadFile returned error: %v", err)
}
if cfg.Addr != ":8080" {
t.Fatalf("unexpected default addr: %q", cfg.Addr)
}
if cfg.PDNSAPIURL != "http://localhost:8081" {
t.Fatalf("unexpected default api url: %q", cfg.PDNSAPIURL)
}
if cfg.PDNSServerID != "localhost" {
t.Fatalf("unexpected default server id: %q", cfg.PDNSServerID)
}
}
func TestLoadFileRequiresAPIKey(t *testing.T) {
clearConfigEnv(t)
_, err := LoadFile("")
if err == nil {
t.Fatal("expected missing api key error")
}
}
func TestLoadFileReadsLDAPConfig(t *testing.T) {
clearConfigEnv(t)
path := writeConfig(t, `
pdns_api_key: "secret"
auth:
ldap:
url: ldap://ldap.example.com:389
start_tls: true
insecure_skip_verify: true
bind_dn: cn=dashboard-reader,ou=service,dc=example,dc=com
bind_password: change-me
user_base_dn: ou=users,dc=example,dc=com
username_attribute: uid
user_filter: "({username_attribute}={username})"
group_base_dn: ou=groups,dc=example,dc=com
group_filter: "(&(objectClass=groupOfNames)(cn=media-admins)(member={user_dn}))"
`)
cfg, err := LoadFile(path)
if err != nil {
t.Fatalf("LoadFile returned error: %v", err)
}
if cfg.Auth.Disabled {
t.Fatal("auth should be enabled")
}
if cfg.Auth.LDAP.URL != "ldap://ldap.example.com:389" {
t.Fatalf("unexpected ldap url: %q", cfg.Auth.LDAP.URL)
}
if !cfg.Auth.LDAP.StartTLS {
t.Fatal("expected start_tls to be true")
}
if !cfg.Auth.LDAP.InsecureSkipVerify {
t.Fatal("expected insecure_skip_verify to be true")
}
if cfg.Auth.LDAP.GroupFilter == "" {
t.Fatal("expected group filter")
}
}
func TestLoadFileLDAPEnvironmentOverridesYAML(t *testing.T) {
clearConfigEnv(t)
t.Setenv("AUTH_LDAP_URL", "ldap://env.example.com:389")
t.Setenv("AUTH_LDAP_BIND_PASSWORD", "from-env")
path := writeConfig(t, `
pdns_api_key: "secret"
auth:
ldap:
url: ldap://file.example.com:389
bind_dn: cn=dashboard-reader,ou=service,dc=example,dc=com
bind_password: from-file
user_base_dn: ou=users,dc=example,dc=com
`)
cfg, err := LoadFile(path)
if err != nil {
t.Fatalf("LoadFile returned error: %v", err)
}
if cfg.Auth.LDAP.URL != "ldap://env.example.com:389" {
t.Fatalf("expected env ldap url, got %q", cfg.Auth.LDAP.URL)
}
if cfg.Auth.LDAP.BindPassword != "from-env" {
t.Fatalf("expected env bind password, got %q", cfg.Auth.LDAP.BindPassword)
}
}
func TestLoadFileRequiresLDAPUnlessAuthDisabled(t *testing.T) {
clearConfigEnv(t)
t.Setenv("PDNS_API_KEY", "secret")
_, err := LoadFile("")
if err == nil {
t.Fatal("expected missing ldap configuration error")
}
}
func TestConfigSearchPathsOrderOnUnix(t *testing.T) {
paths := configSearchPaths("/home/tester", "linux")
want := []string{
"/etc/pdns_admin/config.yaml",
filepath.Join("/home/tester", "config.yaml"),
"config.yaml",
}
if len(paths) != len(want) {
t.Fatalf("unexpected path count: %#v", paths)
}
for i := range want {
if paths[i] != want[i] {
t.Fatalf("path %d = %q, want %q", i, paths[i], want[i])
}
}
}
func TestConfigSearchPathsOrderOnNonUnix(t *testing.T) {
paths := configSearchPaths(`C:\Users\tester`, "windows")
want := []string{
filepath.Join(`C:\Users\tester`, "config.yaml"),
"config.yaml",
}
if len(paths) != len(want) {
t.Fatalf("unexpected path count: %#v", paths)
}
for i := range want {
if paths[i] != want[i] {
t.Fatalf("path %d = %q, want %q", i, paths[i], want[i])
}
}
}
func TestConfigSearchPathsWithoutHome(t *testing.T) {
paths := configSearchPaths("", "windows")
want := []string{"config.yaml"}
if len(paths) != len(want) {
t.Fatalf("unexpected path count: %#v", paths)
}
if paths[0] != want[0] {
t.Fatalf("path = %q, want %q", paths[0], want[0])
}
}
func TestFirstExistingConfigPathUsesOrder(t *testing.T) {
root := t.TempDir()
first := filepath.Join(root, "first.yaml")
second := filepath.Join(root, "second.yaml")
if err := os.WriteFile(second, []byte("pdns_api_key: second"), 0o600); err != nil {
t.Fatalf("write second config: %v", err)
}
if err := os.WriteFile(first, []byte("pdns_api_key: first"), 0o600); err != nil {
t.Fatalf("write first config: %v", err)
}
got := firstExistingConfigPath([]string{first, second})
if got != first {
t.Fatalf("got %q, want %q", got, first)
}
}
func writeConfig(t *testing.T, contents string) string {
t.Helper()
path := filepath.Join(t.TempDir(), "config.yaml")
if err := os.WriteFile(path, []byte(contents), 0o600); err != nil {
t.Fatalf("write config: %v", err)
}
return path
}
func clearConfigEnv(t *testing.T) {
t.Helper()
for _, key := range []string{
"CONFIG_FILE",
"ADDR",
"PDNS_API_URL",
"PDNS_API_KEY",
"PDNS_SERVER_ID",
"AUTH_DISABLED",
"AUTH_LDAP_URL",
"AUTH_LDAP_START_TLS",
"AUTH_LDAP_INSECURE_SKIP_VERIFY",
"AUTH_LDAP_BIND_DN",
"AUTH_LDAP_BIND_PASSWORD",
"AUTH_LDAP_USER_BASE_DN",
"AUTH_LDAP_USERNAME_ATTRIBUTE",
"AUTH_LDAP_USER_FILTER",
"AUTH_LDAP_GROUP_BASE_DN",
"AUTH_LDAP_GROUP_FILTER",
} {
unsetEnv(t, key)
}
}
func unsetEnv(t *testing.T, key string) {
t.Helper()
oldValue, hadValue := os.LookupEnv(key)
if err := os.Unsetenv(key); err != nil {
t.Fatalf("unset %s: %v", key, err)
}
t.Cleanup(func() {
if hadValue {
_ = os.Setenv(key, oldValue)
return
}
_ = os.Unsetenv(key)
})
}