go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/cmd/statsd-to-tsmon/statsd_test.go (about)

     1  // Copyright 2020 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"bytes"
    19  	"testing"
    20  
    21  	. "github.com/smartystreets/goconvey/convey"
    22  )
    23  
    24  func TestParseStatsdMetric(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	Convey("Happy path", t, func() {
    28  		roundTrip := func(line string) bool {
    29  			m := &StatsdMetric{}
    30  			read, err := ParseStatsdMetric([]byte(line), m)
    31  			So(err, ShouldBeNil)
    32  			So(read, ShouldEqual, len(line))
    33  
    34  			// There should be no special chars in parsed portions.
    35  			for _, n := range m.Name {
    36  				So(n, ShouldNotContain, '.')
    37  				So(n, ShouldNotContain, ':')
    38  				So(n, ShouldNotContain, '|')
    39  			}
    40  			So(m.Value, ShouldNotContain, '.')
    41  			So(m.Value, ShouldNotContain, ':')
    42  			So(m.Value, ShouldNotContain, '|')
    43  
    44  			// Reconstruct the original line from parsed components.
    45  			buf := bytes.Join(m.Name, []byte{'.'})
    46  			buf = append(buf, ':')
    47  			buf = append(buf, m.Value...)
    48  			buf = append(buf, '|')
    49  			switch m.Type {
    50  			case StatsdMetricGauge:
    51  				buf = append(buf, 'g')
    52  			case StatsdMetricCounter:
    53  				buf = append(buf, 'c')
    54  			case StatsdMetricTimer:
    55  				buf = append(buf, 'm', 's')
    56  			}
    57  
    58  			return string(buf) == line
    59  		}
    60  
    61  		So(roundTrip("a.bcd.xyz:123|g"), ShouldBeTrue)
    62  		So(roundTrip("a.b.c:123|c"), ShouldBeTrue)
    63  		So(roundTrip("a.b.c:123|ms"), ShouldBeTrue)
    64  		So(roundTrip("a:123|g"), ShouldBeTrue)
    65  	})
    66  
    67  	Convey("Parses one line only", t, func() {
    68  		buf := []byte("a:123|g\nstuff")
    69  		m := &StatsdMetric{}
    70  		read, err := ParseStatsdMetric(buf, m)
    71  		So(err, ShouldBeNil)
    72  		So(buf[read:], ShouldResemble, []byte("stuff"))
    73  
    74  		So(m, ShouldResemble, &StatsdMetric{
    75  			Name:  [][]byte{{'a'}},
    76  			Type:  StatsdMetricGauge,
    77  			Value: []byte("123"),
    78  		})
    79  	})
    80  
    81  	Convey("Skips junk", t, func() {
    82  		buf := []byte("a:123|g|skipped junk\nstuff")
    83  		m := &StatsdMetric{}
    84  		read, err := ParseStatsdMetric(buf, m)
    85  		So(err, ShouldBeNil)
    86  		So(buf[read:], ShouldResemble, []byte("stuff"))
    87  
    88  		So(m, ShouldResemble, &StatsdMetric{
    89  			Name:  [][]byte{{'a'}},
    90  			Type:  StatsdMetricGauge,
    91  			Value: []byte("123"),
    92  		})
    93  	})
    94  
    95  	Convey("ErrMalformedStatsdLine", t, func() {
    96  		buf := []byte("a:b:c\nstuff")
    97  		m := &StatsdMetric{}
    98  		read, err := ParseStatsdMetric(buf, m)
    99  		So(err, ShouldEqual, ErrMalformedStatsdLine)
   100  		So(buf[read:], ShouldResemble, []byte("stuff"))
   101  	})
   102  
   103  	Convey("ErrMalformedStatsdLine on empty line", t, func() {
   104  		buf := []byte("\nstuff")
   105  		m := &StatsdMetric{}
   106  		read, err := ParseStatsdMetric(buf, m)
   107  		So(err, ShouldEqual, ErrMalformedStatsdLine)
   108  		So(buf[read:], ShouldResemble, []byte("stuff"))
   109  	})
   110  
   111  	Convey("ErrMalformedStatsdLine on empty name component", t, func() {
   112  		call := func(pat string) error {
   113  			buf := []byte(pat + "\nother stuff")
   114  			read, err := ParseStatsdMetric(buf, &StatsdMetric{})
   115  			So(buf[read:], ShouldResemble, []byte("other stuff"))
   116  			return err
   117  		}
   118  
   119  		So(call("ab..cd:123|g"), ShouldEqual, ErrMalformedStatsdLine)
   120  		So(call("a.:123|g"), ShouldEqual, ErrMalformedStatsdLine)
   121  		So(call(".a:123|g"), ShouldEqual, ErrMalformedStatsdLine)
   122  		So(call(".:123|g"), ShouldEqual, ErrMalformedStatsdLine)
   123  		So(call(":|g"), ShouldEqual, ErrMalformedStatsdLine)
   124  	})
   125  
   126  	Convey("ErrUnsupportedType", t, func() {
   127  		buf := []byte("a:123|h\nstuff")
   128  		m := &StatsdMetric{}
   129  		read, err := ParseStatsdMetric(buf, m)
   130  		So(err, ShouldEqual, ErrUnsupportedType)
   131  		So(buf[read:], ShouldResemble, []byte("stuff"))
   132  	})
   133  }