All checks were successful
check / check (push) Successful in 4s
When set to a truthy value, sends a startup status notification to all configured notification channels after the first full scan completes. The notification is clearly an all-ok/success message showing the number of monitored domains, hostnames, ports, and certificates. Closes #84
263 lines
7.5 KiB
Go
263 lines
7.5 KiB
Go
package config_test
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/spf13/viper"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"sneak.berlin/go/dnswatcher/internal/config"
|
|
"sneak.berlin/go/dnswatcher/internal/globals"
|
|
"sneak.berlin/go/dnswatcher/internal/logger"
|
|
)
|
|
|
|
// newTestParams creates config.Params suitable for testing
|
|
// without requiring the fx dependency injection framework.
|
|
func newTestParams(t *testing.T) config.Params {
|
|
t.Helper()
|
|
|
|
g := &globals.Globals{
|
|
Appname: "dnswatcher",
|
|
Version: "test",
|
|
}
|
|
|
|
l, err := logger.New(nil, logger.Params{Globals: g})
|
|
require.NoError(t, err, "failed to create logger")
|
|
|
|
return config.Params{
|
|
Globals: g,
|
|
Logger: l,
|
|
}
|
|
}
|
|
|
|
// These tests exercise viper global state and MUST NOT use
|
|
// t.Parallel(). Each test resets viper for isolation.
|
|
|
|
func TestNew_DefaultValues(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com,www.example.com")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 8080, cfg.Port)
|
|
assert.False(t, cfg.Debug)
|
|
assert.Equal(t, "./data", cfg.DataDir)
|
|
assert.Equal(t, time.Hour, cfg.DNSInterval)
|
|
assert.Equal(t, 12*time.Hour, cfg.TLSInterval)
|
|
assert.Equal(t, 7, cfg.TLSExpiryWarning)
|
|
assert.False(t, cfg.MaintenanceMode)
|
|
assert.Empty(t, cfg.SlackWebhook)
|
|
assert.Empty(t, cfg.MattermostWebhook)
|
|
assert.Empty(t, cfg.NtfyTopic)
|
|
assert.Empty(t, cfg.SentryDSN)
|
|
assert.Empty(t, cfg.MetricsUsername)
|
|
assert.Empty(t, cfg.MetricsPassword)
|
|
assert.False(t, cfg.SendTestNotification)
|
|
}
|
|
|
|
func TestNew_EnvironmentOverrides(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
t.Setenv("PORT", "9090")
|
|
t.Setenv("DNSWATCHER_DEBUG", "true")
|
|
t.Setenv("DNSWATCHER_DATA_DIR", "/tmp/test-data")
|
|
t.Setenv("DNSWATCHER_DNS_INTERVAL", "30m")
|
|
t.Setenv("DNSWATCHER_TLS_INTERVAL", "6h")
|
|
t.Setenv("DNSWATCHER_TLS_EXPIRY_WARNING", "14")
|
|
t.Setenv("DNSWATCHER_SLACK_WEBHOOK", "https://hooks.slack.com/t")
|
|
t.Setenv("DNSWATCHER_MATTERMOST_WEBHOOK", "https://mm.test/hooks/t")
|
|
t.Setenv("DNSWATCHER_NTFY_TOPIC", "https://ntfy.sh/test")
|
|
t.Setenv("DNSWATCHER_SENTRY_DSN", "https://sentry.test/1")
|
|
t.Setenv("DNSWATCHER_MAINTENANCE_MODE", "true")
|
|
t.Setenv("DNSWATCHER_METRICS_USERNAME", "admin")
|
|
t.Setenv("DNSWATCHER_METRICS_PASSWORD", "secret")
|
|
t.Setenv("DNSWATCHER_SEND_TEST_NOTIFICATION", "true")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 9090, cfg.Port)
|
|
assert.True(t, cfg.Debug)
|
|
assert.Equal(t, "/tmp/test-data", cfg.DataDir)
|
|
assert.Equal(t, 30*time.Minute, cfg.DNSInterval)
|
|
assert.Equal(t, 6*time.Hour, cfg.TLSInterval)
|
|
assert.Equal(t, 14, cfg.TLSExpiryWarning)
|
|
assert.Equal(t, "https://hooks.slack.com/t", cfg.SlackWebhook)
|
|
assert.Equal(t, "https://mm.test/hooks/t", cfg.MattermostWebhook)
|
|
assert.Equal(t, "https://ntfy.sh/test", cfg.NtfyTopic)
|
|
assert.Equal(t, "https://sentry.test/1", cfg.SentryDSN)
|
|
assert.True(t, cfg.MaintenanceMode)
|
|
assert.Equal(t, "admin", cfg.MetricsUsername)
|
|
assert.Equal(t, "secret", cfg.MetricsPassword)
|
|
assert.True(t, cfg.SendTestNotification)
|
|
}
|
|
|
|
func TestNew_NoTargetsError(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "")
|
|
|
|
_, err := config.New(nil, newTestParams(t))
|
|
require.Error(t, err)
|
|
assert.ErrorIs(t, err, config.ErrNoTargets)
|
|
}
|
|
|
|
func TestNew_OnlyEmptyCSVSegments(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", " , , ")
|
|
|
|
_, err := config.New(nil, newTestParams(t))
|
|
require.Error(t, err)
|
|
assert.ErrorIs(t, err, config.ErrNoTargets)
|
|
}
|
|
|
|
func TestNew_InvalidDNSInterval_FallsBackToDefault(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
t.Setenv("DNSWATCHER_DNS_INTERVAL", "banana")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, time.Hour, cfg.DNSInterval,
|
|
"invalid DNS interval should fall back to 1h default")
|
|
}
|
|
|
|
func TestNew_InvalidTLSInterval_FallsBackToDefault(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
t.Setenv("DNSWATCHER_TLS_INTERVAL", "notaduration")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 12*time.Hour, cfg.TLSInterval,
|
|
"invalid TLS interval should fall back to 12h default")
|
|
}
|
|
|
|
func TestNew_BothIntervalsInvalid(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
t.Setenv("DNSWATCHER_DNS_INTERVAL", "xyz")
|
|
t.Setenv("DNSWATCHER_TLS_INTERVAL", "abc")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, time.Hour, cfg.DNSInterval)
|
|
assert.Equal(t, 12*time.Hour, cfg.TLSInterval)
|
|
}
|
|
|
|
func TestNew_DebugEnablesDebugLogging(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
t.Setenv("DNSWATCHER_DEBUG", "true")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.True(t, cfg.Debug)
|
|
}
|
|
|
|
func TestNew_PortEnvNotPrefixed(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
t.Setenv("PORT", "3000")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 3000, cfg.Port,
|
|
"PORT env should work without DNSWATCHER_ prefix")
|
|
}
|
|
|
|
func TestNew_TargetClassification(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS",
|
|
"example.com,www.example.com,api.example.com,example.org")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
|
|
// example.com and example.org are apex domains
|
|
assert.Len(t, cfg.Domains, 2)
|
|
// www.example.com and api.example.com are hostnames
|
|
assert.Len(t, cfg.Hostnames, 2)
|
|
}
|
|
|
|
func TestNew_InvalidTargetPublicSuffix(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "co.uk")
|
|
|
|
_, err := config.New(nil, newTestParams(t))
|
|
require.Error(t, err, "public suffix should be rejected")
|
|
}
|
|
|
|
func TestNew_EmptyAppnameDefaultsToDnswatcher(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
|
|
g := &globals.Globals{Appname: "", Version: "test"}
|
|
|
|
l, err := logger.New(nil, logger.Params{Globals: g})
|
|
require.NoError(t, err)
|
|
|
|
cfg, err := config.New(
|
|
nil, config.Params{Globals: g, Logger: l},
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 8080, cfg.Port,
|
|
"defaults should load when appname is empty")
|
|
}
|
|
|
|
func TestNew_TargetsWithWhitespace(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", " example.com , www.example.com ")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(cfg.Domains)+len(cfg.Hostnames),
|
|
"whitespace around targets should be trimmed")
|
|
}
|
|
|
|
func TestNew_TargetsWithTrailingComma(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com,www.example.com,")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(cfg.Domains)+len(cfg.Hostnames),
|
|
"trailing comma should be ignored")
|
|
}
|
|
|
|
func TestNew_CustomDNSIntervalDuration(t *testing.T) {
|
|
viper.Reset()
|
|
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
|
t.Setenv("DNSWATCHER_DNS_INTERVAL", "5s")
|
|
|
|
cfg, err := config.New(nil, newTestParams(t))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 5*time.Second, cfg.DNSInterval)
|
|
}
|
|
|
|
func TestStatePath(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
dataDir string
|
|
want string
|
|
}{
|
|
{"default", "./data", "./data/state.json"},
|
|
{"absolute", "/var/lib/dw", "/var/lib/dw/state.json"},
|
|
{"nested", "/opt/app/data", "/opt/app/data/state.json"},
|
|
{"empty", "", "/state.json"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := &config.Config{DataDir: tt.dataDir}
|
|
assert.Equal(t, tt.want, cfg.StatePath())
|
|
})
|
|
}
|
|
}
|