github.com/hashicorp/go-metrics@v0.5.3/datadog/dogstatsd_test.go (about) 1 package datadog 2 3 import ( 4 "net" 5 "reflect" 6 "testing" 7 8 "github.com/hashicorp/go-metrics" 9 ) 10 11 var EmptyTags []metrics.Label 12 13 const ( 14 DogStatsdAddr = "127.0.0.1:7254" 15 HostnameEnabled = true 16 HostnameDisabled = false 17 TestHostname = "test_hostname" 18 ) 19 20 func MockGetHostname() string { 21 return TestHostname 22 } 23 24 var ParseKeyTests = []struct { 25 KeyToParse []string 26 Tags []metrics.Label 27 PropagateHostname bool 28 ExpectedKey []string 29 ExpectedTags []metrics.Label 30 }{ 31 {[]string{"a", MockGetHostname(), "b", "c"}, EmptyTags, HostnameDisabled, []string{"a", "b", "c"}, EmptyTags}, 32 {[]string{"a", "b", "c"}, EmptyTags, HostnameDisabled, []string{"a", "b", "c"}, EmptyTags}, 33 {[]string{"a", "b", "c"}, EmptyTags, HostnameEnabled, []string{"a", "b", "c"}, []metrics.Label{{"host", MockGetHostname()}}}, 34 } 35 36 var FlattenKeyTests = []struct { 37 KeyToFlatten []string 38 Expected string 39 }{ 40 {[]string{"a", "b", "c"}, "a.b.c"}, 41 {[]string{"spaces must", "flatten", "to", "underscores"}, "spaces_must.flatten.to.underscores"}, 42 } 43 44 var MetricSinkTests = []struct { 45 Method string 46 Metric []string 47 Value interface{} 48 Tags []metrics.Label 49 PropagateHostname bool 50 Expected string 51 }{ 52 {"SetGauge", []string{"foo", "bar"}, float32(42), EmptyTags, HostnameDisabled, "foo.bar:42|g"}, 53 {"SetGauge", []string{"foo", "bar", "baz"}, float32(42), EmptyTags, HostnameDisabled, "foo.bar.baz:42|g"}, 54 {"AddSample", []string{"sample", "thing"}, float32(4), EmptyTags, HostnameDisabled, "sample.thing:4.000000|ms"}, 55 {"IncrCounter", []string{"count", "me"}, float32(3), EmptyTags, HostnameDisabled, "count.me:3|c"}, 56 57 {"SetGauge", []string{"foo", "baz"}, float32(42), []metrics.Label{{"my_tag", ""}}, HostnameDisabled, "foo.baz:42|g|#my_tag"}, 58 {"SetGauge", []string{"foo", "baz"}, float32(42), []metrics.Label{{"my tag", "my_value"}}, HostnameDisabled, "foo.baz:42|g|#my_tag:my_value"}, 59 {"SetGauge", []string{"foo", "bar"}, float32(42), []metrics.Label{{"my_tag", "my_value"}, {"other_tag", "other_value"}}, HostnameDisabled, "foo.bar:42|g|#my_tag:my_value,other_tag:other_value"}, 60 {"SetGauge", []string{"foo", "bar"}, float32(42), []metrics.Label{{"my_tag", "my_value"}, {"other_tag", "other_value"}}, HostnameEnabled, "foo.bar:42|g|#my_tag:my_value,other_tag:other_value,host:test_hostname"}, 61 } 62 63 func mockNewDogStatsdSink(addr string, labels []metrics.Label, tagWithHostname bool) *DogStatsdSink { 64 dog, _ := NewDogStatsdSink(addr, MockGetHostname()) 65 _, tags := dog.getFlatkeyAndCombinedLabels(nil, labels) 66 dog.SetTags(tags) 67 if tagWithHostname { 68 dog.EnableHostNamePropagation() 69 } 70 71 return dog 72 } 73 74 func setupTestServerAndBuffer(t *testing.T) (*net.UDPConn, []byte) { 75 udpAddr, err := net.ResolveUDPAddr("udp", DogStatsdAddr) 76 if err != nil { 77 t.Fatal(err) 78 } 79 server, err := net.ListenUDP("udp", udpAddr) 80 if err != nil { 81 t.Fatal(err) 82 } 83 return server, make([]byte, 1024) 84 } 85 86 func TestParseKey(t *testing.T) { 87 for _, tt := range ParseKeyTests { 88 dog := mockNewDogStatsdSink(DogStatsdAddr, tt.Tags, tt.PropagateHostname) 89 // make a copy of the original key 90 original := make([]string, len(tt.KeyToParse)) 91 copy(original, tt.KeyToParse) 92 93 key, tags := dog.parseKey(tt.KeyToParse) 94 95 if !reflect.DeepEqual(key, tt.ExpectedKey) { 96 t.Fatalf("Key Parsing failed for %v", tt.KeyToParse) 97 } 98 99 if !reflect.DeepEqual(tags, tt.ExpectedTags) { 100 t.Fatalf("Tag Parsing Failed for %v, %v != %v", tt.KeyToParse, tags, tt.ExpectedTags) 101 } 102 103 if !reflect.DeepEqual(original, tt.KeyToParse) { 104 t.Fatalf("Key parsing modified the original input key:, original: %v, after parse: %v", original, tt.KeyToParse) 105 } 106 } 107 } 108 109 func TestFlattenKey(t *testing.T) { 110 dog := mockNewDogStatsdSink(DogStatsdAddr, EmptyTags, HostnameDisabled) 111 for _, tt := range FlattenKeyTests { 112 if !reflect.DeepEqual(dog.flattenKey(tt.KeyToFlatten), tt.Expected) { 113 t.Fatalf("Flattening %v failed", tt.KeyToFlatten) 114 } 115 } 116 } 117 118 func TestMetricSink(t *testing.T) { 119 server, buf := setupTestServerAndBuffer(t) 120 defer server.Close() 121 122 for _, tt := range MetricSinkTests { 123 t.Run(tt.Method, func(t *testing.T) { 124 dog := mockNewDogStatsdSink(DogStatsdAddr, tt.Tags, tt.PropagateHostname) 125 method := reflect.ValueOf(dog).MethodByName(tt.Method) 126 method.Call([]reflect.Value{ 127 reflect.ValueOf(tt.Metric), 128 reflect.ValueOf(tt.Value)}) 129 assertServerMatchesExpected(t, server, buf, tt.Expected) 130 }) 131 } 132 } 133 134 func TestTaggableMetrics(t *testing.T) { 135 server, buf := setupTestServerAndBuffer(t) 136 defer server.Close() 137 138 dog := mockNewDogStatsdSink(DogStatsdAddr, EmptyTags, HostnameDisabled) 139 140 dog.AddSampleWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) 141 assertServerMatchesExpected(t, server, buf, "sample.thing:4.000000|ms|#tagkey:tagvalue") 142 143 dog.SetGaugeWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) 144 assertServerMatchesExpected(t, server, buf, "sample.thing:4|g|#tagkey:tagvalue") 145 146 dog.IncrCounterWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) 147 assertServerMatchesExpected(t, server, buf, "sample.thing:4|c|#tagkey:tagvalue") 148 149 dog = mockNewDogStatsdSink(DogStatsdAddr, []metrics.Label{{Name: "global"}}, HostnameEnabled) // with hostname, global tags 150 dog.IncrCounterWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}}) 151 assertServerMatchesExpected(t, server, buf, "sample.thing:4|c|#global,tagkey:tagvalue,host:test_hostname") 152 } 153 154 func assertServerMatchesExpected(t *testing.T, server *net.UDPConn, buf []byte, expected string) { 155 t.Helper() 156 n, _ := server.Read(buf) 157 msg := buf[:n] 158 if string(msg) != expected { 159 t.Fatalf("Line %s does not match expected: %s", string(msg), expected) 160 } 161 } 162 163 func TestMetricSinkInterface(t *testing.T) { 164 var dd *DogStatsdSink 165 _ = metrics.MetricSink(dd) 166 }