diff options
Diffstat (limited to 'internal/cache/cache_test.go')
| -rw-r--r-- | internal/cache/cache_test.go | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/internal/cache/cache_test.go b/internal/cache/cache_test.go new file mode 100644 index 0000000..6556dcb --- /dev/null +++ b/internal/cache/cache_test.go @@ -0,0 +1,140 @@ +package cache + +import ( + "fmt" + "net" + "testing" + "time" + + "github.com/miekg/dns" +) + +func TestSetGet(t *testing.T) { + c, err := NewCache(100, "") + if err != nil { + t.Fatal(err) + } + defer c.Stop() + + msg := new(dns.Msg) + msg.Answer = append(msg.Answer, &dns.A{ + Hdr: dns.RR_Header{Name: "example.com.", Rrtype: dns.TypeA, Ttl:300}, + A: net.IPv4(1,2,3,4), + }) + + key := Key{Name: "example.com.", Qtype: dns.TypeA, Class: dns.ClassINET} + c.Set(key, msg, 300*time.Second) + + got, ok := c.Get(key) + if !ok { + t.Fatal("expected cache hit") + } + if len(got.Answer) != 1 { + t.Fatalf("expected 1 answer, got %d", len(got.Answer)) + } + a, _ := got.Answer[0].(*dns.A) + if !a.A.Equal(net.IPv4(1,2,3,4)) { + t.Errorf("IP = %s, want 1.2.3.4", a.A) + } +} + +func TestExpiry(t *testing.T) { + c, _ := NewCache(10, "") + defer c.Stop() + msg := new(dns.Msg) + key := Key{Name: "x.com.", Qtype: dns.TypeA, Class: dns.ClassINET} + c.Set(key, msg, 30*time.Millisecond) + time.Sleep(60 * time.Millisecond) + _, ok := c.Get(key) + if ok { + t.Fatal("expected miss after expiry") + } +} + +func TestEviction(t *testing.T) { + c, _ := NewCache(2, "") + defer c.Stop() + for i := 0; i < 5; i++ { + name := fmt.Sprintf("d%d.com.", i) + msg := new(dns.Msg) + c.Set(Key{Name: name, Qtype: dns.TypeA, Class: dns.ClassINET}, msg, 60*time.Second) + } + if c.Len() > 2 { + t.Errorf("expected ≤2 entries, got %d", c.Len()) + } +} + +func TestComputeTTL(t *testing.T) { + msg := new(dns.Msg) + msg.Answer = append(msg.Answer, &dns.A{ + Hdr: dns.RR_Header{Name: "x.", Rrtype: dns.TypeA, Ttl: 120}, + }) + if d := computeTTL(msg); d != 120*time.Second { + t.Errorf("TTL = %v, want 120s", d) + } +} + +func TestNegativeTTL(t *testing.T) { + msg := new(dns.Msg) + msg.Rcode = dns.RcodeNameError + msg.Ns = append(msg.Ns, &dns.SOA{ + Hdr: dns.RR_Header{Name: "com.", Rrtype: dns.TypeSOA, Ttl: 900}, + Minttl: 300, + }) + if d := computeTTL(msg); d != 300*time.Second { + t.Errorf("negative TTL = %v, want 300s", d) + } +} + +func TestRace(t *testing.T) { + c, _ := NewCache(1000, "") + defer c.Stop() + done := make(chan struct{}) + go func() { + for i := 0; i < 100; i++ { + msg := new(dns.Msg) + c.Set(Key{Name: fmt.Sprintf("d%d.com.", i), Qtype: dns.TypeA, Class: dns.ClassINET}, msg, time.Second) + } + close(done) + }() + for i := 0; i < 100; i++ { + c.Get(Key{Name: fmt.Sprintf("d%d.com.", i), Qtype: dns.TypeA, Class: dns.ClassINET}) + } + <-done + c.Stats() + c.Len() +} + +func TestSQLitePersistence(t *testing.T) { + dir := t.TempDir() + dbPath := dir + "/cache.db" + + c, err := NewCache(100, dbPath) + if err != nil { + t.Fatal(err) + } + + msg := new(dns.Msg) + msg.Answer = append(msg.Answer, &dns.A{ + Hdr: dns.RR_Header{Name: "x.com.", Rrtype: dns.TypeA, Ttl: 300}, + A: net.IPv4(1, 2, 3, 4), + }) + key := Key{Name: "x.com.", Qtype: dns.TypeA, Class: dns.ClassINET} + c.Set(key, msg, 300*time.Second) + c.Stop() + + c2, err := NewCache(100, dbPath) + if err != nil { + t.Fatal(err) + } + defer c2.Stop() + + got, ok := c2.Get(key) + if !ok { + t.Fatal("expected cache hit from SQLite load") + } + a, _ := got.Answer[0].(*dns.A) + if !a.A.Equal(net.IPv4(1, 2, 3, 4)) { + t.Errorf("IP = %s, want 1.2.3.4", a.A) + } +} |
