Files
wild-central/internal/ddns/cloudflare_test.go
2026-07-10 20:46:22 +00:00

229 lines
4.8 KiB
Go

package ddns
import (
"context"
"testing"
"time"
)
func TestNewRunner_ZeroStatus(t *testing.T) {
rn := NewRunner()
s := rn.GetStatus()
if s.Enabled {
t.Error("new runner should have Enabled=false")
}
if s.CurrentIP != "" {
t.Errorf("new runner should have empty CurrentIP, got %q", s.CurrentIP)
}
if !s.LastChecked.IsZero() {
t.Error("new runner should have zero LastChecked")
}
}
func TestTrigger_NonBlocking(t *testing.T) {
rn := NewRunner()
done := make(chan struct{})
go func() {
rn.Trigger()
rn.Trigger()
close(done)
}()
select {
case <-done:
case <-time.After(time.Second):
t.Error("Trigger blocked unexpectedly")
}
}
func staticParams(token string, records []string) ParamsFunc {
return func() Params {
return Params{APIToken: token, Records: records, IntervalMinutes: 5}
}
}
func TestStart_SetsEnabledStatus(t *testing.T) {
rn := NewRunner()
started := make(chan struct{}, 1)
rn.ipFetcher = func() (string, error) {
select {
case started <- struct{}{}:
default:
}
return "1.2.3.4", nil
}
rn.Start(context.Background(), staticParams("tok", []string{"a.example.com"}))
select {
case <-started:
case <-time.After(time.Second):
t.Fatal("goroutine did not start in time")
}
if !rn.GetStatus().Enabled {
t.Error("expected Enabled=true after Start with valid params")
}
rn.Stop()
if rn.GetStatus().Enabled {
t.Error("expected Enabled=false after Stop()")
}
}
func TestStart_InvalidParams_SetsDisabled(t *testing.T) {
for _, pfn := range []ParamsFunc{
func() Params { return Params{} },
func() Params { return Params{APIToken: ""} },
func() Params { return Params{APIToken: "tok", Records: nil} },
} {
rn := NewRunner()
rn.Start(context.Background(), pfn)
if rn.GetStatus().Enabled {
t.Errorf("expected Enabled=false for empty params")
}
}
}
func TestStart_Restart_KeepsEnabled(t *testing.T) {
rn := NewRunner()
reached := make(chan struct{}, 2)
rn.ipFetcher = func() (string, error) {
select {
case reached <- struct{}{}:
default:
}
return "1.2.3.4", nil
}
pfn := staticParams("tok", []string{"a.example.com"})
rn.Start(context.Background(), pfn)
select {
case <-reached:
case <-time.After(time.Second):
t.Fatal("first goroutine did not start")
}
// Restart — old goroutine is cancelled, new one starts
rn.Start(context.Background(), pfn)
if !rn.GetStatus().Enabled {
t.Error("expected Enabled=true after restart")
}
rn.Stop()
}
func TestStop_ClearsEnabled(t *testing.T) {
rn := NewRunner()
rn.mu.Lock()
rn.status.Enabled = true
rn.mu.Unlock()
rn.Stop()
if rn.GetStatus().Enabled {
t.Error("expected Enabled=false after Stop()")
}
}
func TestStart_CancelsAndRestarts(t *testing.T) {
rn := NewRunner()
block := make(chan struct{})
firstReady := make(chan struct{}, 1)
rn.ipFetcher = func() (string, error) {
select {
case firstReady <- struct{}{}:
default:
}
<-block
return "1.2.3.4", nil
}
pfn := staticParams("tok", []string{"a.example.com"})
rn.Start(context.Background(), pfn)
select {
case <-firstReady:
case <-time.After(time.Second):
t.Fatal("first goroutine did not start")
}
close(block)
// Second Start with disabled params must not deadlock
done := make(chan struct{})
go func() {
rn.Start(context.Background(), func() Params { return Params{} })
close(done)
}()
select {
case <-done:
case <-time.After(time.Second):
t.Error("second Start blocked unexpectedly")
}
}
func TestCheckAndUpdate_UpdatesCurrentIP(t *testing.T) {
rn := NewRunner()
rn.ipFetcher = func() (string, error) { return "5.6.7.8", nil }
rn.paramsFn = staticParams("fake", []string{"a.example.com"})
rn.checkAndUpdate()
if rn.GetStatus().CurrentIP != "5.6.7.8" {
t.Errorf("expected CurrentIP=5.6.7.8, got %q", rn.GetStatus().CurrentIP)
}
}
func TestCheckAndUpdate_SkipsWhenNoParams(t *testing.T) {
rn := NewRunner()
rn.ipFetcher = func() (string, error) { return "1.2.3.4", nil }
rn.paramsFn = func() Params { return Params{} }
rn.checkAndUpdate()
if rn.GetStatus().CurrentIP != "" {
t.Error("expected empty CurrentIP when params have no token/records")
}
}
func TestCheckAndUpdate_ParamsFuncCalledEachTime(t *testing.T) {
rn := NewRunner()
rn.ipFetcher = func() (string, error) { return "1.2.3.4", nil }
calls := 0
rn.paramsFn = func() Params {
calls++
return Params{APIToken: "fake", Records: []string{"a.example.com"}}
}
rn.checkAndUpdate()
rn.checkAndUpdate()
if calls != 2 {
t.Errorf("expected paramsFn called 2 times, got %d", calls)
}
}
func TestGetStatus_ReflectsManualUpdate(t *testing.T) {
rn := NewRunner()
rn.mu.Lock()
rn.status.CurrentIP = "1.2.3.4"
rn.status.LastChecked = time.Now()
rn.status.Enabled = true
rn.mu.Unlock()
s := rn.GetStatus()
if s.CurrentIP != "1.2.3.4" {
t.Errorf("got CurrentIP %q, want 1.2.3.4", s.CurrentIP)
}
if !s.Enabled {
t.Error("expected Enabled=true")
}
}