Skip to content

Commit 22160c8

Browse files
committed
Add a test for the UI
1 parent 9b7c23c commit 22160c8

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package main
2+
3+
import (
4+
"net"
5+
"testing"
6+
"time"
7+
8+
"codeberg.org/miekg/dns"
9+
)
10+
11+
// TestCacheStatisticsAccuracy tests that cache statistics only include
12+
// queries that participate in caching (cache hits + queries that went to a server).
13+
// Blocked queries should be excluded from cache statistics.
14+
func TestCacheStatisticsAccuracy(t *testing.T) {
15+
// Create a minimal proxy config
16+
proxy := &Proxy{
17+
monitoringUI: MonitoringUIConfig{
18+
Enabled: true,
19+
MaxQueryLogEntries: 100,
20+
MaxMemoryMB: 1,
21+
},
22+
}
23+
24+
// Create monitoring UI and metrics collector
25+
ui := NewMonitoringUI(proxy)
26+
if ui == nil {
27+
t.Fatal("Failed to create monitoring UI")
28+
}
29+
mc := ui.metricsCollector
30+
31+
// Initial state: no cache hits or misses
32+
if mc.cacheHits != 0 {
33+
t.Errorf("Initial cacheHits should be 0, got %d", mc.cacheHits)
34+
}
35+
if mc.cacheMisses != 0 {
36+
t.Errorf("Initial cacheMisses should be 0, got %d", mc.cacheMisses)
37+
}
38+
39+
// Test case 1: Cache hit - should increment cacheHits
40+
{
41+
msg := &dns.Msg{}
42+
msg.Question = []dns.RR{&dns.A{Hdr: dns.Header{Name: "example.com.", Class: dns.ClassINET}}}
43+
44+
addr := net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}
45+
clientAddr := net.Addr(&addr)
46+
47+
pluginsState := PluginsState{
48+
requestStart: time.Now(),
49+
timeout: 5 * time.Second,
50+
qName: "example.com.",
51+
serverName: "cloudflare",
52+
clientProto: "udp",
53+
clientAddr: &clientAddr,
54+
cacheHit: true, // This is a cache hit
55+
returnCode: PluginsReturnCodePass,
56+
}
57+
58+
ui.UpdateMetrics(pluginsState, msg)
59+
60+
if mc.cacheHits != 1 {
61+
t.Errorf("After cache hit, cacheHits should be 1, got %d", mc.cacheHits)
62+
}
63+
if mc.cacheMisses != 0 {
64+
t.Errorf("After cache hit, cacheMisses should be 0, got %d", mc.cacheMisses)
65+
}
66+
}
67+
68+
// Test case 2: Cache miss with server resolution - should increment cacheMisses
69+
{
70+
msg := &dns.Msg{}
71+
msg.Question = []dns.RR{&dns.A{Hdr: dns.Header{Name: "example2.com.", Class: dns.ClassINET}}}
72+
73+
addr := net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}
74+
clientAddr := net.Addr(&addr)
75+
76+
pluginsState := PluginsState{
77+
requestStart: time.Now(),
78+
timeout: 5 * time.Second,
79+
qName: "example2.com.",
80+
serverName: "cloudflare", // Query went to a server
81+
clientProto: "udp",
82+
clientAddr: &clientAddr,
83+
cacheHit: false, // Cache miss
84+
returnCode: PluginsReturnCodePass,
85+
}
86+
87+
ui.UpdateMetrics(pluginsState, msg)
88+
89+
if mc.cacheHits != 1 {
90+
t.Errorf("After cache miss, cacheHits should still be 1, got %d", mc.cacheHits)
91+
}
92+
if mc.cacheMisses != 1 {
93+
t.Errorf("After cache miss, cacheMisses should be 1, got %d", mc.cacheMisses)
94+
}
95+
}
96+
97+
// Test case 3: Blocked query (REJECT) - should NOT increment either counter
98+
{
99+
msg := &dns.Msg{}
100+
msg.Question = []dns.RR{&dns.A{Hdr: dns.Header{Name: "blocked.com.", Class: dns.ClassINET}}}
101+
102+
addr := net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}
103+
clientAddr := net.Addr(&addr)
104+
105+
pluginsState := PluginsState{
106+
requestStart: time.Now(),
107+
timeout: 5 * time.Second,
108+
qName: "blocked.com.",
109+
serverName: "-", // No server - query was blocked
110+
clientProto: "udp",
111+
clientAddr: &clientAddr,
112+
cacheHit: false,
113+
returnCode: PluginsReturnCodeReject, // Blocked query
114+
}
115+
116+
ui.UpdateMetrics(pluginsState, msg)
117+
118+
// Cache stats should NOT change for blocked queries
119+
if mc.cacheHits != 1 {
120+
t.Errorf("After blocked query, cacheHits should still be 1, got %d", mc.cacheHits)
121+
}
122+
if mc.cacheMisses != 1 {
123+
t.Errorf("After blocked query, cacheMisses should still be 1, got %d", mc.cacheMisses)
124+
}
125+
if mc.blockCount != 1 {
126+
t.Errorf("After blocked query, blockCount should be 1, got %d", mc.blockCount)
127+
}
128+
}
129+
130+
// Test case 4: Another blocked query (DROP) - should NOT increment cache counters
131+
{
132+
msg := &dns.Msg{}
133+
msg.Question = []dns.RR{&dns.A{Hdr: dns.Header{Name: "dropped.com.", Class: dns.ClassINET}}}
134+
135+
addr := net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}
136+
clientAddr := net.Addr(&addr)
137+
138+
pluginsState := PluginsState{
139+
requestStart: time.Now(),
140+
timeout: 5 * time.Second,
141+
qName: "dropped.com.",
142+
serverName: "-", // No server - query was dropped
143+
clientProto: "udp",
144+
clientAddr: &clientAddr,
145+
cacheHit: false,
146+
returnCode: PluginsReturnCodeDrop, // Dropped query
147+
}
148+
149+
ui.UpdateMetrics(pluginsState, msg)
150+
151+
// Cache stats should NOT change for dropped queries
152+
if mc.cacheHits != 1 {
153+
t.Errorf("After dropped query, cacheHits should still be 1, got %d", mc.cacheHits)
154+
}
155+
if mc.cacheMisses != 1 {
156+
t.Errorf("After dropped query, cacheMisses should still be 1, got %d", mc.cacheMisses)
157+
}
158+
if mc.blockCount != 2 {
159+
t.Errorf("After dropped query, blockCount should be 2, got %d", mc.blockCount)
160+
}
161+
}
162+
163+
// Verify cache hit ratio calculation
164+
metrics := mc.GetMetrics()
165+
cacheHitRatio, ok := metrics["cache_hit_ratio"].(float64)
166+
if !ok {
167+
t.Fatal("cache_hit_ratio not found in metrics or wrong type")
168+
}
169+
170+
// Expected: 1 hit / (1 hit + 1 miss) = 0.5
171+
expectedRatio := 0.5
172+
if cacheHitRatio != expectedRatio {
173+
t.Errorf("Expected cache hit ratio %.2f, got %.2f", expectedRatio, cacheHitRatio)
174+
}
175+
176+
// Verify total queries includes all queries (including blocked ones)
177+
totalQueries, ok := metrics["total_queries"].(uint64)
178+
if !ok {
179+
t.Fatal("total_queries not found in metrics or wrong type")
180+
}
181+
// We sent 4 queries total (1 cache hit + 1 cache miss + 2 blocked)
182+
if totalQueries != 4 {
183+
t.Errorf("Expected total_queries to be 4, got %d", totalQueries)
184+
}
185+
186+
// Verify blocked queries count
187+
blockedQueries, ok := metrics["blocked_queries"].(uint64)
188+
if !ok {
189+
t.Fatal("blocked_queries not found in metrics or wrong type")
190+
}
191+
if blockedQueries != 2 {
192+
t.Errorf("Expected blocked_queries to be 2, got %d", blockedQueries)
193+
}
194+
}

0 commit comments

Comments
 (0)