Codebase list golang-github-go-kit-kit / b04b2e2 metrics / cloudwatch / cloudwatch_test.go
b04b2e2

Tree @b04b2e2 (Download .tar.gz)

cloudwatch_test.go @b04b2e2

f99615e
 
 
 
 
196891a
ead1f1c
 
f99615e
 
 
ead1f1c
f99615e
24eddfe
f99615e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196891a
f99615e
 
 
 
 
 
 
 
 
 
 
 
 
b04b2e2
 
 
f99615e
 
 
 
 
24eddfe
 
196891a
 
 
 
 
 
 
24eddfe
196891a
24eddfe
 
196891a
 
 
 
24eddfe
 
196891a
 
 
24eddfe
 
196891a
 
a887a20
24eddfe
196891a
24eddfe
 
 
 
 
f99615e
 
 
 
196891a
f99615e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196891a
f99615e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
package cloudwatch

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"testing"

	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/teststat"
)

type mockCloudWatch struct {
	cloudwatchiface.CloudWatchAPI
	mtx                sync.RWMutex
	valuesReceived     map[string]float64
	dimensionsReceived map[string][]*cloudwatch.Dimension
}

func newMockCloudWatch() *mockCloudWatch {
	return &mockCloudWatch{
		valuesReceived:     map[string]float64{},
		dimensionsReceived: map[string][]*cloudwatch.Dimension{},
	}
}

func (mcw *mockCloudWatch) PutMetricData(input *cloudwatch.PutMetricDataInput) (*cloudwatch.PutMetricDataOutput, error) {
	mcw.mtx.Lock()
	defer mcw.mtx.Unlock()
	for _, datum := range input.MetricData {
		mcw.valuesReceived[*datum.MetricName] = *datum.Value
		mcw.dimensionsReceived[*datum.MetricName] = datum.Dimensions
	}
	return nil, nil
}

func testDimensions(svc *mockCloudWatch, name string, labelValues ...string) error {
	dimensions, ok := svc.dimensionsReceived[name]
	if !ok {
		if len(labelValues) > 0 {
			return errors.New("Expected dimensions to be available, but none were")
		}
	}
LabelValues:
	for i, j := 0, 0; i < len(labelValues); i, j = i+2, j+1 {
		name, value := labelValues[i], labelValues[i+1]
		for _, dimension := range dimensions {
			if *dimension.Name == name {
				if *dimension.Value == value {
					break LabelValues
				}
			}
		}
		return fmt.Errorf("Could not find dimension with name %s and value %s", name, value)
	}

	return nil
}

func TestCounter(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, 10, log.NewNopLogger())
	counter := cw.NewCounter(name).With(label, value)
	valuef := func() float64 {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		return svc.valuesReceived[name]
	}
	if err := teststat.TestCounter(counter, valuef); err != nil {
		t.Fatal(err)
	}
	if err := teststat.TestCounter(counter, valuef); err != nil {
		t.Fatal("Fill and flush counter 2nd time: ", err)
	}
	if err := testDimensions(svc, name, label, value); err != nil {
		t.Fatal(err)
	}
}

func TestCounterLowSendConcurrency(t *testing.T) {
	namespace := "abc"
	var names, labels, values []string
	for i := 1; i <= 45; i++ {
		num := strconv.Itoa(i)
		names = append(names, "name"+num)
		labels = append(labels, "label"+num)
		values = append(values, "value"+num)
	}
	svc := newMockCloudWatch()
	cw := New(namespace, svc, 2, log.NewNopLogger())

	counters := make(map[string]metrics.Counter)
	var wants []float64
	for i, name := range names {
		counters[name] = cw.NewCounter(name).With(labels[i], values[i])
		wants = append(wants, teststat.FillCounter(counters[name]))
	}

	err := cw.Send()
	if err != nil {
		t.Fatal(err)
	}

	for i, name := range names {
		if svc.valuesReceived[name] != wants[i] {
			t.Fatalf("want %f, have %f", wants[i], svc.valuesReceived[name])
		}
		if err := testDimensions(svc, name, labels[i], values[i]); err != nil {
			t.Fatal(err)
		}
	}
}

func TestGauge(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, 10, log.NewNopLogger())
	gauge := cw.NewGauge(name).With(label, value)
	valuef := func() float64 {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		return svc.valuesReceived[name]
	}
	if err := teststat.TestGauge(gauge, valuef); err != nil {
		t.Fatal(err)
	}
	if err := testDimensions(svc, name, label, value); err != nil {
		t.Fatal(err)
	}
}

func TestHistogram(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, 10, log.NewNopLogger())
	histogram := cw.NewHistogram(name, 50).With(label, value)
	n50 := fmt.Sprintf("%s_50", name)
	n90 := fmt.Sprintf("%s_90", name)
	n95 := fmt.Sprintf("%s_95", name)
	n99 := fmt.Sprintf("%s_99", name)
	quantiles := func() (p50, p90, p95, p99 float64) {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		p50 = svc.valuesReceived[n50]
		p90 = svc.valuesReceived[n90]
		p95 = svc.valuesReceived[n95]
		p99 = svc.valuesReceived[n99]
		return
	}
	if err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {
		t.Fatal(err)
	}
	if err := testDimensions(svc, n50, label, value); err != nil {
		t.Fatal(err)
	}
	if err := testDimensions(svc, n90, label, value); err != nil {
		t.Fatal(err)
	}
	if err := testDimensions(svc, n95, label, value); err != nil {
		t.Fatal(err)
	}
	if err := testDimensions(svc, n99, label, value); err != nil {
		t.Fatal(err)
	}
}