blob: 0de0e1734c29bfc37f3627eb5031d19b33a27a7b [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"reflect"
"testing"
"time"
cloudmonitoring "google.golang.org/api/monitoring/v3"
)
func TestCalcIncidents(t *testing.T) {
testCases := []struct {
points []*cloudmonitoring.Point
expectedIncidentData []incidentData
}{
// No incidents.
{
points: []*cloudmonitoring.Point{
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896102, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896101, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896100, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
},
expectedIncidentData: []incidentData{},
},
// One warning incident.
{
points: []*cloudmonitoring.Point{
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896103, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896102, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 3000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896101, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 3000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896100, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
},
expectedIncidentData: []incidentData{
incidentData{
Start: 1429896101,
Duration: 2,
Status: serviceStatusWarning,
},
},
},
// One warning incident and one critical incident.
{
points: []*cloudmonitoring.Point{
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896104, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896103, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 3000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896102, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 3000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896101, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 5000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896100, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
},
expectedIncidentData: []incidentData{
incidentData{
Start: 1429896101,
Duration: 1,
Status: serviceStatusDown,
},
incidentData{
Start: 1429896102,
Duration: 2,
Status: serviceStatusWarning,
},
},
},
// One warning incident at the beginning and one critical incident at the end.
{
points: []*cloudmonitoring.Point{
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896103, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 3000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896102, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 3000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896101, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 1000,
},
},
&cloudmonitoring.Point{
Interval: &cloudmonitoring.TimeInterval{
StartTime: time.Unix(1429896100, 0).Format(time.RFC3339),
},
Value: &cloudmonitoring.TypedValue{
DoubleValue: 5000,
},
},
},
expectedIncidentData: []incidentData{
incidentData{
Start: 1429896100,
Duration: 1,
Status: serviceStatusDown,
},
incidentData{
Start: 1429896102,
Duration: 1,
Status: serviceStatusWarning,
},
},
},
}
for index, test := range testCases {
got, err := calcIncidents(test.points)
if err != nil {
t.Fatalf("index #%d: want no errors, got: %v", index, err)
}
if expected := test.expectedIncidentData; !reflect.DeepEqual(got, expected) {
t.Fatalf("index #%d: want: %#v, got: %#v", index, expected, got)
}
}
}
func TestOverThresholdFor(t *testing.T) {
testCases := []struct {
timestamps []int64
values []float64
threshold float64
holdMinutes int
expectedResult bool
}{
// Not over threshold.
{
timestamps: []int64{0, 60, 120, 180, 240, 300, 360},
values: []float64{0, 0, 0, 0, 0, 0, 0},
threshold: 100,
holdMinutes: 5,
expectedResult: false,
},
// Over threshold, but not long enough.
{
timestamps: []int64{0, 60, 120, 180, 240, 300, 360},
values: []float64{0, 0, 0, 0, 0, 200, 200},
threshold: 100,
holdMinutes: 5,
expectedResult: false,
},
// Over threshold and long enough.
{
timestamps: []int64{0, 60, 120, 180, 240, 300, 360},
values: []float64{0, 200, 200, 200, 200, 200, 200},
threshold: 100,
holdMinutes: 5,
expectedResult: true,
},
}
for _, test := range testCases {
got := overThresholdFor(test.timestamps, test.values, test.threshold, test.holdMinutes)
if got != test.expectedResult {
t.Fatalf("want %v, got %v", test.expectedResult, got)
}
}
}
func TestAggregateMetricData(t *testing.T) {
aggData := map[string]*aggMetricData{}
testSteps := []struct {
metric *metricData
expectedAggData map[string]*aggMetricData
}{
{
metric: &metricData{
Name: "metric1",
HistoryTimestamps: []int64{1, 2, 3},
HistoryValues: []float64{100.0, 200.0, 300.0},
},
expectedAggData: map[string]*aggMetricData{
"metric1": &aggMetricData{
TimestampsToValues: map[int64][]float64{
1: []float64{100.0},
2: []float64{200.0},
3: []float64{300.0},
},
},
},
},
{
metric: &metricData{
Name: "metric1",
HistoryTimestamps: []int64{1, 2},
HistoryValues: []float64{101.0, 201.0},
},
expectedAggData: map[string]*aggMetricData{
"metric1": &aggMetricData{
TimestampsToValues: map[int64][]float64{
1: []float64{100.0, 101.0},
2: []float64{200.0, 201.0},
3: []float64{300.0},
},
},
},
},
{
metric: &metricData{
Name: "metric2",
HistoryTimestamps: []int64{4, 5, 6},
HistoryValues: []float64{10.0, 20.0, 30.0},
},
expectedAggData: map[string]*aggMetricData{
"metric1": &aggMetricData{
TimestampsToValues: map[int64][]float64{
1: []float64{100.0, 101.0},
2: []float64{200.0, 201.0},
3: []float64{300.0},
},
},
"metric2": &aggMetricData{
TimestampsToValues: map[int64][]float64{
4: []float64{10.0},
5: []float64{20.0},
6: []float64{30.0},
},
},
},
},
}
for _, test := range testSteps {
aggregateMetricData(aggData, test.metric)
if got, want := aggData, test.expectedAggData; !reflect.DeepEqual(got, want) {
t.Fatalf("want %v, got %v", want, got)
}
}
}
func TestCalculateMaxAndAverageData(t *testing.T) {
aggData := map[string]*aggMetricData{
"metric1": &aggMetricData{
TimestampsToValues: map[int64][]float64{
1: []float64{100.0, 200.0},
2: []float64{200.0, 300.0},
3: []float64{300.0},
},
},
"metric2": &aggMetricData{
TimestampsToValues: map[int64][]float64{
4: []float64{10.0},
},
},
}
gotMaxData, gotMaxRangeData, gotAverageData, gotAverageRangeData := calculateMaxAndAverageData(aggData, "zone1")
expectedMaxData := map[string]*metricData{
"metric1": &metricData{
ZoneName: "zone1",
Name: "metric1",
CurrentValue: 300.0,
MinTime: 1,
MaxTime: 3,
MinValue: 200.0,
MaxValue: 300.0,
HistoryTimestamps: []int64{1, 2, 3},
HistoryValues: []float64{200.0, 300.0, 300.0},
Threshold: -1,
Healthy: true,
},
"metric2": &metricData{
ZoneName: "zone1",
Name: "metric2",
CurrentValue: 10.0,
MinTime: 4,
MaxTime: 4,
MinValue: 10.0,
MaxValue: 10.0,
HistoryTimestamps: []int64{4},
HistoryValues: []float64{10.0},
Threshold: -1,
Healthy: true,
},
}
expectedMaxRangeData := &rangeData{
MinTime: 1,
MaxTime: 4,
}
expectedAverageData := map[string]*metricData{
"metric1": &metricData{
ZoneName: "zone1",
Name: "metric1",
CurrentValue: 300.0,
MinTime: 1,
MaxTime: 3,
MinValue: 150.0,
MaxValue: 300.0,
HistoryTimestamps: []int64{1, 2, 3},
HistoryValues: []float64{150.0, 250.0, 300.0},
Threshold: -1,
Healthy: true,
},
"metric2": &metricData{
ZoneName: "zone1",
Name: "metric2",
CurrentValue: 10.0,
MinTime: 4,
MaxTime: 4,
MinValue: 10.0,
MaxValue: 10.0,
HistoryTimestamps: []int64{4},
HistoryValues: []float64{10.0},
Threshold: -1,
Healthy: true,
},
}
expectedAverageRangeData := &rangeData{
MinTime: 1,
MaxTime: 4,
}
if !reflect.DeepEqual(gotMaxData, expectedMaxData) {
t.Fatalf("want %#v, got %#v", expectedMaxData, gotMaxData)
}
if !reflect.DeepEqual(gotMaxRangeData, expectedMaxRangeData) {
t.Fatalf("want %#v, got %#v", expectedMaxRangeData, gotMaxRangeData)
}
if !reflect.DeepEqual(gotAverageData, expectedAverageData) {
t.Fatalf("want %#v, got %#v", expectedAverageData, gotAverageData)
}
if !reflect.DeepEqual(gotAverageRangeData, expectedAverageRangeData) {
t.Fatalf("want %#v, got %#v", expectedAverageRangeData, gotAverageRangeData)
}
}