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  }