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) } }