Skip to content

Commit 55ab95e

Browse files
test(metrics): Prometheus metrics (#1868)
1 parent 6581668 commit 55ab95e

1 file changed

Lines changed: 162 additions & 0 deletions

File tree

metrics/metrics_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package metrics_test
2+
3+
import (
4+
"context"
5+
"net"
6+
"strings"
7+
"testing"
8+
"time"
9+
10+
"github.com/0xERR0R/blocky/config"
11+
"github.com/0xERR0R/blocky/evt"
12+
"github.com/0xERR0R/blocky/lists"
13+
"github.com/0xERR0R/blocky/log"
14+
"github.com/0xERR0R/blocky/metrics"
15+
"github.com/0xERR0R/blocky/model"
16+
"github.com/0xERR0R/blocky/resolver"
17+
18+
"github.com/go-chi/chi/v5"
19+
"github.com/miekg/dns"
20+
"github.com/prometheus/client_golang/prometheus"
21+
"github.com/sirupsen/logrus"
22+
)
23+
24+
func init() {
25+
log.Silence() // Silence log output during tests
26+
}
27+
28+
func AssertRegistryComplete(t *testing.T, reg *prometheus.Registry) {
29+
mfs, err := reg.Gather()
30+
if err != nil {
31+
t.Fatalf("failed to gather metrics: %v", err)
32+
}
33+
34+
if len(mfs) == 0 {
35+
t.Fatal("no metrics were gathered; registry appears to be empty")
36+
}
37+
38+
found := make(map[string]struct{})
39+
for _, mf := range mfs {
40+
name := mf.GetName()
41+
if strings.HasPrefix(name, "go_") ||
42+
strings.HasPrefix(name, "process_") ||
43+
strings.HasPrefix(name, "promhttp_") {
44+
continue
45+
}
46+
found[name] = struct{}{}
47+
}
48+
49+
expected := []string{
50+
// these require a BlockingCacheGroupChanged event
51+
"blocky_denylist_cache_entries",
52+
"blocky_allowlist_cache_entries",
53+
// these require a request
54+
"blocky_query_total",
55+
"blocky_request_duration_seconds",
56+
"blocky_response_total",
57+
// these should be default
58+
"blocky_error_total",
59+
"blocky_blocking_enabled",
60+
"blocky_cache_entries",
61+
"blocky_cache_hits_total",
62+
"blocky_cache_misses_total",
63+
"blocky_last_list_group_refresh_timestamp_seconds",
64+
"blocky_prefetches_total",
65+
"blocky_prefetch_hits_total",
66+
"blocky_prefetch_domain_name_cache_entries",
67+
"blocky_failed_downloads_total",
68+
}
69+
70+
if len(found) != len(expected) {
71+
t.Errorf("Found %d / %d expected metrics", len(found), len(expected))
72+
}
73+
74+
// helperto check if a string is in a slice
75+
contains := func(slice []string, item string) bool {
76+
for _, s := range slice {
77+
if s == item {
78+
return true
79+
}
80+
}
81+
82+
return false
83+
}
84+
85+
for name := range found {
86+
if !contains(expected, name) {
87+
t.Errorf("found additional metric %q in registry", name)
88+
}
89+
}
90+
91+
for _, name := range expected {
92+
if _, ok := found[name]; !ok {
93+
t.Errorf("expected metric %q not found in registry", name)
94+
}
95+
}
96+
}
97+
98+
type MockResolver struct{}
99+
100+
func (m *MockResolver) Resolve(ctx context.Context, request *model.Request) (*model.Response, error) {
101+
resp := &model.Response{
102+
Res: &dns.Msg{},
103+
Reason: "mocking",
104+
RType: 0,
105+
}
106+
107+
return resp, nil
108+
}
109+
110+
func (m *MockResolver) IsEnabled() bool {
111+
return true
112+
}
113+
114+
func (m *MockResolver) LogConfig(*logrus.Entry) {
115+
// no-op for testing
116+
}
117+
118+
func (m *MockResolver) String() string {
119+
return "mockResolver"
120+
}
121+
122+
func (m *MockResolver) Type() string {
123+
return "MockResolver"
124+
}
125+
126+
func TestAllExpectedMetricsAreRegistered(t *testing.T) {
127+
// New Server
128+
metrics.RegisterEventListeners()
129+
130+
config := config.Metrics{Enable: true, Path: "/metrics"}
131+
132+
// createQueryResolver
133+
metricsResolver := resolver.NewMetricsResolver(config)
134+
metricsResolver.Next(&MockResolver{})
135+
136+
// prepare request
137+
dnsMsg := new(dns.Msg)
138+
dnsMsg.SetQuestion("example.com.", dns.TypeA)
139+
140+
req := model.Request{
141+
ClientIP: net.ParseIP("192.168.0.1"),
142+
RequestClientID: "test-client",
143+
Protocol: model.RequestProtocolUDP,
144+
ClientNames: []string{"test-client"},
145+
Req: dnsMsg,
146+
RequestTS: time.Now().Add(-42 * time.Millisecond),
147+
}
148+
149+
ctx := context.Background()
150+
// now use the counters
151+
_, err := metricsResolver.Resolve(ctx, &req)
152+
if err != nil {
153+
t.Fatal("failed to call metrics resolver")
154+
}
155+
evt.Bus().Publish(evt.BlockingCacheGroupChanged, lists.ListCacheTypeDenylist, "group", 0)
156+
evt.Bus().Publish(evt.BlockingCacheGroupChanged, lists.ListCacheTypeAllowlist, "group", 0)
157+
158+
// createHTTPRouter
159+
metrics.Start(chi.NewMux(), config)
160+
161+
AssertRegistryComplete(t, metrics.Reg)
162+
}

0 commit comments

Comments
 (0)