github.com/netdata/go.d.plugin@v0.58.1/modules/powerdns_recursor/recursor_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package powerdns_recursor 4 5 import ( 6 "net/http" 7 "net/http/httptest" 8 "os" 9 "testing" 10 11 "github.com/netdata/go.d.plugin/pkg/tlscfg" 12 "github.com/netdata/go.d.plugin/pkg/web" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 var ( 19 v431statistics, _ = os.ReadFile("testdata/v4.3.1/statistics.json") 20 authoritativeStatistics, _ = os.ReadFile("testdata/authoritative/statistics.json") 21 ) 22 23 func Test_testDataIsCorrectlyReadAndValid(t *testing.T) { 24 for name, data := range map[string][]byte{ 25 "v431statistics": v431statistics, 26 "authoritativeStatistics": authoritativeStatistics, 27 } { 28 require.NotNilf(t, data, name) 29 } 30 } 31 32 func TestNew(t *testing.T) { 33 assert.IsType(t, (*Recursor)(nil), New()) 34 } 35 36 func TestRecursor_Init(t *testing.T) { 37 tests := map[string]struct { 38 config Config 39 wantFail bool 40 }{ 41 "success on default config": { 42 config: New().Config, 43 }, 44 "fails on unset URL": { 45 wantFail: true, 46 config: Config{ 47 HTTP: web.HTTP{ 48 Request: web.Request{URL: ""}, 49 }, 50 }, 51 }, 52 "fails on invalid TLSCA": { 53 wantFail: true, 54 config: Config{ 55 HTTP: web.HTTP{ 56 Request: web.Request{ 57 URL: "http://127.0.0.1:38001", 58 }, 59 Client: web.Client{ 60 TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"}, 61 }, 62 }, 63 }, 64 }, 65 } 66 67 for name, test := range tests { 68 t.Run(name, func(t *testing.T) { 69 recursor := New() 70 recursor.Config = test.config 71 72 if test.wantFail { 73 assert.False(t, recursor.Init()) 74 } else { 75 assert.True(t, recursor.Init()) 76 } 77 }) 78 } 79 } 80 81 func TestRecursor_Check(t *testing.T) { 82 tests := map[string]struct { 83 prepare func() (r *Recursor, cleanup func()) 84 wantFail bool 85 }{ 86 "success on valid response v4.3.1": { 87 prepare: preparePowerDNSRecursorV431, 88 }, 89 "fails on response from PowerDNS Authoritative Server": { 90 wantFail: true, 91 prepare: preparePowerDNSRecursorAuthoritativeData, 92 }, 93 "fails on 404 response": { 94 wantFail: true, 95 prepare: preparePowerDNSRecursor404, 96 }, 97 "fails on connection refused": { 98 wantFail: true, 99 prepare: preparePowerDNSRecursorConnectionRefused, 100 }, 101 "fails on response with invalid data": { 102 wantFail: true, 103 prepare: preparePowerDNSRecursorInvalidData, 104 }, 105 } 106 107 for name, test := range tests { 108 t.Run(name, func(t *testing.T) { 109 recursor, cleanup := test.prepare() 110 defer cleanup() 111 require.True(t, recursor.Init()) 112 113 if test.wantFail { 114 assert.False(t, recursor.Check()) 115 } else { 116 assert.True(t, recursor.Check()) 117 } 118 }) 119 } 120 } 121 122 func TestRecursor_Charts(t *testing.T) { 123 recursor := New() 124 require.True(t, recursor.Init()) 125 assert.NotNil(t, recursor.Charts()) 126 } 127 128 func TestRecursor_Cleanup(t *testing.T) { 129 assert.NotPanics(t, New().Cleanup) 130 } 131 132 func TestRecursor_Collect(t *testing.T) { 133 tests := map[string]struct { 134 prepare func() (r *Recursor, cleanup func()) 135 wantCollected map[string]int64 136 }{ 137 "success on valid response v4.3.1": { 138 prepare: preparePowerDNSRecursorV431, 139 wantCollected: map[string]int64{ 140 "all-outqueries": 41, 141 "answers-slow": 1, 142 "answers0-1": 1, 143 "answers1-10": 1, 144 "answers10-100": 1, 145 "answers100-1000": 1, 146 "auth-zone-queries": 1, 147 "auth4-answers-slow": 1, 148 "auth4-answers0-1": 1, 149 "auth4-answers1-10": 5, 150 "auth4-answers10-100": 35, 151 "auth4-answers100-1000": 1, 152 "auth6-answers-slow": 1, 153 "auth6-answers0-1": 1, 154 "auth6-answers1-10": 1, 155 "auth6-answers10-100": 1, 156 "auth6-answers100-1000": 1, 157 "cache-entries": 171, 158 "cache-hits": 1, 159 "cache-misses": 1, 160 "case-mismatches": 1, 161 "chain-resends": 1, 162 "client-parse-errors": 1, 163 "concurrent-queries": 1, 164 "cpu-msec-thread-0": 439, 165 "cpu-msec-thread-1": 445, 166 "cpu-msec-thread-2": 466, 167 "dlg-only-drops": 1, 168 "dnssec-authentic-data-queries": 1, 169 "dnssec-check-disabled-queries": 1, 170 "dnssec-queries": 1, 171 "dnssec-result-bogus": 1, 172 "dnssec-result-indeterminate": 1, 173 "dnssec-result-insecure": 1, 174 "dnssec-result-nta": 1, 175 "dnssec-result-secure": 5, 176 "dnssec-validations": 5, 177 "dont-outqueries": 1, 178 "ecs-queries": 1, 179 "ecs-responses": 1, 180 "edns-ping-matches": 1, 181 "edns-ping-mismatches": 1, 182 "empty-queries": 1, 183 "failed-host-entries": 1, 184 "fd-usage": 32, 185 "ignored-packets": 1, 186 "ipv6-outqueries": 1, 187 "ipv6-questions": 1, 188 "malloc-bytes": 1, 189 "max-cache-entries": 1000000, 190 "max-mthread-stack": 1, 191 "max-packetcache-entries": 500000, 192 "negcache-entries": 1, 193 "no-packet-error": 1, 194 "noedns-outqueries": 1, 195 "noerror-answers": 1, 196 "noping-outqueries": 1, 197 "nsset-invalidations": 1, 198 "nsspeeds-entries": 78, 199 "nxdomain-answers": 1, 200 "outgoing-timeouts": 1, 201 "outgoing4-timeouts": 1, 202 "outgoing6-timeouts": 1, 203 "over-capacity-drops": 1, 204 "packetcache-entries": 1, 205 "packetcache-hits": 1, 206 "packetcache-misses": 1, 207 "policy-drops": 1, 208 "policy-result-custom": 1, 209 "policy-result-drop": 1, 210 "policy-result-noaction": 1, 211 "policy-result-nodata": 1, 212 "policy-result-nxdomain": 1, 213 "policy-result-truncate": 1, 214 "qa-latency": 1, 215 "qname-min-fallback-success": 1, 216 "query-pipe-full-drops": 1, 217 "questions": 1, 218 "real-memory-usage": 44773376, 219 "rebalanced-queries": 1, 220 "resource-limits": 1, 221 "security-status": 3, 222 "server-parse-errors": 1, 223 "servfail-answers": 1, 224 "spoof-prevents": 1, 225 "sys-msec": 1520, 226 "tcp-client-overflow": 1, 227 "tcp-clients": 1, 228 "tcp-outqueries": 1, 229 "tcp-questions": 1, 230 "throttle-entries": 1, 231 "throttled-out": 1, 232 "throttled-outqueries": 1, 233 "too-old-drops": 1, 234 "truncated-drops": 1, 235 "udp-in-errors": 1, 236 "udp-noport-errors": 1, 237 "udp-recvbuf-errors": 1, 238 "udp-sndbuf-errors": 1, 239 "unauthorized-tcp": 1, 240 "unauthorized-udp": 1, 241 "unexpected-packets": 1, 242 "unreachables": 1, 243 "uptime": 1624, 244 "user-msec": 465, 245 "variable-responses": 1, 246 "x-our-latency": 1, 247 "x-ourtime-slow": 1, 248 "x-ourtime0-1": 1, 249 "x-ourtime1-2": 1, 250 "x-ourtime16-32": 1, 251 "x-ourtime2-4": 1, 252 "x-ourtime4-8": 1, 253 "x-ourtime8-16": 1, 254 }, 255 }, 256 "fails on response from PowerDNS Authoritative Server": { 257 prepare: preparePowerDNSRecursorAuthoritativeData, 258 }, 259 "fails on 404 response": { 260 prepare: preparePowerDNSRecursor404, 261 }, 262 "fails on connection refused": { 263 prepare: preparePowerDNSRecursorConnectionRefused, 264 }, 265 "fails on response with invalid data": { 266 prepare: preparePowerDNSRecursorInvalidData, 267 }, 268 } 269 270 for name, test := range tests { 271 t.Run(name, func(t *testing.T) { 272 recursor, cleanup := test.prepare() 273 defer cleanup() 274 require.True(t, recursor.Init()) 275 276 collected := recursor.Collect() 277 278 assert.Equal(t, test.wantCollected, collected) 279 if len(test.wantCollected) > 0 { 280 ensureCollectedHasAllChartsDimsVarsIDs(t, recursor, collected) 281 } 282 }) 283 } 284 } 285 286 func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, rec *Recursor, collected map[string]int64) { 287 for _, chart := range *rec.Charts() { 288 if chart.Obsolete { 289 continue 290 } 291 for _, dim := range chart.Dims { 292 _, ok := collected[dim.ID] 293 assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID) 294 } 295 for _, v := range chart.Vars { 296 _, ok := collected[v.ID] 297 assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID) 298 } 299 } 300 } 301 302 func preparePowerDNSRecursorV431() (*Recursor, func()) { 303 srv := preparePowerDNSRecursorEndpoint() 304 recursor := New() 305 recursor.URL = srv.URL 306 307 return recursor, srv.Close 308 } 309 310 func preparePowerDNSRecursorAuthoritativeData() (*Recursor, func()) { 311 srv := preparePowerDNSAuthoritativeEndpoint() 312 recursor := New() 313 recursor.URL = srv.URL 314 315 return recursor, srv.Close 316 } 317 318 func preparePowerDNSRecursorInvalidData() (*Recursor, func()) { 319 srv := httptest.NewServer(http.HandlerFunc( 320 func(w http.ResponseWriter, r *http.Request) { 321 _, _ = w.Write([]byte("hello and\n goodbye")) 322 })) 323 recursor := New() 324 recursor.URL = srv.URL 325 326 return recursor, srv.Close 327 } 328 329 func preparePowerDNSRecursor404() (*Recursor, func()) { 330 srv := httptest.NewServer(http.HandlerFunc( 331 func(w http.ResponseWriter, r *http.Request) { 332 w.WriteHeader(http.StatusNotFound) 333 })) 334 recursor := New() 335 recursor.URL = srv.URL 336 337 return recursor, srv.Close 338 } 339 340 func preparePowerDNSRecursorConnectionRefused() (*Recursor, func()) { 341 recursor := New() 342 recursor.URL = "http://127.0.0.1:38001" 343 344 return recursor, func() {} 345 } 346 347 func preparePowerDNSRecursorEndpoint() *httptest.Server { 348 return httptest.NewServer(http.HandlerFunc( 349 func(w http.ResponseWriter, r *http.Request) { 350 switch r.URL.Path { 351 case urlPathLocalStatistics: 352 _, _ = w.Write(v431statistics) 353 default: 354 w.WriteHeader(http.StatusNotFound) 355 } 356 })) 357 } 358 359 func preparePowerDNSAuthoritativeEndpoint() *httptest.Server { 360 return httptest.NewServer(http.HandlerFunc( 361 func(w http.ResponseWriter, r *http.Request) { 362 switch r.URL.Path { 363 case urlPathLocalStatistics: 364 _, _ = w.Write(authoritativeStatistics) 365 default: 366 w.WriteHeader(http.StatusNotFound) 367 } 368 })) 369 }