master
go 346 lines 9.38 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package apache
4
5 import (
6 "context"
7 "net/http"
8 "net/http/httptest"
9 "os"
10 "testing"
11
12 "github.com/netdata/netdata/go/plugins/pkg/web"
13 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/collecttest"
14
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17 )
18
19 var (
20 dataConfigJSON, _ = os.ReadFile("testdata/config.json")
21 dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
22
23 dataSimpleStatusMPMEvent, _ = os.ReadFile("testdata/simple-status-mpm-event.txt")
24 dataExtendedStatusMPMEvent, _ = os.ReadFile("testdata/extended-status-mpm-event.txt")
25 dataExtendedStatusMPMPrefork, _ = os.ReadFile("testdata/extended-status-mpm-prefork.txt")
26 dataLighttpdStatus, _ = os.ReadFile("testdata/lighttpd-status.txt")
27 )
28
29 func Test_testDataIsValid(t *testing.T) {
30 for name, data := range map[string][]byte{
31 "dataConfigJSON": dataConfigJSON,
32 "dataConfigYAML": dataConfigYAML,
33 "dataSimpleStatusMPMEvent": dataSimpleStatusMPMEvent,
34 "dataExtendedStatusMPMEvent": dataExtendedStatusMPMEvent,
35 "dataExtendedStatusMPMPrefork": dataExtendedStatusMPMPrefork,
36 "dataLighttpdStatus": dataLighttpdStatus,
37 } {
38 require.NotNil(t, data, name)
39
40 }
41 }
42
43 func TestCollector_ConfigurationSerialize(t *testing.T) {
44 collecttest.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
45 }
46
47 func TestCollector_Init(t *testing.T) {
48 tests := map[string]struct {
49 wantFail bool
50 config Config
51 }{
52 "success with default": {
53 wantFail: false,
54 config: New().Config,
55 },
56 "success when URL has no wantMetrics suffix": {
57 wantFail: false,
58 config: Config{
59 HTTPConfig: web.HTTPConfig{
60 RequestConfig: web.RequestConfig{URL: "http://127.0.0.1:38001"},
61 },
62 },
63 },
64 "fail when URL not set": {
65 wantFail: true,
66 config: Config{
67 HTTPConfig: web.HTTPConfig{
68 RequestConfig: web.RequestConfig{URL: ""},
69 },
70 },
71 },
72 }
73
74 for name, test := range tests {
75 t.Run(name, func(t *testing.T) {
76 collr := New()
77 collr.Config = test.config
78
79 if test.wantFail {
80 assert.Error(t, collr.Init(context.Background()))
81 } else {
82 assert.NoError(t, collr.Init(context.Background()))
83 }
84 })
85 }
86 }
87
88 func TestCollector_Check(t *testing.T) {
89 tests := map[string]struct {
90 wantFail bool
91 prepare func(t *testing.T) (collr *Collector, cleanup func())
92 }{
93 "success on simple status MPM Event": {
94 wantFail: false,
95 prepare: caseMPMEventSimpleStatus,
96 },
97 "success on extended status MPM Event": {
98 wantFail: false,
99 prepare: caseMPMEventExtendedStatus,
100 },
101 "success on extended status MPM Prefork": {
102 wantFail: false,
103 prepare: caseMPMPreforkExtendedStatus,
104 },
105 "fail on Lighttpd response": {
106 wantFail: true,
107 prepare: caseLighttpdResponse,
108 },
109 "fail on invalid data response": {
110 wantFail: true,
111 prepare: caseInvalidDataResponse,
112 },
113 "fail on connection refused": {
114 wantFail: true,
115 prepare: caseConnectionRefused,
116 },
117 "fail on 404 response": {
118 wantFail: true,
119 prepare: case404,
120 },
121 }
122
123 for name, test := range tests {
124 t.Run(name, func(t *testing.T) {
125 collr, cleanup := test.prepare(t)
126 defer cleanup()
127
128 if test.wantFail {
129 assert.Error(t, collr.Check(context.Background()))
130 } else {
131 assert.NoError(t, collr.Check(context.Background()))
132 }
133 })
134 }
135 }
136
137 func TestCollector_Charts(t *testing.T) {
138 assert.NotNil(t, New().Charts())
139 }
140
141 func TestCollector_Collect(t *testing.T) {
142 tests := map[string]struct {
143 prepare func(t *testing.T) (collr *Collector, cleanup func())
144 wantNumOfCharts int
145 wantMetrics map[string]int64
146 }{
147 "success on simple status MPM Event": {
148 prepare: caseMPMEventSimpleStatus,
149 wantNumOfCharts: len(baseCharts),
150 wantMetrics: map[string]int64{
151 "busy_workers": 1,
152 "conns_async_closing": 0,
153 "conns_async_keep_alive": 0,
154 "conns_async_writing": 0,
155 "conns_total": 0,
156 "idle_workers": 74,
157 "scoreboard_closing": 0,
158 "scoreboard_dns_lookup": 0,
159 "scoreboard_finishing": 0,
160 "scoreboard_idle_cleanup": 0,
161 "scoreboard_keepalive": 0,
162 "scoreboard_logging": 0,
163 "scoreboard_open": 325,
164 "scoreboard_reading": 0,
165 "scoreboard_sending": 1,
166 "scoreboard_starting": 0,
167 "scoreboard_waiting": 74,
168 },
169 },
170 "success on extended status MPM Event": {
171 prepare: caseMPMEventExtendedStatus,
172 wantNumOfCharts: len(baseCharts) + len(extendedCharts),
173 wantMetrics: map[string]int64{
174 "busy_workers": 1,
175 "bytes_per_req": 136533000,
176 "bytes_per_sec": 4800000,
177 "conns_async_closing": 0,
178 "conns_async_keep_alive": 0,
179 "conns_async_writing": 0,
180 "conns_total": 0,
181 "idle_workers": 99,
182 "req_per_sec": 3515,
183 "scoreboard_closing": 0,
184 "scoreboard_dns_lookup": 0,
185 "scoreboard_finishing": 0,
186 "scoreboard_idle_cleanup": 0,
187 "scoreboard_keepalive": 0,
188 "scoreboard_logging": 0,
189 "scoreboard_open": 300,
190 "scoreboard_reading": 0,
191 "scoreboard_sending": 1,
192 "scoreboard_starting": 0,
193 "scoreboard_waiting": 99,
194 "total_accesses": 9,
195 "total_kBytes": 12,
196 "uptime": 256,
197 },
198 },
199 "success on extended status MPM Prefork": {
200 prepare: caseMPMPreforkExtendedStatus,
201 wantNumOfCharts: len(baseCharts) + len(extendedCharts) - 2,
202 wantMetrics: map[string]int64{
203 "busy_workers": 70,
204 "bytes_per_req": 3617880000,
205 "bytes_per_sec": 614250000000,
206 "idle_workers": 1037,
207 "req_per_sec": 16978100,
208 "scoreboard_closing": 0,
209 "scoreboard_dns_lookup": 0,
210 "scoreboard_finishing": 0,
211 "scoreboard_idle_cleanup": 0,
212 "scoreboard_keepalive": 0,
213 "scoreboard_logging": 0,
214 "scoreboard_open": 3,
215 "scoreboard_reading": 0,
216 "scoreboard_sending": 0,
217 "scoreboard_starting": 0,
218 "scoreboard_waiting": 3,
219 "total_accesses": 120358784,
220 "total_kBytes": 4252382776,
221 "uptime": 708904,
222 },
223 },
224 "fail on Lighttpd response": {
225 prepare: caseLighttpdResponse,
226 wantNumOfCharts: 0,
227 wantMetrics: nil,
228 },
229 "fail on invalid data response": {
230 prepare: caseInvalidDataResponse,
231 wantNumOfCharts: 0,
232 wantMetrics: nil,
233 },
234 "fail on connection refused": {
235 prepare: caseConnectionRefused,
236 wantNumOfCharts: 0,
237 wantMetrics: nil,
238 },
239 "fail on 404 response": {
240 prepare: case404,
241 wantNumOfCharts: 0,
242 wantMetrics: nil,
243 },
244 }
245
246 for name, test := range tests {
247 t.Run(name, func(t *testing.T) {
248 collr, cleanup := test.prepare(t)
249 defer cleanup()
250
251 _ = collr.Check(context.Background())
252
253 collected := collr.Collect(context.Background())
254
255 require.Equal(t, test.wantMetrics, collected)
256 assert.Equal(t, test.wantNumOfCharts, len(*collr.Charts()))
257 })
258 }
259 }
260
261 func caseMPMEventSimpleStatus(t *testing.T) (*Collector, func()) {
262 t.Helper()
263 srv := httptest.NewServer(http.HandlerFunc(
264 func(w http.ResponseWriter, r *http.Request) {
265 _, _ = w.Write(dataSimpleStatusMPMEvent)
266 }))
267 collr := New()
268 collr.URL = srv.URL + "/server-status?auto"
269 require.NoError(t, collr.Init(context.Background()))
270
271 return collr, srv.Close
272 }
273
274 func caseMPMEventExtendedStatus(t *testing.T) (*Collector, func()) {
275 t.Helper()
276 srv := httptest.NewServer(http.HandlerFunc(
277 func(w http.ResponseWriter, r *http.Request) {
278 _, _ = w.Write(dataExtendedStatusMPMEvent)
279 }))
280 collr := New()
281 collr.URL = srv.URL + "/server-status?auto"
282 require.NoError(t, collr.Init(context.Background()))
283
284 return collr, srv.Close
285 }
286
287 func caseMPMPreforkExtendedStatus(t *testing.T) (*Collector, func()) {
288 t.Helper()
289 srv := httptest.NewServer(http.HandlerFunc(
290 func(w http.ResponseWriter, r *http.Request) {
291 _, _ = w.Write(dataExtendedStatusMPMPrefork)
292 }))
293 collr := New()
294 collr.URL = srv.URL + "/server-status?auto"
295 require.NoError(t, collr.Init(context.Background()))
296
297 return collr, srv.Close
298 }
299
300 func caseLighttpdResponse(t *testing.T) (*Collector, func()) {
301 t.Helper()
302 srv := httptest.NewServer(http.HandlerFunc(
303 func(w http.ResponseWriter, r *http.Request) {
304 _, _ = w.Write(dataLighttpdStatus)
305 }))
306 collr := New()
307 collr.URL = srv.URL + "/server-status?auto"
308 require.NoError(t, collr.Init(context.Background()))
309
310 return collr, srv.Close
311 }
312
313 func caseInvalidDataResponse(t *testing.T) (*Collector, func()) {
314 t.Helper()
315 srv := httptest.NewServer(http.HandlerFunc(
316 func(w http.ResponseWriter, r *http.Request) {
317 _, _ = w.Write([]byte("hello and\n goodbye"))
318 }))
319 collr := New()
320 collr.URL = srv.URL + "/server-status?auto"
321 require.NoError(t, collr.Init(context.Background()))
322
323 return collr, srv.Close
324 }
325
326 func caseConnectionRefused(t *testing.T) (*Collector, func()) {
327 t.Helper()
328 collr := New()
329 collr.URL = "http://127.0.0.1:65001/server-status?auto"
330 require.NoError(t, collr.Init(context.Background()))
331
332 return collr, func() {}
333 }
334
335 func case404(t *testing.T) (*Collector, func()) {
336 t.Helper()
337 srv := httptest.NewServer(http.HandlerFunc(
338 func(w http.ResponseWriter, r *http.Request) {
339 w.WriteHeader(http.StatusNotFound)
340 }))
341 collr := New()
342 collr.URL = srv.URL + "/server-status?auto"
343 require.NoError(t, collr.Init(context.Background()))
344
345 return collr, srv.Close
346 }