github.com/hashicorp/go-metrics@v0.5.3/inmem_test.go (about)

     1  package metrics
     2  
     3  import (
     4  	"math"
     5  	"net/url"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  )
    10  
    11  func TestInmemSink(t *testing.T) {
    12  	inm := NewInmemSink(10*time.Millisecond, 50*time.Millisecond)
    13  
    14  	data := inm.Data()
    15  	if len(data) != 1 {
    16  		t.Fatalf("bad: %v", data)
    17  	}
    18  
    19  	// Add data points
    20  	inm.SetGauge([]string{"foo", "bar"}, 42)
    21  	inm.SetGaugeWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
    22  	inm.EmitKey([]string{"foo", "bar"}, 42)
    23  	inm.IncrCounter([]string{"foo", "bar"}, 20)
    24  	inm.IncrCounter([]string{"foo", "bar"}, 22)
    25  	inm.IncrCounterWithLabels([]string{"foo", "bar"}, 20, []Label{{"a", "b"}})
    26  	inm.IncrCounterWithLabels([]string{"foo", "bar"}, 22, []Label{{"a", "b"}})
    27  	inm.AddSample([]string{"foo", "bar"}, 20)
    28  	inm.AddSample([]string{"foo", "bar"}, 22)
    29  	inm.AddSampleWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
    30  
    31  	data = inm.Data()
    32  	if len(data) != 1 {
    33  		t.Fatalf("bad: %v", data)
    34  	}
    35  
    36  	intvM := data[0]
    37  	intvM.RLock()
    38  
    39  	if time.Now().Sub(intvM.Interval) > 10*time.Millisecond {
    40  		t.Fatalf("interval too old")
    41  	}
    42  	if intvM.Gauges["foo.bar"].Value != 42 {
    43  		t.Fatalf("bad val: %v", intvM.Gauges)
    44  	}
    45  	if intvM.Gauges["foo.bar;a=b"].Value != 23 {
    46  		t.Fatalf("bad val: %v", intvM.Gauges)
    47  	}
    48  	if intvM.Points["foo.bar"][0] != 42 {
    49  		t.Fatalf("bad val: %v", intvM.Points)
    50  	}
    51  
    52  	for _, agg := range []SampledValue{intvM.Counters["foo.bar"], intvM.Counters["foo.bar;a=b"]} {
    53  		if agg.Count != 2 {
    54  			t.Fatalf("bad val: %v", agg)
    55  		}
    56  		if agg.Rate != 4200 {
    57  			t.Fatalf("bad val: %v", agg.Rate)
    58  		}
    59  		if agg.Sum != 42 {
    60  			t.Fatalf("bad val: %v", agg)
    61  		}
    62  		if agg.SumSq != 884 {
    63  			t.Fatalf("bad val: %v", agg)
    64  		}
    65  		if agg.Min != 20 {
    66  			t.Fatalf("bad val: %v", agg)
    67  		}
    68  		if agg.Max != 22 {
    69  			t.Fatalf("bad val: %v", agg)
    70  		}
    71  		if agg.AggregateSample.Mean() != 21 {
    72  			t.Fatalf("bad val: %v", agg)
    73  		}
    74  		if agg.AggregateSample.Stddev() != math.Sqrt(2) {
    75  			t.Fatalf("bad val: %v", agg)
    76  		}
    77  
    78  		if agg.LastUpdated.IsZero() {
    79  			t.Fatalf("agg.LastUpdated is not set: %v", agg)
    80  		}
    81  
    82  		diff := time.Now().Sub(agg.LastUpdated).Seconds()
    83  		if diff > 1 {
    84  			t.Fatalf("time diff too great: %f", diff)
    85  		}
    86  	}
    87  
    88  	if _, ok := intvM.Samples["foo.bar"]; !ok {
    89  		t.Fatalf("missing sample")
    90  	}
    91  
    92  	if _, ok := intvM.Samples["foo.bar;a=b"]; !ok {
    93  		t.Fatalf("missing sample")
    94  	}
    95  
    96  	intvM.RUnlock()
    97  
    98  	for i := 1; i < 10; i++ {
    99  		time.Sleep(10 * time.Millisecond)
   100  		inm.SetGauge([]string{"foo", "bar"}, 42)
   101  		data = inm.Data()
   102  		if len(data) != min(i+1, 5) {
   103  			t.Fatalf("bad: %v", data)
   104  		}
   105  	}
   106  
   107  	// Should not exceed 5 intervals!
   108  	time.Sleep(10 * time.Millisecond)
   109  	inm.SetGauge([]string{"foo", "bar"}, 42)
   110  	data = inm.Data()
   111  	if len(data) != 5 {
   112  		t.Fatalf("bad: %v", data)
   113  	}
   114  }
   115  
   116  func TestNewInmemSinkFromURL(t *testing.T) {
   117  	for _, tc := range []struct {
   118  		desc           string
   119  		input          string
   120  		expectErr      string
   121  		expectInterval time.Duration
   122  		expectRetain   time.Duration
   123  	}{
   124  		{
   125  			desc:           "interval and duration are set via query params",
   126  			input:          "inmem://?interval=11s&retain=22s",
   127  			expectInterval: duration(t, "11s"),
   128  			expectRetain:   duration(t, "22s"),
   129  		},
   130  		{
   131  			desc:      "interval is required",
   132  			input:     "inmem://?retain=22s",
   133  			expectErr: "Bad 'interval' param",
   134  		},
   135  		{
   136  			desc:      "interval must be a duration",
   137  			input:     "inmem://?retain=30s&interval=HIYA",
   138  			expectErr: "Bad 'interval' param",
   139  		},
   140  		{
   141  			desc:      "retain is required",
   142  			input:     "inmem://?interval=30s",
   143  			expectErr: "Bad 'retain' param",
   144  		},
   145  		{
   146  			desc:      "retain must be a valid duration",
   147  			input:     "inmem://?interval=30s&retain=HELLO",
   148  			expectErr: "Bad 'retain' param",
   149  		},
   150  	} {
   151  		t.Run(tc.desc, func(t *testing.T) {
   152  			u, err := url.Parse(tc.input)
   153  			if err != nil {
   154  				t.Fatalf("error parsing URL: %s", err)
   155  			}
   156  			ms, err := NewInmemSinkFromURL(u)
   157  			if tc.expectErr != "" {
   158  				if !strings.Contains(err.Error(), tc.expectErr) {
   159  					t.Fatalf("expected err: %q, to contain: %q", err, tc.expectErr)
   160  				}
   161  			} else {
   162  				if err != nil {
   163  					t.Fatalf("unexpected err: %s", err)
   164  				}
   165  				is := ms.(*InmemSink)
   166  				if is.interval != tc.expectInterval {
   167  					t.Fatalf("expected interval %s, got: %s", tc.expectInterval, is.interval)
   168  				}
   169  				if is.retain != tc.expectRetain {
   170  					t.Fatalf("expected retain %s, got: %s", tc.expectRetain, is.retain)
   171  				}
   172  			}
   173  		})
   174  	}
   175  }
   176  
   177  func min(a, b int) int {
   178  	if a < b {
   179  		return a
   180  	}
   181  	return b
   182  }
   183  
   184  func duration(t *testing.T, s string) time.Duration {
   185  	dur, err := time.ParseDuration(s)
   186  	if err != nil {
   187  		t.Fatalf("error parsing duration: %s", err)
   188  	}
   189  	return dur
   190  }