diff options
Diffstat (limited to 'plugin/cache/cache_test.go')
-rw-r--r-- | plugin/cache/cache_test.go | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/plugin/cache/cache_test.go b/plugin/cache/cache_test.go index d839ea1a3..7f8c28e3f 100644 --- a/plugin/cache/cache_test.go +++ b/plugin/cache/cache_test.go @@ -266,7 +266,7 @@ func TestServeFromStaleCache(t *testing.T) { req.SetQuestion("cached.org.", dns.TypeA) ctx := context.TODO() - // Cache example.org. + // Cache cached.org. with 60s TTL rec := dnstest.NewRecorder(&test.ResponseWriter{}) c.staleUpTo = 1 * time.Hour c.ServeDNS(ctx, rec, req) @@ -304,6 +304,80 @@ func TestServeFromStaleCache(t *testing.T) { } } +func TestServeFromStaleCacheFetchVerify(t *testing.T) { + c := New() + c.Next = ttlBackend(120) + + req := new(dns.Msg) + req.SetQuestion("cached.org.", dns.TypeA) + ctx := context.TODO() + + // Cache cached.org. with 120s TTL + rec := dnstest.NewRecorder(&test.ResponseWriter{}) + c.staleUpTo = 1 * time.Hour + c.verifyStale = true + c.ServeDNS(ctx, rec, req) + if c.pcache.Len() != 1 { + t.Fatalf("Msg with > 0 TTL should have been cached") + } + + tests := []struct { + name string + upstreamRCode int + upstreamTtl int + futureMinutes int + expectedRCode int + expectedTtl int + }{ + // After 1 minutes of initial TTL, we should see a cached response + {"cached.org.", dns.RcodeSuccess, 200, 1, dns.RcodeSuccess, 60}, // ttl = 120 - 60 -- not refreshed + + // After the 2 more minutes, we should see upstream responses because upstream is available + {"cached.org.", dns.RcodeSuccess, 200, 3, dns.RcodeSuccess, 200}, + + // After the TTL expired, if the server fails we should get the cached entry + {"cached.org.", dns.RcodeServerFailure, 200, 7, dns.RcodeSuccess, 0}, + + // After 1 more minutes, if the server serves nxdomain we should see them (despite being within the serve stale period) + {"cached.org.", dns.RcodeNameError, 150, 8, dns.RcodeNameError, 150}, + } + + for i, tt := range tests { + rec := dnstest.NewRecorder(&test.ResponseWriter{}) + c.now = func() time.Time { return time.Now().Add(time.Duration(tt.futureMinutes) * time.Minute) } + + if tt.upstreamRCode == dns.RcodeSuccess { + c.Next = ttlBackend(tt.upstreamTtl) + } else if tt.upstreamRCode == dns.RcodeServerFailure { + // Make upstream fail, should now rely on cache during the c.staleUpTo period + c.Next = servFailBackend(tt.upstreamTtl) + } else if tt.upstreamRCode == dns.RcodeNameError { + c.Next = nxDomainBackend(tt.upstreamTtl) + } else { + t.Fatal("upstream code not implemented") + } + + r := req.Copy() + r.SetQuestion(tt.name, dns.TypeA) + ret, _ := c.ServeDNS(ctx, rec, r) + if ret != tt.expectedRCode { + t.Errorf("Test %d: expected rcode=%v, got rcode=%v", i, tt.expectedRCode, ret) + continue + } + if ret == dns.RcodeSuccess { + recTtl := rec.Msg.Answer[0].Header().Ttl + if tt.expectedTtl != int(recTtl) { + t.Errorf("Test %d: expected TTL=%d, got TTL=%d", i, tt.expectedTtl, recTtl) + } + } else if ret == dns.RcodeNameError { + soaTtl := rec.Msg.Ns[0].Header().Ttl + if tt.expectedTtl != int(soaTtl) { + t.Errorf("Test %d: expected TTL=%d, got TTL=%d", i, tt.expectedTtl, soaTtl) + } + } + } +} + func TestNegativeStaleMaskingPositiveCache(t *testing.T) { c := New() c.staleUpTo = time.Minute * 10 @@ -454,6 +528,20 @@ func ttlBackend(ttl int) plugin.Handler { }) } +func servFailBackend(ttl int) plugin.Handler { + return plugin.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + m := new(dns.Msg) + m.SetReply(r) + m.Response, m.RecursionAvailable = true, true + + m.Ns = []dns.RR{test.SOA(fmt.Sprintf("example.org. %d IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082540 7200 3600 1209600 3600", ttl))} + + m.MsgHdr.Rcode = dns.RcodeServerFailure + w.WriteMsg(m) + return dns.RcodeServerFailure, nil + }) +} + func TestComputeTTL(t *testing.T) { tests := []struct { msgTTL time.Duration |