package resolver_test import ( "context" "sync" "testing" "time" "github.com/stretchr/testify/assert" "sneak.berlin/go/dnswatcher/internal/resolver" ) // Tests for the live-DNS harness in livedns_test.go itself. These // exercise pure logic and the retry/concurrency plumbing; they // perform no DNS resolution of any kind, so they neither mock DNS // nor depend on it. func TestLiveQuorumIsStrictMajority(t *testing.T) { t.Parallel() cases := map[int]int{ 0: 1, 1: 1, 2: 2, 3: 2, 4: 3, 5: 3, 13: 7, } for total, want := range cases { assert.Equal( t, want, liveQuorum(total), "liveQuorum(%d)", total, ) } } func TestStatusCountingIgnoresSilentNameservers(t *testing.T) { t.Parallel() results := map[string]*resolver.NameserverResponse{ "ns1.example.": { Nameserver: "ns1.example.", Status: resolver.StatusOK, }, "ns2.example.": { Nameserver: "ns2.example.", Status: resolver.StatusOK, }, "ns3.example.": { Nameserver: "ns3.example.", Status: resolver.StatusTimeout, }, "ns4.example.": { Nameserver: "ns4.example.", Status: resolver.StatusError, }, } assert.Equal( t, 2, countStatus(results, resolver.StatusOK), ) assert.Equal( t, 0, countStatus(results, resolver.StatusNXDomain), ) // Two of four answered, which is short of the quorum of // three: this is the state that triggers a retry rather // than an assertion failure. assert.Equal(t, 2, answeredCount(results)) assert.Less(t, answeredCount(results), liveQuorum(len(results))) assert.Equal( t, "ns1.example.=ok ns2.example.=ok "+ "ns3.example.=timeout ns4.example.=error", describeStatuses(results), ) } func TestRetryLiveRecoversFromTransientFailure(t *testing.T) { t.Parallel() const wantAttempts = 2 attempts := 0 retryLive(t, "transient", func(_ context.Context) error { attempts++ if attempts < wantAttempts { return errLiveNoAnswer } return nil }) assert.Equal(t, wantAttempts, attempts) } func TestRetryLiveGivesEachAttemptADeadline(t *testing.T) { t.Parallel() retryLive(t, "deadline", func(ctx context.Context) error { deadline, ok := ctx.Deadline() assert.True(t, ok, "attempt should carry a deadline") assert.LessOrEqual( t, time.Until(deadline), liveAttemptTimeout, ) return nil }) } func TestRunLiveBoundsConcurrency(t *testing.T) { t.Parallel() const workers = 24 var ( mu sync.Mutex wg sync.WaitGroup inFlight int maxSeen int ) wg.Add(workers) for range workers { go func() { defer wg.Done() _ = runLive(func(_ context.Context) error { mu.Lock() inFlight++ if inFlight > maxSeen { maxSeen = inFlight } mu.Unlock() time.Sleep(time.Millisecond) mu.Lock() inFlight-- mu.Unlock() return nil }) }() } wg.Wait() assert.Positive(t, maxSeen) assert.LessOrEqual( t, maxSeen, liveConcurrency, "live queries must stay under the package-wide gate", ) }