github.com/netdata/go.d.plugin@v0.58.1/modules/squidlog/squidlog_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package squidlog 4 5 import ( 6 "bytes" 7 "os" 8 "testing" 9 10 "github.com/netdata/go.d.plugin/pkg/logs" 11 "github.com/netdata/go.d.plugin/pkg/metrics" 12 13 "github.com/netdata/go.d.plugin/agent/module" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 var ( 19 nativeFormatAccessLog, _ = os.ReadFile("testdata/access.log") 20 ) 21 22 func Test_readTestData(t *testing.T) { 23 assert.NotNil(t, nativeFormatAccessLog) 24 } 25 26 func TestNew(t *testing.T) { 27 assert.Implements(t, (*module.Module)(nil), New()) 28 } 29 30 func TestSquidLog_Init(t *testing.T) { 31 squidlog := New() 32 33 assert.True(t, squidlog.Init()) 34 } 35 36 func TestSquidLog_Check(t *testing.T) { 37 } 38 39 func TestSquidLog_Check_ErrorOnCreatingLogReaderNoLogFile(t *testing.T) { 40 squid := New() 41 defer squid.Cleanup() 42 squid.Path = "testdata/not_exists.log" 43 require.True(t, squid.Init()) 44 45 assert.False(t, squid.Check()) 46 } 47 48 func TestSquid_Check_ErrorOnCreatingParserUnknownFormat(t *testing.T) { 49 squid := New() 50 defer squid.Cleanup() 51 squid.Path = "testdata/unknown.log" 52 require.True(t, squid.Init()) 53 54 assert.False(t, squid.Check()) 55 } 56 57 func TestSquid_Check_ErrorOnCreatingParserZeroKnownFields(t *testing.T) { 58 squid := New() 59 defer squid.Cleanup() 60 squid.Path = "testdata/access.log" 61 squid.Parser.CSV.Format = "$one $two" 62 require.True(t, squid.Init()) 63 64 assert.False(t, squid.Check()) 65 } 66 67 func TestSquidLog_Charts(t *testing.T) { 68 assert.Nil(t, New().Charts()) 69 70 squid := prepareSquidCollect(t) 71 assert.NotNil(t, squid.Charts()) 72 73 } 74 75 func TestSquidLog_Cleanup(t *testing.T) { 76 New().Cleanup() 77 } 78 79 func TestSquidLog_Collect(t *testing.T) { 80 squid := prepareSquidCollect(t) 81 82 expected := map[string]int64{ 83 "bytes_sent": 6827357, 84 "cache_error_tag_ABORTED": 326, 85 "cache_handling_tag_CF": 154, 86 "cache_handling_tag_CLIENT": 172, 87 "cache_load_source_tag_MEM": 172, 88 "cache_object_tag_NEGATIVE": 308, 89 "cache_object_tag_STALE": 172, 90 "cache_result_code_NONE": 158, 91 "cache_result_code_TCP_CF_NEGATIVE_NEGATIVE_ABORTED": 154, 92 "cache_result_code_UDP_CLIENT_STALE_MEM_ABORTED": 172, 93 "cache_transport_tag_NONE": 158, 94 "cache_transport_tag_TCP": 154, 95 "cache_transport_tag_UDP": 172, 96 "hier_code_HIER_CACHE_DIGEST_HIT": 128, 97 "hier_code_HIER_NO_CACHE_DIGEST_DIRECT": 130, 98 "hier_code_HIER_PARENT_HIT": 106, 99 "hier_code_HIER_SINGLE_PARENT": 120, 100 "http_resp_0xx": 51, 101 "http_resp_1xx": 45, 102 "http_resp_2xx": 62, 103 "http_resp_3xx": 120, 104 "http_resp_4xx": 112, 105 "http_resp_5xx": 46, 106 "http_resp_6xx": 48, 107 "http_resp_code_0": 51, 108 "http_resp_code_100": 45, 109 "http_resp_code_200": 62, 110 "http_resp_code_300": 58, 111 "http_resp_code_304": 62, 112 "http_resp_code_400": 56, 113 "http_resp_code_401": 56, 114 "http_resp_code_500": 46, 115 "http_resp_code_603": 48, 116 "mime_type_application": 52, 117 "mime_type_audio": 56, 118 "mime_type_font": 44, 119 "mime_type_image": 50, 120 "mime_type_message": 44, 121 "mime_type_model": 62, 122 "mime_type_multipart": 61, 123 "mime_type_text": 61, 124 "mime_type_video": 54, 125 "req_method_COPY": 84, 126 "req_method_GET": 70, 127 "req_method_HEAD": 59, 128 "req_method_OPTIONS": 99, 129 "req_method_POST": 74, 130 "req_method_PURGE": 98, 131 "req_type_bad": 56, 132 "req_type_error": 94, 133 "req_type_redirect": 58, 134 "req_type_success": 276, 135 "requests": 500, 136 "resp_time_avg": 3015931, 137 "resp_time_count": 484, 138 "resp_time_max": 4988000, 139 "resp_time_min": 1002000, 140 "resp_time_sum": 1459711000, 141 "server_address_2001:db8:2ce:a": 79, 142 "server_address_2001:db8:2ce:b": 89, 143 "server_address_203.0.113.100": 67, 144 "server_address_203.0.113.200": 70, 145 "server_address_content-gateway": 87, 146 "uniq_clients": 5, 147 "unmatched": 16, 148 } 149 150 collected := squid.Collect() 151 152 assert.Equal(t, expected, collected) 153 testCharts(t, squid, collected) 154 } 155 156 func TestSquidLog_Collect_ReturnOldDataIfNothingRead(t *testing.T) { 157 squid := prepareSquidCollect(t) 158 159 expected := map[string]int64{ 160 "bytes_sent": 6827357, 161 "cache_error_tag_ABORTED": 326, 162 "cache_handling_tag_CF": 154, 163 "cache_handling_tag_CLIENT": 172, 164 "cache_load_source_tag_MEM": 172, 165 "cache_object_tag_NEGATIVE": 308, 166 "cache_object_tag_STALE": 172, 167 "cache_result_code_NONE": 158, 168 "cache_result_code_TCP_CF_NEGATIVE_NEGATIVE_ABORTED": 154, 169 "cache_result_code_UDP_CLIENT_STALE_MEM_ABORTED": 172, 170 "cache_transport_tag_NONE": 158, 171 "cache_transport_tag_TCP": 154, 172 "cache_transport_tag_UDP": 172, 173 "hier_code_HIER_CACHE_DIGEST_HIT": 128, 174 "hier_code_HIER_NO_CACHE_DIGEST_DIRECT": 130, 175 "hier_code_HIER_PARENT_HIT": 106, 176 "hier_code_HIER_SINGLE_PARENT": 120, 177 "http_resp_0xx": 51, 178 "http_resp_1xx": 45, 179 "http_resp_2xx": 62, 180 "http_resp_3xx": 120, 181 "http_resp_4xx": 112, 182 "http_resp_5xx": 46, 183 "http_resp_6xx": 48, 184 "http_resp_code_0": 51, 185 "http_resp_code_100": 45, 186 "http_resp_code_200": 62, 187 "http_resp_code_300": 58, 188 "http_resp_code_304": 62, 189 "http_resp_code_400": 56, 190 "http_resp_code_401": 56, 191 "http_resp_code_500": 46, 192 "http_resp_code_603": 48, 193 "mime_type_application": 52, 194 "mime_type_audio": 56, 195 "mime_type_font": 44, 196 "mime_type_image": 50, 197 "mime_type_message": 44, 198 "mime_type_model": 62, 199 "mime_type_multipart": 61, 200 "mime_type_text": 61, 201 "mime_type_video": 54, 202 "req_method_COPY": 84, 203 "req_method_GET": 70, 204 "req_method_HEAD": 59, 205 "req_method_OPTIONS": 99, 206 "req_method_POST": 74, 207 "req_method_PURGE": 98, 208 "req_type_bad": 56, 209 "req_type_error": 94, 210 "req_type_redirect": 58, 211 "req_type_success": 276, 212 "requests": 500, 213 "resp_time_avg": 0, 214 "resp_time_count": 0, 215 "resp_time_max": 0, 216 "resp_time_min": 0, 217 "resp_time_sum": 0, 218 "server_address_2001:db8:2ce:a": 79, 219 "server_address_2001:db8:2ce:b": 89, 220 "server_address_203.0.113.100": 67, 221 "server_address_203.0.113.200": 70, 222 "server_address_content-gateway": 87, 223 "uniq_clients": 0, 224 "unmatched": 16, 225 } 226 227 _ = squid.Collect() 228 collected := squid.Collect() 229 230 assert.Equal(t, expected, collected) 231 testCharts(t, squid, collected) 232 } 233 234 func testCharts(t *testing.T, squidlog *SquidLog, collected map[string]int64) { 235 t.Helper() 236 ensureChartsDynamicDimsCreated(t, squidlog) 237 ensureCollectedHasAllChartsDimsVarsIDs(t, squidlog, collected) 238 } 239 240 func ensureChartsDynamicDimsCreated(t *testing.T, squid *SquidLog) { 241 ensureDynamicDimsCreated(t, squid, cacheCodeChart.ID, pxCacheCode, squid.mx.CacheCode) 242 ensureDynamicDimsCreated(t, squid, cacheCodeTransportTagChart.ID, pxTransportTag, squid.mx.CacheCodeTransportTag) 243 ensureDynamicDimsCreated(t, squid, cacheCodeHandlingTagChart.ID, pxHandlingTag, squid.mx.CacheCodeHandlingTag) 244 ensureDynamicDimsCreated(t, squid, cacheCodeObjectTagChart.ID, pxObjectTag, squid.mx.CacheCodeObjectTag) 245 ensureDynamicDimsCreated(t, squid, cacheCodeLoadSourceTagChart.ID, pxSourceTag, squid.mx.CacheCodeLoadSourceTag) 246 ensureDynamicDimsCreated(t, squid, cacheCodeErrorTagChart.ID, pxErrorTag, squid.mx.CacheCodeErrorTag) 247 ensureDynamicDimsCreated(t, squid, httpRespCodesChart.ID, pxHTTPCode, squid.mx.HTTPRespCode) 248 ensureDynamicDimsCreated(t, squid, reqMethodChart.ID, pxReqMethod, squid.mx.ReqMethod) 249 ensureDynamicDimsCreated(t, squid, hierCodeChart.ID, pxHierCode, squid.mx.HierCode) 250 ensureDynamicDimsCreated(t, squid, serverAddrChart.ID, pxSrvAddr, squid.mx.Server) 251 ensureDynamicDimsCreated(t, squid, mimeTypeChart.ID, pxMimeType, squid.mx.MimeType) 252 } 253 254 func ensureDynamicDimsCreated(t *testing.T, squid *SquidLog, chartID, dimPrefix string, data metrics.CounterVec) { 255 chart := squid.Charts().Get(chartID) 256 assert.NotNilf(t, chart, "chart '%s' is not created", chartID) 257 if chart == nil { 258 return 259 } 260 for v := range data { 261 id := dimPrefix + v 262 assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s', expected '%s'", chart.ID, v, id) 263 } 264 } 265 266 func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, s *SquidLog, collected map[string]int64) { 267 for _, chart := range *s.Charts() { 268 for _, dim := range chart.Dims { 269 _, ok := collected[dim.ID] 270 assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID) 271 } 272 for _, v := range chart.Vars { 273 _, ok := collected[v.ID] 274 assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID) 275 } 276 } 277 } 278 279 func prepareSquidCollect(t *testing.T) *SquidLog { 280 t.Helper() 281 squid := New() 282 squid.Path = "testdata/access.log" 283 require.True(t, squid.Init()) 284 require.True(t, squid.Check()) 285 defer squid.Cleanup() 286 287 p, err := logs.NewCSVParser(squid.Parser.CSV, bytes.NewReader(nativeFormatAccessLog)) 288 require.NoError(t, err) 289 squid.parser = p 290 return squid 291 } 292 293 // generateLogs is used to populate 'testdata/access.log' 294 //func generateLogs(w io.Writer, num int) error { 295 // var ( 296 // client = []string{"localhost", "203.0.113.1", "203.0.113.2", "2001:db8:2ce:1", "2001:db8:2ce:2"} 297 // cacheCode = []string{"TCP_CF_NEGATIVE_NEGATIVE_ABORTED", "UDP_CLIENT_STALE_MEM_ABORTED", "NONE"} 298 // httpCode = []string{"000", "100", "200", "300", "304", "400", "401", "500", "603"} 299 // method = []string{"GET", "HEAD", "POST", "COPY", "PURGE", "OPTIONS"} 300 // hierCode = []string{"HIER_PARENT_HIT", "HIER_SINGLE_PARENT", "HIER_CACHE_DIGEST_HIT", "HIER_NO_CACHE_DIGEST_DIRECT"} 301 // server = []string{"content-gateway", "203.0.113.100", "203.0.113.200", "2001:db8:2ce:a", "2001:db8:2ce:b", "-"} 302 // mimeType = []string{"application", "audio", "font", "image", "message", "model", "multipart", "video", "text"} 303 // ) 304 // 305 // r := rand.New(rand.NewSource(time.Now().UnixNano())) 306 // randFromString := func(s []string) string { return s[r.Intn(len(s))] } 307 // randInt := func(min, max int) int { return r.Intn(max-min) + min } 308 // 309 // var line string 310 // for i := 0; i < num; i++ { 311 // unmatched := randInt(1, 100) > 95 312 // if i > 0 && unmatched { 313 // line = "Unmatched! The rat the cat the dog chased killed ate the malt!\n" 314 // } else { 315 // // 1576177221.686 0 ::1 TCP_MISS/200 1621 GET cache_object://localhost/counters - HIER_NONE/- text/plain 316 // line = fmt.Sprintf( 317 // "1576177221.686 %d %s %s/%s %d %s cache_object://localhost/counters - %s/%s %s/plain\n", 318 // randInt(1000, 5000), 319 // randFromString(client), 320 // randFromString(cacheCode), 321 // randFromString(httpCode), 322 // randInt(9000, 19000), 323 // randFromString(method), 324 // randFromString(hierCode), 325 // randFromString(server), 326 // randFromString(mimeType), 327 // ) 328 // } 329 // _, err := fmt.Fprint(w, line) 330 // if err != nil { 331 // return err 332 // } 333 // } 334 // return nil 335 //}