github.com/netdata/go.d.plugin@v0.58.1/modules/pihole/pihole_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package pihole 4 5 import ( 6 "fmt" 7 "net/http" 8 "net/http/httptest" 9 "os" 10 "testing" 11 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 const ( 19 pathSetupVarsOK = "testdata/setupVars.conf" 20 pathSetupVarsWrong = "testdata/wrong.conf" 21 ) 22 23 var ( 24 dataEmptyResp = []byte("[]") 25 dataSummaryRawResp, _ = os.ReadFile("testdata/summaryRaw.json") 26 dataGetQueryTypesResp, _ = os.ReadFile("testdata/getQueryTypes.json") 27 dataGetForwardDestinationsResp, _ = os.ReadFile("testdata/getForwardDestinations.json") 28 ) 29 30 func TestPihole_Init(t *testing.T) { 31 tests := map[string]struct { 32 wantFail bool 33 config Config 34 }{ 35 "success with default": { 36 wantFail: false, 37 config: New().Config, 38 }, 39 "fail when URL not set": { 40 wantFail: true, 41 config: Config{ 42 HTTP: web.HTTP{ 43 Request: web.Request{URL: ""}, 44 }, 45 }, 46 }, 47 } 48 49 for name, test := range tests { 50 t.Run(name, func(t *testing.T) { 51 p := New() 52 p.Config = test.config 53 54 if test.wantFail { 55 assert.False(t, p.Init()) 56 } else { 57 assert.True(t, p.Init()) 58 } 59 }) 60 } 61 } 62 63 func TestPihole_Check(t *testing.T) { 64 tests := map[string]struct { 65 wantFail bool 66 prepare func(t *testing.T) (p *Pihole, cleanup func()) 67 }{ 68 "success with web password": { 69 wantFail: false, 70 prepare: caseSuccessWithWebPassword, 71 }, 72 "fail without web password": { 73 wantFail: true, 74 prepare: caseFailNoWebPassword, 75 }, 76 "fail on unsupported version": { 77 wantFail: true, 78 prepare: caseFailUnsupportedVersion, 79 }, 80 } 81 82 for name, test := range tests { 83 t.Run(name, func(t *testing.T) { 84 p, cleanup := test.prepare(t) 85 defer cleanup() 86 87 if test.wantFail { 88 assert.False(t, p.Check()) 89 } else { 90 assert.True(t, p.Check()) 91 } 92 }) 93 } 94 } 95 96 func TestPihole_Charts(t *testing.T) { 97 assert.NotNil(t, New().Charts()) 98 } 99 100 func TestPihole_Collect(t *testing.T) { 101 tests := map[string]struct { 102 prepare func(t *testing.T) (p *Pihole, cleanup func()) 103 wantMetrics map[string]int64 104 wantNumCharts int 105 }{ 106 "success with web password": { 107 prepare: caseSuccessWithWebPassword, 108 wantNumCharts: len(baseCharts) + 2, 109 wantMetrics: map[string]int64{ 110 "A": 1229, 111 "AAAA": 1229, 112 "ANY": 100, 113 "PTR": 7143, 114 "SOA": 100, 115 "SRV": 100, 116 "TXT": 100, 117 "ads_blocked_today": 1, 118 "ads_blocked_today_perc": 33333, 119 "ads_percentage_today": 100, 120 "blocking_status_disabled": 0, 121 "blocking_status_enabled": 1, 122 "blocklist_last_update": 106273651, 123 "destination_blocked": 220, 124 "destination_cached": 8840, 125 "destination_other": 940, 126 "dns_queries_today": 1, 127 "domains_being_blocked": 1, 128 "queries_cached": 1, 129 "queries_cached_perc": 33333, 130 "queries_forwarded": 1, 131 "queries_forwarded_perc": 33333, 132 "unique_clients": 1, 133 }, 134 }, 135 "fail without web password": { 136 prepare: caseFailNoWebPassword, 137 wantMetrics: nil, 138 }, 139 "fail on unsupported version": { 140 prepare: caseFailUnsupportedVersion, 141 wantMetrics: nil, 142 }, 143 } 144 145 for name, test := range tests { 146 t.Run(name, func(t *testing.T) { 147 p, cleanup := test.prepare(t) 148 defer cleanup() 149 150 mx := p.Collect() 151 152 copyBlockListLastUpdate(mx, test.wantMetrics) 153 require.Equal(t, test.wantMetrics, mx) 154 if len(test.wantMetrics) > 0 { 155 assert.Len(t, *p.Charts(), test.wantNumCharts) 156 } 157 }) 158 } 159 } 160 161 func caseSuccessWithWebPassword(t *testing.T) (*Pihole, func()) { 162 p, srv := New(), mockPiholeServer{}.newPiholeHTTPServer() 163 164 p.SetupVarsPath = pathSetupVarsOK 165 p.URL = srv.URL 166 167 require.True(t, p.Init()) 168 169 return p, srv.Close 170 } 171 172 func caseFailNoWebPassword(t *testing.T) (*Pihole, func()) { 173 p, srv := New(), mockPiholeServer{}.newPiholeHTTPServer() 174 175 p.SetupVarsPath = pathSetupVarsWrong 176 p.URL = srv.URL 177 178 require.True(t, p.Init()) 179 180 return p, srv.Close 181 } 182 183 func caseFailUnsupportedVersion(t *testing.T) (*Pihole, func()) { 184 p, srv := New(), mockPiholeServer{unsupportedVersion: true}.newPiholeHTTPServer() 185 186 p.SetupVarsPath = pathSetupVarsOK 187 p.URL = srv.URL 188 189 require.True(t, p.Init()) 190 191 return p, srv.Close 192 } 193 194 type mockPiholeServer struct { 195 unsupportedVersion bool 196 errOnAPIVersion bool 197 errOnSummary bool 198 errOnQueryTypes bool 199 errOnGetForwardDst bool 200 errOnTopClients bool 201 errOnTopItems bool 202 } 203 204 func (m mockPiholeServer) newPiholeHTTPServer() *httptest.Server { 205 return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 206 if r.URL.Path != urlPathAPI || len(r.URL.Query()) == 0 { 207 w.WriteHeader(http.StatusBadRequest) 208 } 209 210 if r.URL.Query().Get(urlQueryKeyAuth) == "" { 211 _, _ = w.Write(dataEmptyResp) 212 return 213 } 214 215 if r.URL.Query().Has(urlQueryKeyAPIVersion) { 216 if m.errOnAPIVersion { 217 w.WriteHeader(http.StatusNotFound) 218 } else if m.unsupportedVersion { 219 _, _ = w.Write([]byte(fmt.Sprintf(`{"version": %d}`, wantAPIVersion+1))) 220 } else { 221 _, _ = w.Write([]byte(fmt.Sprintf(`{"version": %d}`, wantAPIVersion))) 222 } 223 return 224 } 225 226 if r.URL.Query().Has(urlQueryKeySummaryRaw) { 227 if m.errOnSummary { 228 w.WriteHeader(http.StatusNotFound) 229 } else { 230 _, _ = w.Write(dataSummaryRawResp) 231 } 232 return 233 } 234 235 data := dataEmptyResp 236 isErr := false 237 switch { 238 case r.URL.Query().Has(urlQueryKeyGetQueryTypes): 239 data, isErr = dataGetQueryTypesResp, m.errOnQueryTypes 240 case r.URL.Query().Has(urlQueryKeyGetForwardDestinations): 241 data, isErr = dataGetForwardDestinationsResp, m.errOnGetForwardDst 242 } 243 244 if isErr { 245 w.WriteHeader(http.StatusNotFound) 246 } else { 247 _, _ = w.Write(data) 248 } 249 })) 250 } 251 252 func copyBlockListLastUpdate(dst, src map[string]int64) { 253 k := "blocklist_last_update" 254 if v, ok := src[k]; ok { 255 if _, ok := dst[k]; ok { 256 dst[k] = v 257 } 258 } 259 }