1package metrics
2
3import (
4	"reflect"
5	"runtime"
6	"testing"
7	"time"
8)
9
10func mockMetric() (*MockSink, *Metrics) {
11	m := &MockSink{}
12	met := &Metrics{Config: Config{FilterDefault: true}, sink: m}
13	return m, met
14}
15
16func TestMetrics_SetGauge(t *testing.T) {
17	m, met := mockMetric()
18	met.SetGauge([]string{"key"}, float32(1))
19	if m.getKeys()[0][0] != "key" {
20		t.Fatalf("")
21	}
22	if m.vals[0] != 1 {
23		t.Fatalf("")
24	}
25
26	m, met = mockMetric()
27	labels := []Label{{"a", "b"}}
28	met.SetGaugeWithLabels([]string{"key"}, float32(1), labels)
29	if m.getKeys()[0][0] != "key" {
30		t.Fatalf("")
31	}
32	if m.vals[0] != 1 {
33		t.Fatalf("")
34	}
35	if !reflect.DeepEqual(m.labels[0], labels) {
36		t.Fatalf("")
37	}
38
39	m, met = mockMetric()
40	met.HostName = "test"
41	met.EnableHostname = true
42	met.SetGauge([]string{"key"}, float32(1))
43	if m.getKeys()[0][0] != "test" || m.getKeys()[0][1] != "key" {
44		t.Fatalf("")
45	}
46	if m.vals[0] != 1 {
47		t.Fatalf("")
48	}
49
50	m, met = mockMetric()
51	met.EnableTypePrefix = true
52	met.SetGauge([]string{"key"}, float32(1))
53	if m.getKeys()[0][0] != "gauge" || m.getKeys()[0][1] != "key" {
54		t.Fatalf("")
55	}
56	if m.vals[0] != 1 {
57		t.Fatalf("")
58	}
59
60	m, met = mockMetric()
61	met.ServiceName = "service"
62	met.SetGauge([]string{"key"}, float32(1))
63	if m.getKeys()[0][0] != "service" || m.getKeys()[0][1] != "key" {
64		t.Fatalf("")
65	}
66	if m.vals[0] != 1 {
67		t.Fatalf("")
68	}
69}
70
71func TestMetrics_EmitKey(t *testing.T) {
72	m, met := mockMetric()
73	met.EmitKey([]string{"key"}, float32(1))
74	if m.getKeys()[0][0] != "key" {
75		t.Fatalf("")
76	}
77	if m.vals[0] != 1 {
78		t.Fatalf("")
79	}
80
81	m, met = mockMetric()
82	met.EnableTypePrefix = true
83	met.EmitKey([]string{"key"}, float32(1))
84	if m.getKeys()[0][0] != "kv" || m.getKeys()[0][1] != "key" {
85		t.Fatalf("")
86	}
87	if m.vals[0] != 1 {
88		t.Fatalf("")
89	}
90
91	m, met = mockMetric()
92	met.ServiceName = "service"
93	met.EmitKey([]string{"key"}, float32(1))
94	if m.getKeys()[0][0] != "service" || m.getKeys()[0][1] != "key" {
95		t.Fatalf("")
96	}
97	if m.vals[0] != 1 {
98		t.Fatalf("")
99	}
100}
101
102func TestMetrics_IncrCounter(t *testing.T) {
103	m, met := mockMetric()
104	met.IncrCounter([]string{"key"}, float32(1))
105	if m.getKeys()[0][0] != "key" {
106		t.Fatalf("")
107	}
108	if m.vals[0] != 1 {
109		t.Fatalf("")
110	}
111
112	m, met = mockMetric()
113	labels := []Label{{"a", "b"}}
114	met.IncrCounterWithLabels([]string{"key"}, float32(1), labels)
115	if m.getKeys()[0][0] != "key" {
116		t.Fatalf("")
117	}
118	if m.vals[0] != 1 {
119		t.Fatalf("")
120	}
121	if !reflect.DeepEqual(m.labels[0], labels) {
122		t.Fatalf("")
123	}
124
125	m, met = mockMetric()
126	met.EnableTypePrefix = true
127	met.IncrCounter([]string{"key"}, float32(1))
128	if m.getKeys()[0][0] != "counter" || m.getKeys()[0][1] != "key" {
129		t.Fatalf("")
130	}
131	if m.vals[0] != 1 {
132		t.Fatalf("")
133	}
134
135	m, met = mockMetric()
136	met.ServiceName = "service"
137	met.IncrCounter([]string{"key"}, float32(1))
138	if m.getKeys()[0][0] != "service" || m.getKeys()[0][1] != "key" {
139		t.Fatalf("")
140	}
141	if m.vals[0] != 1 {
142		t.Fatalf("")
143	}
144}
145
146func TestMetrics_AddSample(t *testing.T) {
147	m, met := mockMetric()
148	met.AddSample([]string{"key"}, float32(1))
149	if m.getKeys()[0][0] != "key" {
150		t.Fatalf("")
151	}
152	if m.vals[0] != 1 {
153		t.Fatalf("")
154	}
155
156	m, met = mockMetric()
157	labels := []Label{{"a", "b"}}
158	met.AddSampleWithLabels([]string{"key"}, float32(1), labels)
159	if m.getKeys()[0][0] != "key" {
160		t.Fatalf("")
161	}
162	if m.vals[0] != 1 {
163		t.Fatalf("")
164	}
165	if !reflect.DeepEqual(m.labels[0], labels) {
166		t.Fatalf("")
167	}
168
169	m, met = mockMetric()
170	met.EnableTypePrefix = true
171	met.AddSample([]string{"key"}, float32(1))
172	if m.getKeys()[0][0] != "sample" || m.getKeys()[0][1] != "key" {
173		t.Fatalf("")
174	}
175	if m.vals[0] != 1 {
176		t.Fatalf("")
177	}
178
179	m, met = mockMetric()
180	met.ServiceName = "service"
181	met.AddSample([]string{"key"}, float32(1))
182	if m.getKeys()[0][0] != "service" || m.getKeys()[0][1] != "key" {
183		t.Fatalf("")
184	}
185	if m.vals[0] != 1 {
186		t.Fatalf("")
187	}
188}
189
190func TestMetrics_MeasureSince(t *testing.T) {
191	m, met := mockMetric()
192	met.TimerGranularity = time.Millisecond
193	n := time.Now()
194	met.MeasureSince([]string{"key"}, n)
195	if m.getKeys()[0][0] != "key" {
196		t.Fatalf("")
197	}
198	if m.vals[0] > 0.1 {
199		t.Fatalf("")
200	}
201
202	m, met = mockMetric()
203	met.TimerGranularity = time.Millisecond
204	labels := []Label{{"a", "b"}}
205	met.MeasureSinceWithLabels([]string{"key"}, n, labels)
206	if m.getKeys()[0][0] != "key" {
207		t.Fatalf("")
208	}
209	if m.vals[0] > 0.1 {
210		t.Fatalf("")
211	}
212	if !reflect.DeepEqual(m.labels[0], labels) {
213		t.Fatalf("")
214	}
215
216	m, met = mockMetric()
217	met.TimerGranularity = time.Millisecond
218	met.EnableTypePrefix = true
219	met.MeasureSince([]string{"key"}, n)
220	if m.getKeys()[0][0] != "timer" || m.getKeys()[0][1] != "key" {
221		t.Fatalf("")
222	}
223	if m.vals[0] > 0.1 {
224		t.Fatalf("")
225	}
226
227	m, met = mockMetric()
228	met.TimerGranularity = time.Millisecond
229	met.ServiceName = "service"
230	met.MeasureSince([]string{"key"}, n)
231	if m.getKeys()[0][0] != "service" || m.getKeys()[0][1] != "key" {
232		t.Fatalf("")
233	}
234	if m.vals[0] > 0.1 {
235		t.Fatalf("")
236	}
237}
238
239func TestMetrics_EmitRuntimeStats(t *testing.T) {
240	runtime.GC()
241	m, met := mockMetric()
242	met.emitRuntimeStats()
243
244	if m.getKeys()[0][0] != "runtime" || m.getKeys()[0][1] != "num_goroutines" {
245		t.Fatalf("bad key %v", m.getKeys())
246	}
247	if m.vals[0] <= 1 {
248		t.Fatalf("bad val: %v", m.vals)
249	}
250
251	if m.getKeys()[1][0] != "runtime" || m.getKeys()[1][1] != "alloc_bytes" {
252		t.Fatalf("bad key %v", m.getKeys())
253	}
254	if m.vals[1] <= 40000 {
255		t.Fatalf("bad val: %v", m.vals)
256	}
257
258	if m.getKeys()[2][0] != "runtime" || m.getKeys()[2][1] != "sys_bytes" {
259		t.Fatalf("bad key %v", m.getKeys())
260	}
261	if m.vals[2] <= 100000 {
262		t.Fatalf("bad val: %v", m.vals)
263	}
264
265	if m.getKeys()[3][0] != "runtime" || m.getKeys()[3][1] != "malloc_count" {
266		t.Fatalf("bad key %v", m.getKeys())
267	}
268	if m.vals[3] <= 100 {
269		t.Fatalf("bad val: %v", m.vals)
270	}
271
272	if m.getKeys()[4][0] != "runtime" || m.getKeys()[4][1] != "free_count" {
273		t.Fatalf("bad key %v", m.getKeys())
274	}
275	if m.vals[4] <= 100 {
276		t.Fatalf("bad val: %v", m.vals)
277	}
278
279	if m.getKeys()[5][0] != "runtime" || m.getKeys()[5][1] != "heap_objects" {
280		t.Fatalf("bad key %v", m.getKeys())
281	}
282	if m.vals[5] <= 100 {
283		t.Fatalf("bad val: %v", m.vals)
284	}
285
286	if m.getKeys()[6][0] != "runtime" || m.getKeys()[6][1] != "total_gc_pause_ns" {
287		t.Fatalf("bad key %v", m.getKeys())
288	}
289	if m.vals[6] <= 100 {
290		t.Fatalf("bad val: %v\nkeys: %v", m.vals, m.getKeys())
291	}
292
293	if m.getKeys()[7][0] != "runtime" || m.getKeys()[7][1] != "total_gc_runs" {
294		t.Fatalf("bad key %v", m.getKeys())
295	}
296	if m.vals[7] < 1 {
297		t.Fatalf("bad val: %v", m.vals)
298	}
299
300	if m.getKeys()[8][0] != "runtime" || m.getKeys()[8][1] != "gc_pause_ns" {
301		t.Fatalf("bad key %v", m.getKeys())
302	}
303	if m.vals[8] <= 1000 {
304		t.Fatalf("bad val: %v", m.vals)
305	}
306}
307
308func TestInsert(t *testing.T) {
309	k := []string{"hi", "bob"}
310	exp := []string{"hi", "there", "bob"}
311	out := insert(1, "there", k)
312	if !reflect.DeepEqual(exp, out) {
313		t.Fatalf("bad insert %v %v", exp, out)
314	}
315}
316
317func TestMetrics_Filter_Blacklist(t *testing.T) {
318	m := &MockSink{}
319	conf := DefaultConfig("")
320	conf.AllowedPrefixes = []string{"service", "debug.thing"}
321	conf.BlockedPrefixes = []string{"debug"}
322	conf.EnableHostname = false
323	met, err := New(conf, m)
324	if err != nil {
325		t.Fatal(err)
326	}
327
328	// Allowed by default
329	key := []string{"thing"}
330	met.SetGauge(key, 1)
331	if !reflect.DeepEqual(m.getKeys()[0], key) {
332		t.Fatalf("key doesn't exist %v, %v", m.getKeys()[0], key)
333	}
334	if m.vals[0] != 1 {
335		t.Fatalf("bad val: %v", m.vals[0])
336	}
337
338	// Allowed by filter
339	key = []string{"service", "thing"}
340	met.SetGauge(key, 2)
341	if !reflect.DeepEqual(m.getKeys()[1], key) {
342		t.Fatalf("key doesn't exist")
343	}
344	if m.vals[1] != 2 {
345		t.Fatalf("bad val: %v", m.vals[1])
346	}
347
348	// Allowed by filter, subtree of a blocked entry
349	key = []string{"debug", "thing"}
350	met.SetGauge(key, 3)
351	if !reflect.DeepEqual(m.getKeys()[2], key) {
352		t.Fatalf("key doesn't exist")
353	}
354	if m.vals[2] != 3 {
355		t.Fatalf("bad val: %v", m.vals[2])
356	}
357
358	// Blocked by filter
359	key = []string{"debug", "other-thing"}
360	met.SetGauge(key, 4)
361	if len(m.getKeys()) != 3 {
362		t.Fatalf("key shouldn't exist")
363	}
364}
365
366func HasElem(s interface{}, elem interface{}) bool {
367	arrV := reflect.ValueOf(s)
368
369	if arrV.Kind() == reflect.Slice {
370		for i := 0; i < arrV.Len(); i++ {
371			if arrV.Index(i).Interface() == elem {
372				return true
373			}
374		}
375	}
376
377	return false
378}
379
380func TestMetrics_Filter_Whitelist(t *testing.T) {
381	m := &MockSink{}
382	conf := DefaultConfig("")
383	conf.AllowedPrefixes = []string{"service", "debug.thing"}
384	conf.BlockedPrefixes = []string{"debug"}
385	conf.FilterDefault = false
386	conf.EnableHostname = false
387	conf.BlockedLabels = []string{"bad_label"}
388	met, err := New(conf, m)
389	if err != nil {
390		t.Fatal(err)
391	}
392
393	// Blocked by default
394	key := []string{"thing"}
395	met.SetGauge(key, 1)
396	if len(m.getKeys()) != 0 {
397		t.Fatalf("key should not exist")
398	}
399
400	// Allowed by filter
401	key = []string{"service", "thing"}
402	met.SetGauge(key, 2)
403	if !reflect.DeepEqual(m.getKeys()[0], key) {
404		t.Fatalf("key doesn't exist")
405	}
406	if m.vals[0] != 2 {
407		t.Fatalf("bad val: %v", m.vals[0])
408	}
409
410	// Allowed by filter, subtree of a blocked entry
411	key = []string{"debug", "thing"}
412	met.SetGauge(key, 3)
413	if !reflect.DeepEqual(m.getKeys()[1], key) {
414		t.Fatalf("key doesn't exist")
415	}
416	if m.vals[1] != 3 {
417		t.Fatalf("bad val: %v", m.vals[1])
418	}
419
420	// Blocked by filter
421	key = []string{"debug", "other-thing"}
422	met.SetGauge(key, 4)
423	if len(m.getKeys()) != 2 {
424		t.Fatalf("key shouldn't exist")
425	}
426	// Test blacklisting of labels
427	key = []string{"debug", "thing"}
428	goodLabel := Label{Name: "good", Value: "should be present"}
429	badLabel := Label{Name: "bad_label", Value: "should not be there"}
430	labels := []Label{badLabel, goodLabel}
431	met.SetGaugeWithLabels(key, 3, labels)
432	if !reflect.DeepEqual(m.getKeys()[1], key) {
433		t.Fatalf("key doesn't exist")
434	}
435	if m.vals[2] != 3 {
436		t.Fatalf("bad val: %v", m.vals[1])
437	}
438	if HasElem(m.labels[2], badLabel) {
439		t.Fatalf("bad_label should not be present in %v", m.labels[2])
440	}
441	if !HasElem(m.labels[2], goodLabel) {
442		t.Fatalf("good label is not present in %v", m.labels[2])
443	}
444}
445
446func TestMetrics_Filter_Labels_Whitelist(t *testing.T) {
447	m := &MockSink{}
448	conf := DefaultConfig("")
449	conf.AllowedPrefixes = []string{"service", "debug.thing"}
450	conf.BlockedPrefixes = []string{"debug"}
451	conf.FilterDefault = false
452	conf.EnableHostname = false
453	conf.AllowedLabels = []string{"good_label"}
454	conf.BlockedLabels = []string{"bad_label"}
455	met, err := New(conf, m)
456	if err != nil {
457		t.Fatal(err)
458	}
459
460	// Blocked by default
461	key := []string{"thing"}
462	key = []string{"debug", "thing"}
463	goodLabel := Label{Name: "good_label", Value: "should be present"}
464	notReallyGoodLabel := Label{Name: "not_really_good_label", Value: "not whitelisted, but not blacklisted"}
465	badLabel := Label{Name: "bad_label", Value: "should not be there"}
466	labels := []Label{badLabel, notReallyGoodLabel, goodLabel}
467	met.SetGaugeWithLabels(key, 1, labels)
468
469	if HasElem(m.labels[0], badLabel) {
470		t.Fatalf("bad_label should not be present in %v", m.labels[0])
471	}
472	if HasElem(m.labels[0], notReallyGoodLabel) {
473		t.Fatalf("not_really_good_label should not be present in %v", m.labels[0])
474	}
475	if !HasElem(m.labels[0], goodLabel) {
476		t.Fatalf("good label is not present in %v", m.labels[0])
477	}
478
479	conf.AllowedLabels = nil
480	met.UpdateFilterAndLabels(conf.AllowedPrefixes, conf.BlockedLabels, conf.AllowedLabels, conf.BlockedLabels)
481	met.SetGaugeWithLabels(key, 1, labels)
482
483	if HasElem(m.labels[1], badLabel) {
484		t.Fatalf("bad_label should not be present in %v", m.labels[1])
485	}
486	// Since no whitelist, not_really_good_label should be there
487	if !HasElem(m.labels[1], notReallyGoodLabel) {
488		t.Fatalf("not_really_good_label is not present in %v", m.labels[1])
489	}
490	if !HasElem(m.labels[1], goodLabel) {
491		t.Fatalf("good label is not present in %v", m.labels[1])
492	}
493}
494
495func TestMetrics_Filter_Labels_ModifyArgs(t *testing.T) {
496	m := &MockSink{}
497	conf := DefaultConfig("")
498	conf.FilterDefault = false
499	conf.EnableHostname = false
500	conf.AllowedLabels = []string{"keep"}
501	conf.BlockedLabels = []string{"delete"}
502	met, err := New(conf, m)
503	if err != nil {
504		t.Fatal(err)
505	}
506
507	// Blocked by default
508	key := []string{"thing"}
509	key = []string{"debug", "thing"}
510	goodLabel := Label{Name: "keep", Value: "should be kept"}
511	badLabel := Label{Name: "delete", Value: "should be deleted"}
512	argLabels := []Label{badLabel, goodLabel, badLabel, goodLabel, badLabel, goodLabel, badLabel}
513	origLabels := append([]Label{}, argLabels...)
514	met.SetGaugeWithLabels(key, 1, argLabels)
515
516	if !reflect.DeepEqual(argLabels, origLabels) {
517		t.Fatalf("SetGaugeWithLabels modified the input argument")
518	}
519}
520