github.com/hashicorp/go-metrics@v0.5.3/inmem_endpoint_test.go (about) 1 package metrics 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 "time" 11 12 "github.com/pascaldekloe/goe/verify" 13 ) 14 15 func TestDisplayMetrics(t *testing.T) { 16 interval := 10 * time.Millisecond 17 inm := NewInmemSink(interval, 50*time.Millisecond) 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"}, 40, []Label{{"a", "b"}}) 27 inm.AddSample([]string{"foo", "bar"}, 20) 28 inm.AddSample([]string{"foo", "bar"}, 24) 29 inm.AddSampleWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}}) 30 inm.AddSampleWithLabels([]string{"foo", "bar"}, 33, []Label{{"a", "b"}}) 31 32 data := inm.Data() 33 if len(data) != 1 { 34 t.Fatalf("bad: %v", data) 35 } 36 37 expected := MetricsSummary{ 38 Timestamp: data[0].Interval.Round(time.Second).UTC().String(), 39 Gauges: []GaugeValue{ 40 { 41 Name: "foo.bar", 42 Hash: "foo.bar", 43 Value: float32(42), 44 DisplayLabels: map[string]string{}, 45 }, 46 { 47 Name: "foo.bar", 48 Hash: "foo.bar;a=b", 49 Value: float32(23), 50 DisplayLabels: map[string]string{"a": "b"}, 51 }, 52 }, 53 Points: []PointValue{ 54 { 55 Name: "foo.bar", 56 Points: []float32{42}, 57 }, 58 }, 59 Counters: []SampledValue{ 60 { 61 Name: "foo.bar", 62 Hash: "foo.bar", 63 AggregateSample: &AggregateSample{ 64 Count: 2, 65 Min: 20, 66 Max: 22, 67 Sum: 42, 68 SumSq: 884, 69 Rate: 4200, 70 }, 71 Mean: 21, 72 Stddev: 1.4142135623730951, 73 }, 74 { 75 Name: "foo.bar", 76 Hash: "foo.bar;a=b", 77 AggregateSample: &AggregateSample{ 78 Count: 2, 79 Min: 20, 80 Max: 40, 81 Sum: 60, 82 SumSq: 2000, 83 Rate: 6000, 84 }, 85 Mean: 30, 86 Stddev: 14.142135623730951, 87 DisplayLabels: map[string]string{"a": "b"}, 88 }, 89 }, 90 Samples: []SampledValue{ 91 { 92 Name: "foo.bar", 93 Hash: "foo.bar", 94 AggregateSample: &AggregateSample{ 95 Count: 2, 96 Min: 20, 97 Max: 24, 98 Sum: 44, 99 SumSq: 976, 100 Rate: 4400, 101 }, 102 Mean: 22, 103 Stddev: 2.8284271247461903, 104 }, 105 { 106 Name: "foo.bar", 107 Hash: "foo.bar;a=b", 108 AggregateSample: &AggregateSample{ 109 Count: 2, 110 Min: 23, 111 Max: 33, 112 Sum: 56, 113 SumSq: 1618, 114 Rate: 5600, 115 }, 116 Mean: 28, 117 Stddev: 7.0710678118654755, 118 DisplayLabels: map[string]string{"a": "b"}, 119 }, 120 }, 121 } 122 123 raw, err := inm.DisplayMetrics(nil, nil) 124 if err != nil { 125 t.Fatalf("err: %v", err) 126 } 127 result := raw.(MetricsSummary) 128 129 // Ignore the LastUpdated field, we don't export that anyway 130 for i, got := range result.Counters { 131 expected.Counters[i].LastUpdated = got.LastUpdated 132 } 133 for i, got := range result.Samples { 134 expected.Samples[i].LastUpdated = got.LastUpdated 135 } 136 137 verify.Values(t, "all", result, expected) 138 } 139 140 func TestDisplayMetrics_RaceSetGauge(t *testing.T) { 141 interval := 200 * time.Millisecond 142 inm := NewInmemSink(interval, 10*interval) 143 result := make(chan float32) 144 145 go func() { 146 for { 147 time.Sleep(150 * time.Millisecond) 148 inm.SetGauge([]string{"foo", "bar"}, float32(42)) 149 } 150 }() 151 152 go func() { 153 start := time.Now() 154 var summary MetricsSummary 155 // test for twenty intervals 156 for time.Now().Sub(start) < 20*interval { 157 time.Sleep(100 * time.Millisecond) 158 raw, _ := inm.DisplayMetrics(nil, nil) 159 summary = raw.(MetricsSummary) 160 } 161 // save result 162 for _, g := range summary.Gauges { 163 if g.Name == "foo.bar" { 164 result <- g.Value 165 } 166 } 167 close(result) 168 }() 169 170 got := <-result 171 verify.Values(t, "all", got, float32(42)) 172 } 173 174 func TestDisplayMetrics_RaceAddSample(t *testing.T) { 175 interval := 200 * time.Millisecond 176 inm := NewInmemSink(interval, 10*interval) 177 result := make(chan float32) 178 179 go func() { 180 for { 181 time.Sleep(75 * time.Millisecond) 182 inm.AddSample([]string{"foo", "bar"}, float32(0.0)) 183 } 184 }() 185 186 go func() { 187 start := time.Now() 188 var summary MetricsSummary 189 // test for twenty intervals 190 for time.Now().Sub(start) < 20*interval { 191 time.Sleep(100 * time.Millisecond) 192 raw, _ := inm.DisplayMetrics(nil, nil) 193 summary = raw.(MetricsSummary) 194 } 195 // save result 196 for _, g := range summary.Gauges { 197 if g.Name == "foo.bar" { 198 result <- g.Value 199 } 200 } 201 close(result) 202 }() 203 204 got := <-result 205 verify.Values(t, "all", got, float32(0.0)) 206 } 207 208 func TestDisplayMetrics_RaceIncrCounter(t *testing.T) { 209 interval := 200 * time.Millisecond 210 inm := NewInmemSink(interval, 10*interval) 211 result := make(chan float32) 212 213 go func() { 214 for { 215 time.Sleep(75 * time.Millisecond) 216 inm.IncrCounter([]string{"foo", "bar"}, float32(0.0)) 217 } 218 }() 219 220 go func() { 221 start := time.Now() 222 var summary MetricsSummary 223 // test for twenty intervals 224 for time.Now().Sub(start) < 20*interval { 225 time.Sleep(30 * time.Millisecond) 226 raw, _ := inm.DisplayMetrics(nil, nil) 227 summary = raw.(MetricsSummary) 228 } 229 // save result for testing 230 for _, g := range summary.Gauges { 231 if g.Name == "foo.bar" { 232 result <- g.Value 233 } 234 } 235 close(result) 236 }() 237 238 got := <-result 239 verify.Values(t, "all", got, float32(0.0)) 240 } 241 242 func TestDisplayMetrics_RaceMetricsSetGauge(t *testing.T) { 243 interval := 200 * time.Millisecond 244 inm := NewInmemSink(interval, 10*interval) 245 met := &Metrics{Config: Config{FilterDefault: true}, sink: inm} 246 result := make(chan float32) 247 labels := []Label{ 248 {"name1", "value1"}, 249 {"name2", "value2"}, 250 } 251 252 go func() { 253 for { 254 time.Sleep(75 * time.Millisecond) 255 met.SetGaugeWithLabels([]string{"foo", "bar"}, float32(42), labels) 256 } 257 }() 258 259 go func() { 260 start := time.Now() 261 var summary MetricsSummary 262 // test for twenty intervals 263 for time.Now().Sub(start) < 40*interval { 264 time.Sleep(150 * time.Millisecond) 265 raw, _ := inm.DisplayMetrics(nil, nil) 266 summary = raw.(MetricsSummary) 267 } 268 // save result 269 for _, g := range summary.Gauges { 270 if g.Name == "foo.bar" { 271 result <- g.Value 272 } 273 } 274 close(result) 275 }() 276 277 got := <-result 278 verify.Values(t, "all", got, float32(42)) 279 } 280 281 func TestInmemSink_Stream(t *testing.T) { 282 interval := 10 * time.Millisecond 283 total := 50 * time.Millisecond 284 inm := NewInmemSink(interval, total) 285 286 ctx, cancel := context.WithTimeout(context.Background(), total*2) 287 defer cancel() 288 289 chDone := make(chan struct{}) 290 291 go func() { 292 for i := float32(0); ctx.Err() == nil; i++ { 293 inm.SetGaugeWithLabels([]string{"gauge", "foo"}, 20+i, []Label{{"a", "b"}}) 294 inm.EmitKey([]string{"key", "foo"}, 30+i) 295 inm.IncrCounterWithLabels([]string{"counter", "bar"}, 40+i, []Label{{"a", "b"}}) 296 inm.IncrCounterWithLabels([]string{"counter", "bar"}, 50+i, []Label{{"a", "b"}}) 297 inm.AddSampleWithLabels([]string{"sample", "bar"}, 60+i, []Label{{"a", "b"}}) 298 inm.AddSampleWithLabels([]string{"sample", "bar"}, 70+i, []Label{{"a", "b"}}) 299 time.Sleep(interval / 3) 300 } 301 close(chDone) 302 }() 303 304 resp := httptest.NewRecorder() 305 enc := encoder{ 306 encoder: json.NewEncoder(resp), 307 flusher: resp, 308 } 309 inm.Stream(ctx, enc) 310 311 <-chDone 312 313 decoder := json.NewDecoder(resp.Body) 314 var prevGaugeValue float32 315 for i := 0; i < 8; i++ { 316 var summary MetricsSummary 317 if err := decoder.Decode(&summary); err != nil { 318 t.Fatalf("expected no error while decoding response %d, got %v", i, err) 319 } 320 if count := len(summary.Gauges); count != 1 { 321 t.Fatalf("expected at least one gauge in response %d, got %v", i, count) 322 } 323 value := summary.Gauges[0].Value 324 // The upper bound of the gauge value is not known, but we can expect it 325 // to be less than 50 because it increments by 3 every interval and we run 326 // for ~10 intervals. 327 if value < 20 || value > 50 { 328 t.Fatalf("expected interval %d guage value between 20 and 50, got %v", i, value) 329 } 330 if value <= prevGaugeValue { 331 t.Fatalf("expected interval %d guage value to be greater than previous, %v == %v", i, value, prevGaugeValue) 332 } 333 prevGaugeValue = value 334 } 335 } 336 337 type encoder struct { 338 flusher http.Flusher 339 encoder *json.Encoder 340 } 341 342 func (e encoder) Encode(metrics interface{}) error { 343 if err := e.encoder.Encode(metrics); err != nil { 344 fmt.Println("failed to encode metrics summary", "error", err) 345 return err 346 } 347 e.flusher.Flush() 348 return nil 349 }