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  //}