check / check (push) Successful in 1m18s
The watcher tests used a stand-in resolver and the resolver timeout test a stand-in DNS client, against the rule that DNS is never mocked. The watcher tests now run the real resolver against live DNS. A change is tested by saving values live DNS never returns (names under .invalid, 192.0.2.1) in the state a check starts from, or by marking a real nameserver failed. The timeout test queries 192.0.2.1, where nothing answers. The live-DNS retry and concurrency limit moved from the resolver tests to internal/livedns, so both packages share them. NewFromLoggerWithClient had no other use and is gone. TESTING.md and the DNSClient comment now state the README's rule. Model: opus-5-5
104 lines
1.8 KiB
Go
104 lines
1.8 KiB
Go
package livedns_test
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"sneak.berlin/go/dnswatcher/internal/livedns"
|
|
)
|
|
|
|
// Tests for the retry and the concurrency limit themselves. They
|
|
// perform no DNS resolution of any kind.
|
|
|
|
func TestRetryRecoversFromTransientFailure(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const wantAttempts = 2
|
|
|
|
attempts := 0
|
|
|
|
livedns.Retry(t, "transient", func(_ context.Context) error {
|
|
attempts++
|
|
|
|
if attempts < wantAttempts {
|
|
return livedns.ErrNoAnswer
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
assert.Equal(t, wantAttempts, attempts)
|
|
}
|
|
|
|
func TestRetryGivesEachAttemptADeadline(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
livedns.Retry(t, "deadline", func(ctx context.Context) error {
|
|
deadline, ok := ctx.Deadline()
|
|
assert.True(t, ok, "attempt should carry a deadline")
|
|
|
|
remaining := time.Until(deadline)
|
|
|
|
assert.LessOrEqual(t, remaining, livedns.AttemptTimeout)
|
|
|
|
// Lower bound too: without one this passes for a
|
|
// deadline far shorter than intended, which would
|
|
// silently turn every live attempt into an instant
|
|
// timeout.
|
|
assert.Greater(t, remaining, livedns.AttemptTimeout/2)
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestRunBoundsConcurrency(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()
|
|
|
|
_ = livedns.Run(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, livedns.Concurrency,
|
|
"live queries must stay under the package-wide gate",
|
|
)
|
|
}
|