Files
traefik-selectel/internal/config/config_test.go
T
vasyanskandClaude Sonnet 5 273cd54415
Build / Tests (push) Successful in 13s
Build / Build image (push) Failing after 15s
Build / Notify on failure (push) Failing after 0s
feat: traefik-selectel-dns — A-записи Selectel по роутам Traefik + CI сборка образа
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 21:50:07 +07:00

141 lines
4.5 KiB
Go

package config
import (
"fmt"
"strings"
"testing"
"time"
)
func env(m map[string]string) func(string) string { return func(k string) string { return m[k] } }
func files(m map[string]string) func(string) ([]byte, error) {
return func(p string) ([]byte, error) {
if v, ok := m[p]; ok {
return []byte(v), nil
}
return nil, fmt.Errorf("no such file %s", p)
}
}
var goodEnv = map[string]string{
"SELECTEL_USERNAME": "u", "SELECTEL_PASSWORD": "p", "SELECTEL_ACCOUNT_ID": "1", "SELECTEL_PROJECT_ID": "2",
}
func withEnv(extra map[string]string) map[string]string {
m := map[string]string{}
for k, v := range goodEnv {
m[k] = v
}
for k, v := range extra {
m[k] = v
}
return m
}
func TestLoadYAMLAndDefaults(t *testing.T) {
yml := "zones: [Example.com., example.com, dev.example.org]\nip: 1.2.3.4\ndelete:\n enabled: true\n gracePeriod: 15m\n"
cfg, err := Load("/cfg.yaml", env(goodEnv), files(map[string]string{"/cfg.yaml": yml}))
if err != nil {
t.Fatal(err)
}
if len(cfg.Zones) != 2 || cfg.Zones[0] != "example.com" {
t.Errorf("zones = %v", cfg.Zones)
}
if cfg.TTL != 300 || cfg.Interval != 30*time.Second || cfg.Listen != ":9090" || cfg.Traefik.APIURL != "http://traefik:8080" {
t.Errorf("defaults: %+v", cfg)
}
if !cfg.Delete.Enabled || cfg.Delete.GracePeriod != 15*time.Minute {
t.Errorf("delete: %+v", cfg.Delete)
}
}
func TestDeleteDisabledByDefault(t *testing.T) {
cfg, err := Load("", env(withEnv(map[string]string{"TSD_ZONES": "example.com", "TSD_IP": "1.2.3.4"})), files(nil))
if err != nil {
t.Fatal(err)
}
if cfg.Delete.Enabled || cfg.Delete.GracePeriod != 10*time.Minute {
t.Errorf("delete: %+v", cfg.Delete)
}
}
func TestEnvOverrides(t *testing.T) {
yml := "zones: [example.com]\nip: 1.2.3.4\nttl: 300\n"
cfg, err := Load("/c.yaml", env(withEnv(map[string]string{
"TSD_IP": "5.6.7.8", "TSD_TTL": "120", "TSD_ZONES": "a.com, b.com", "TSD_DRY_RUN": "true", "TSD_INTERVAL": "1m",
})), files(map[string]string{"/c.yaml": yml}))
if err != nil {
t.Fatal(err)
}
if cfg.IP != "5.6.7.8" || cfg.TTL != 120 || len(cfg.Zones) != 2 || cfg.Zones[1] != "b.com" || !cfg.DryRun || cfg.Interval != time.Minute {
t.Errorf("cfg = %+v", cfg)
}
}
func TestUnknownYAMLFieldRejected(t *testing.T) {
_, err := Load("/c.yaml", env(goodEnv), files(map[string]string{"/c.yaml": "zones: [a.com]\nip: 1.1.1.1\npassword: leak\n"}))
if err == nil {
t.Fatal("ожидалась ошибка на неизвестное поле")
}
}
func TestValidation(t *testing.T) {
base := func() map[string]string {
return withEnv(map[string]string{"TSD_ZONES": "example.com", "TSD_IP": "1.2.3.4"})
}
cases := map[string]map[string]string{
"ip не IPv4": {"TSD_IP": "::1"},
"ip мусор": {"TSD_IP": "example"},
"ttl мал": {"TSD_TTL": "59"},
"ttl велик": {"TSD_TTL": "604801"},
"нет зон": {"TSD_ZONES": " "},
"grace < interval": {"TSD_DELETE_ENABLED": "true", "TSD_DELETE_GRACE_PERIOD": "1s"},
"bad loglevel": {"TSD_LOG_LEVEL": "trace"},
"bad bool": {"TSD_DRY_RUN": "maybe"},
"нет пароля": {"SELECTEL_PASSWORD": ""},
}
for name, extra := range cases {
t.Run(name, func(t *testing.T) {
m := base()
for k, v := range extra {
m[k] = v
}
if _, err := Load("", env(m), files(nil)); err == nil {
t.Fatal("ожидалась ошибка")
}
})
}
if _, err := Load("", env(base()), files(nil)); err != nil {
t.Fatalf("базовый конфиг должен быть валиден: %v", err)
}
}
func TestSecretFileSupport(t *testing.T) {
e := map[string]string{
"SELECTEL_USERNAME_FILE": "/run/secrets/u", "SELECTEL_PASSWORD_FILE": "/run/secrets/p",
"SELECTEL_ACCOUNT_ID": "1", "SELECTEL_PROJECT_ID_FILE": "/run/secrets/pid",
}
f := files(map[string]string{"/run/secrets/u": "user\n", "/run/secrets/p": " pass \n", "/run/secrets/pid": "pid-1\n"})
c, err := LoadCredentials(env(e), f)
if err != nil {
t.Fatal(err)
}
if c.Username != "user" || c.Password != "pass" || c.ProjectID != "pid-1" || c.AccountID != "1" {
t.Errorf("creds = %s", c)
}
}
func TestSecretBothSetIsError(t *testing.T) {
_, err := Secret("X", env(map[string]string{"X": "a", "X_FILE": "/f"}), files(map[string]string{"/f": "b"}))
if err == nil || !strings.Contains(err.Error(), "одно") {
t.Fatalf("err = %v", err)
}
}
func TestSecretMissingFile(t *testing.T) {
if _, err := Secret("X", env(map[string]string{"X_FILE": "/nope"}), files(nil)); err == nil {
t.Fatal("ожидалась ошибка")
}
}