aboutsummaryrefslogtreecommitdiff
path: root/plugin/cache/cache_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/cache/cache_test.go')
-rw-r--r--plugin/cache/cache_test.go90
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