350 lines
9.3 KiB
Go
350 lines
9.3 KiB
Go
package datadogunifi_test
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DataDog/datadog-go/v5/statsd"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/unpoller/unpoller/pkg/datadogunifi"
|
|
"github.com/unpoller/unpoller/pkg/unittest"
|
|
"golift.io/cnfg"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type mockValue struct {
|
|
value any
|
|
tags []string
|
|
}
|
|
|
|
// mockStatsd allows us to mock statsd.ClientInterface and collect data to ensure we're writing out
|
|
// metrics as expected with the correct types
|
|
type mockStatsd struct {
|
|
sync.RWMutex
|
|
gauges map[string]mockValue
|
|
counts map[string]mockValue
|
|
histograms map[string]mockValue
|
|
distributions map[string]mockValue
|
|
sets map[string]mockValue
|
|
timings map[string]mockValue
|
|
events []string
|
|
checks []string
|
|
}
|
|
|
|
// GaugeWithTimestamp mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error {
|
|
// not supported
|
|
return nil
|
|
}
|
|
|
|
// CountWithTimestamp mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) CountWithTimestamp(name string, value int64, tags []string, rate float64, timestamp time.Time) error {
|
|
// not supported
|
|
return nil
|
|
}
|
|
|
|
// IsClosed mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) IsClosed() bool {
|
|
return false
|
|
}
|
|
|
|
// HistogramWithTimestamp mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) HistogramWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
// DistributionWithTimestamp mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) DistributionWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
// SetWithTimestamp mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) SetWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
// TimingWithTimestamp mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) TimingWithTimestamp(name string, value int64, tags []string, rate float64) error {
|
|
return nil
|
|
}
|
|
|
|
// GetTelemetry mock interface
|
|
// nolint:all
|
|
func (m *mockStatsd) GetTelemetry() statsd.Telemetry {
|
|
return statsd.Telemetry{}
|
|
}
|
|
|
|
// Gauge measures the value of a metric at a particular time.
|
|
func (m *mockStatsd) Gauge(name string, value float64, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.gauges[name] = mockValue{value, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Count tracks how many times something happened per second.
|
|
func (m *mockStatsd) Count(name string, value int64, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.counts[name] = mockValue{value, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Histogram tracks the statistical distribution of a set of values on each host.
|
|
func (m *mockStatsd) Histogram(name string, value float64, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.histograms[name] = mockValue{value, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Distribution tracks the statistical distribution of a set of values across your infrastructure.
|
|
func (m *mockStatsd) Distribution(name string, value float64, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.distributions[name] = mockValue{value, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Decr is just Count of -1
|
|
func (m *mockStatsd) Decr(name string, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.counts[name] = mockValue{-1, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Incr is just Count of 1
|
|
func (m *mockStatsd) Incr(name string, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.counts[name] = mockValue{1, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Set counts the number of unique elements in a group.
|
|
func (m *mockStatsd) Set(name string, value string, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.sets[name] = mockValue{value, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Timing sends timing information, it is an alias for TimeInMilliseconds
|
|
func (m *mockStatsd) Timing(name string, value time.Duration, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.timings[name] = mockValue{value, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TimeInMilliseconds sends timing information in milliseconds.
|
|
// It is flushed by statsd with percentiles, mean and other info (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing)
|
|
func (m *mockStatsd) TimeInMilliseconds(name string, value float64, tags []string, _ float64) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.timings[name] = mockValue{value, tags}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Event sends the provided Event.
|
|
func (m *mockStatsd) Event(e *statsd.Event) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.events = append(m.events, e.Title)
|
|
|
|
return nil
|
|
}
|
|
|
|
// SimpleEvent sends an event with the provided title and text.
|
|
func (m *mockStatsd) SimpleEvent(title, _ string) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.events = append(m.events, title)
|
|
|
|
return nil
|
|
}
|
|
|
|
// ServiceCheck sends the provided ServiceCheck.
|
|
func (m *mockStatsd) ServiceCheck(sc *statsd.ServiceCheck) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.checks = append(m.checks, sc.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// SimpleServiceCheck sends an serviceCheck with the provided name and status.
|
|
func (m *mockStatsd) SimpleServiceCheck(name string, _ statsd.ServiceCheckStatus) error {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.checks = append(m.checks, name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Close the client connection.
|
|
func (m *mockStatsd) Close() error {
|
|
return nil
|
|
}
|
|
|
|
// Flush forces a flush of all the queued dogstatsd payloads.
|
|
func (m *mockStatsd) Flush() error {
|
|
return nil
|
|
}
|
|
|
|
// SetWriteTimeout allows the user to set a custom write timeout.
|
|
func (m *mockStatsd) SetWriteTimeout(_ time.Duration) error {
|
|
return nil
|
|
}
|
|
|
|
type testExpectations struct {
|
|
Gauges []string `yaml:"gauges"`
|
|
Counts []string `yaml:"counts"`
|
|
Timings []string `yaml:"timings"`
|
|
Sets []string `yaml:"sets"`
|
|
Histograms []string `yaml:"histograms"`
|
|
Distributions []string `yaml:"distributions"`
|
|
ServiceChecks []string `yaml:"service_checks"`
|
|
}
|
|
|
|
func TestDataDogUnifiIntegration(t *testing.T) {
|
|
// load test expectations file
|
|
yamlFile, err := os.ReadFile("integration_test_expectations.yaml")
|
|
require.NoError(t, err)
|
|
|
|
var testExpectationsData testExpectations
|
|
err = yaml.Unmarshal(yamlFile, &testExpectationsData)
|
|
require.NoError(t, err)
|
|
|
|
testRig := unittest.NewTestSetup(t)
|
|
defer testRig.Close()
|
|
|
|
mockCapture := &mockStatsd{
|
|
gauges: make(map[string]mockValue, 0),
|
|
counts: make(map[string]mockValue, 0),
|
|
histograms: make(map[string]mockValue, 0),
|
|
distributions: make(map[string]mockValue, 0),
|
|
sets: make(map[string]mockValue, 0),
|
|
timings: make(map[string]mockValue, 0),
|
|
events: make([]string, 0),
|
|
checks: make([]string, 0),
|
|
}
|
|
|
|
u := datadogunifi.DatadogUnifi{
|
|
Datadog: &datadogunifi.Datadog{
|
|
Config: &datadogunifi.Config{
|
|
Enable: unittest.PBool(true),
|
|
Interval: cnfg.Duration{Duration: time.Hour},
|
|
},
|
|
},
|
|
Statsd: mockCapture,
|
|
}
|
|
|
|
testRig.Initialize()
|
|
|
|
u.Collector = testRig.Collector
|
|
u.Collect(time.Minute)
|
|
mockCapture.RLock()
|
|
defer mockCapture.RUnlock()
|
|
|
|
// gauges
|
|
assert.Equal(t, len(testExpectationsData.Gauges), len(mockCapture.gauges))
|
|
|
|
expectedKeys := unittest.NewSetFromSlice[string](testExpectationsData.Gauges)
|
|
foundKeys := unittest.NewSetFromMap[string](mockCapture.gauges)
|
|
additions, deletions := expectedKeys.Difference(foundKeys)
|
|
assert.Len(t, additions, 0)
|
|
assert.Len(t, deletions, 0)
|
|
|
|
// counts
|
|
assert.Len(t, mockCapture.counts, 12)
|
|
|
|
expectedKeys = unittest.NewSetFromSlice[string](testExpectationsData.Counts)
|
|
foundKeys = unittest.NewSetFromMap[string](mockCapture.counts)
|
|
additions, deletions = expectedKeys.Difference(foundKeys)
|
|
assert.Len(t, additions, 0)
|
|
assert.Len(t, deletions, 0)
|
|
|
|
// timings
|
|
assert.Len(t, mockCapture.timings, 2)
|
|
|
|
expectedKeys = unittest.NewSetFromSlice[string](testExpectationsData.Timings)
|
|
foundKeys = unittest.NewSetFromMap[string](mockCapture.timings)
|
|
additions, deletions = expectedKeys.Difference(foundKeys)
|
|
assert.Len(t, additions, 0)
|
|
assert.Len(t, deletions, 0)
|
|
|
|
// histograms
|
|
assert.Len(t, mockCapture.histograms, 0)
|
|
|
|
expectedKeys = unittest.NewSetFromSlice[string](testExpectationsData.Histograms)
|
|
foundKeys = unittest.NewSetFromMap[string](mockCapture.histograms)
|
|
additions, deletions = expectedKeys.Difference(foundKeys)
|
|
assert.Len(t, additions, 0)
|
|
assert.Len(t, deletions, 0)
|
|
|
|
// distributions
|
|
assert.Len(t, mockCapture.distributions, 0)
|
|
|
|
expectedKeys = unittest.NewSetFromSlice[string](testExpectationsData.Distributions)
|
|
foundKeys = unittest.NewSetFromMap[string](mockCapture.distributions)
|
|
additions, deletions = expectedKeys.Difference(foundKeys)
|
|
assert.Len(t, additions, 0)
|
|
assert.Len(t, deletions, 0)
|
|
|
|
// sets
|
|
assert.Len(t, mockCapture.sets, 0)
|
|
|
|
expectedKeys = unittest.NewSetFromSlice[string](testExpectationsData.Sets)
|
|
foundKeys = unittest.NewSetFromMap[string](mockCapture.sets)
|
|
additions, deletions = expectedKeys.Difference(foundKeys)
|
|
assert.Len(t, additions, 0)
|
|
assert.Len(t, deletions, 0)
|
|
|
|
// events
|
|
// at least one event from an alarm should happen
|
|
assert.GreaterOrEqual(t, len(mockCapture.events), 1)
|
|
|
|
// service checks
|
|
assert.Len(t, mockCapture.checks, 0)
|
|
|
|
expectedKeys = unittest.NewSetFromSlice[string](testExpectationsData.ServiceChecks)
|
|
foundKeys = unittest.NewSetFromSlice[string](mockCapture.checks)
|
|
additions, deletions = expectedKeys.Difference(foundKeys)
|
|
assert.Len(t, additions, 0)
|
|
assert.Len(t, deletions, 0)
|
|
}
|