mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-22 18:26:41 +00:00
Split isConnectedOnAllWay into a thin method that snapshots state and a pure evalConnStatus helper that takes a connStatusInputs struct, so the tri-state decision logic can be exercised without constructing full Worker or Handshaker objects. Add table-driven tests covering force-relay, ICE-unavailable and fully-available code paths, plus unit tests for iceRetryState budget/hourly transitions and reset.
104 lines
2.3 KiB
Go
104 lines
2.3 KiB
Go
package guard
|
|
|
|
import (
|
|
"testing"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func newTestRetryState() *iceRetryState {
|
|
return &iceRetryState{log: log.NewEntry(log.StandardLogger())}
|
|
}
|
|
|
|
func TestICERetryState_AllowsInitialBudget(t *testing.T) {
|
|
s := newTestRetryState()
|
|
|
|
for i := 1; i <= maxICERetries; i++ {
|
|
if !s.shouldRetry() {
|
|
t.Fatalf("shouldRetry returned false on attempt %d, want true (budget = %d)", i, maxICERetries)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestICERetryState_ExhaustsAfterBudget(t *testing.T) {
|
|
s := newTestRetryState()
|
|
|
|
for i := 0; i < maxICERetries; i++ {
|
|
_ = s.shouldRetry()
|
|
}
|
|
|
|
if s.shouldRetry() {
|
|
t.Fatalf("shouldRetry returned true after budget exhausted, want false")
|
|
}
|
|
}
|
|
|
|
func TestICERetryState_HourlyCNilBeforeEnterHourlyMode(t *testing.T) {
|
|
s := newTestRetryState()
|
|
|
|
if s.hourlyC() != nil {
|
|
t.Fatalf("hourlyC returned non-nil channel before enterHourlyMode")
|
|
}
|
|
}
|
|
|
|
func TestICERetryState_EnterHourlyModeArmsTicker(t *testing.T) {
|
|
s := newTestRetryState()
|
|
for i := 0; i < maxICERetries+1; i++ {
|
|
_ = s.shouldRetry()
|
|
}
|
|
|
|
s.enterHourlyMode()
|
|
defer s.reset()
|
|
|
|
if s.hourlyC() == nil {
|
|
t.Fatalf("hourlyC returned nil after enterHourlyMode")
|
|
}
|
|
}
|
|
|
|
func TestICERetryState_ShouldRetryTrueInHourlyMode(t *testing.T) {
|
|
s := newTestRetryState()
|
|
s.enterHourlyMode()
|
|
defer s.reset()
|
|
|
|
if !s.shouldRetry() {
|
|
t.Fatalf("shouldRetry returned false in hourly mode, want true")
|
|
}
|
|
|
|
// Subsequent calls also return true — we keep retrying on each hourly tick.
|
|
if !s.shouldRetry() {
|
|
t.Fatalf("second shouldRetry returned false in hourly mode, want true")
|
|
}
|
|
}
|
|
|
|
func TestICERetryState_ResetRestoresBudget(t *testing.T) {
|
|
s := newTestRetryState()
|
|
for i := 0; i < maxICERetries+1; i++ {
|
|
_ = s.shouldRetry()
|
|
}
|
|
s.enterHourlyMode()
|
|
|
|
s.reset()
|
|
|
|
if s.hourlyC() != nil {
|
|
t.Fatalf("hourlyC returned non-nil channel after reset")
|
|
}
|
|
if s.retries != 0 {
|
|
t.Fatalf("retries = %d after reset, want 0", s.retries)
|
|
}
|
|
|
|
for i := 1; i <= maxICERetries; i++ {
|
|
if !s.shouldRetry() {
|
|
t.Fatalf("shouldRetry returned false on attempt %d after reset, want true", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestICERetryState_ResetIsIdempotent(t *testing.T) {
|
|
s := newTestRetryState()
|
|
s.reset()
|
|
s.reset() // second call must not panic or re-stop a nil ticker
|
|
|
|
if s.hourlyC() != nil {
|
|
t.Fatalf("hourlyC non-nil after double reset")
|
|
}
|
|
}
|