Skip to content

Commit 2dc23c9

Browse files
Rewrite TestTimeActiveStage tests (#3795)
This commit rewrites the existing TestTimeActiveStage unit tests to have complete isolation between test cases. Before this change, each test case affected the state of its subsequent tests. The motivation behind this change is to make it easier to assert that alerts have been marked as muted. Signed-off-by: George Robinson <[email protected]>
1 parent 14cbe63 commit 2dc23c9

File tree

1 file changed

+89
-89
lines changed

1 file changed

+89
-89
lines changed

notify/notify_test.go

Lines changed: 89 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020
"io"
2121
"reflect"
22+
"strings"
2223
"testing"
2324
"time"
2425

@@ -936,97 +937,96 @@ func TestTimeMuteStage(t *testing.T) {
936937
}
937938

938939
func TestTimeActiveStage(t *testing.T) {
939-
// Route mutes alerts inside business hours if it is an active time interval
940-
muteIn := `
941-
---
942-
- weekdays: ['monday:friday']
943-
times:
944-
- start_time: '00:00'
945-
end_time: '09:00'
946-
- start_time: '17:00'
947-
end_time: '24:00'
948-
- weekdays: ['saturday', 'sunday']`
949-
950-
cases := []struct {
951-
fireTime string
952-
labels model.LabelSet
953-
shouldMute bool
954-
}{
955-
{
956-
// Friday during business hours
957-
fireTime: "01 Jan 21 09:00 +0000",
958-
labels: model.LabelSet{"mute": "me"},
959-
shouldMute: true,
960-
},
961-
{
962-
fireTime: "02 Dec 20 16:59 +0000",
963-
labels: model.LabelSet{"mute": "me"},
964-
shouldMute: true,
965-
},
966-
{
967-
// Tuesday before 5pm
968-
fireTime: "01 Dec 20 16:59 +0000",
969-
labels: model.LabelSet{"mute": "me"},
970-
shouldMute: true,
971-
},
972-
{
973-
// Saturday
974-
fireTime: "17 Oct 20 10:00 +0000",
975-
labels: model.LabelSet{"foo": "bar"},
976-
shouldMute: false,
977-
},
978-
{
979-
// Wednesday before 9am
980-
fireTime: "14 Oct 20 05:00 +0000",
981-
labels: model.LabelSet{"dont": "mute"},
982-
shouldMute: false,
983-
},
984-
}
985-
var intervals []timeinterval.TimeInterval
986-
err := yaml.Unmarshal([]byte(muteIn), &intervals)
940+
sydney, err := time.LoadLocation("Australia/Sydney")
987941
if err != nil {
988-
t.Fatalf("Couldn't unmarshal time interval %s", err)
989-
}
990-
m := map[string][]timeinterval.TimeInterval{"test": intervals}
991-
intervener := timeinterval.NewIntervener(m)
992-
metrics := NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{})
993-
stage := NewTimeActiveStage(intervener, metrics)
994-
995-
outAlerts := []*types.Alert{}
996-
nonMuteCount := 0
997-
for _, tc := range cases {
998-
now, err := time.Parse(time.RFC822Z, tc.fireTime)
999-
if err != nil {
1000-
t.Fatalf("Couldn't parse fire time %s %s", tc.fireTime, err)
1001-
}
1002-
// Count alerts with shouldMute == false and compare to ensure none are muted incorrectly
1003-
if !tc.shouldMute {
1004-
nonMuteCount++
1005-
}
1006-
a := model.Alert{Labels: tc.labels}
1007-
alerts := []*types.Alert{{Alert: a}}
1008-
ctx := context.Background()
1009-
ctx = WithNow(ctx, now)
1010-
ctx = WithActiveTimeIntervals(ctx, []string{"test"})
1011-
ctx = WithMuteTimeIntervals(ctx, []string{})
942+
t.Fatalf("Failed to load location Australia/Sydney: %s", err)
943+
}
944+
weekdays := map[string][]timeinterval.TimeInterval{
945+
"weekdays": {{
946+
Weekdays: []timeinterval.WeekdayRange{{
947+
InclusiveRange: timeinterval.InclusiveRange{
948+
Begin: 1, // Monday
949+
End: 5, // Friday
950+
},
951+
}},
952+
Times: []timeinterval.TimeRange{{
953+
StartMinute: 540, // 09:00
954+
EndMinute: 1020, // 17:00
955+
}},
956+
Location: &timeinterval.Location{Location: sydney},
957+
}},
958+
}
959+
960+
tests := []struct {
961+
name string
962+
intervals map[string][]timeinterval.TimeInterval
963+
now time.Time
964+
alerts []*types.Alert
965+
mutedBy []string
966+
}{{
967+
name: "Should be muted outside working hours",
968+
intervals: weekdays,
969+
now: time.Date(2024, 1, 1, 0, 0, 0, 0, sydney),
970+
alerts: []*types.Alert{{Alert: model.Alert{Labels: model.LabelSet{"foo": "bar"}}}},
971+
mutedBy: []string{"weekdays"},
972+
}, {
973+
name: "Should not be muted during workings hours",
974+
intervals: weekdays,
975+
now: time.Date(2024, 1, 1, 9, 0, 0, 0, sydney),
976+
alerts: []*types.Alert{{Alert: model.Alert{Labels: model.LabelSet{"foo": "bar"}}}},
977+
mutedBy: nil,
978+
}, {
979+
name: "Should be muted during weekends",
980+
intervals: weekdays,
981+
now: time.Date(2024, 1, 6, 10, 0, 0, 0, sydney),
982+
alerts: []*types.Alert{{Alert: model.Alert{Labels: model.LabelSet{"foo": "bar"}}}},
983+
mutedBy: []string{"weekdays"},
984+
}, {
985+
name: "Should be muted at 12pm UTC",
986+
intervals: weekdays,
987+
now: time.Date(2024, 1, 6, 10, 0, 0, 0, time.UTC),
988+
alerts: []*types.Alert{{Alert: model.Alert{Labels: model.LabelSet{"foo": "bar"}}}},
989+
mutedBy: []string{"weekdays"},
990+
}}
991+
992+
for _, test := range tests {
993+
t.Run(test.name, func(t *testing.T) {
994+
r := prometheus.NewRegistry()
995+
metrics := NewMetrics(r, featurecontrol.NoopFlags{})
996+
intervener := timeinterval.NewIntervener(test.intervals)
997+
st := NewTimeActiveStage(intervener, metrics)
998+
999+
// Get the names of all time intervals for the context.
1000+
activeTimeIntervalNames := make([]string, 0, len(test.intervals))
1001+
for name := range test.intervals {
1002+
activeTimeIntervalNames = append(activeTimeIntervalNames, name)
1003+
}
10121004

1013-
_, out, err := stage.Exec(ctx, log.NewNopLogger(), alerts...)
1014-
if err != nil {
1015-
t.Fatalf("Unexpected error in time mute stage %s", err)
1016-
}
1017-
outAlerts = append(outAlerts, out...)
1018-
}
1019-
for _, alert := range outAlerts {
1020-
if _, ok := alert.Alert.Labels["mute"]; ok {
1021-
t.Fatalf("Expected alert to be muted %+v", alert.Alert)
1022-
}
1023-
}
1024-
if len(outAlerts) != nonMuteCount {
1025-
t.Fatalf("Expected %d alerts after time mute stage but got %d", nonMuteCount, len(outAlerts))
1026-
}
1027-
suppressed := int(prom_testutil.ToFloat64(metrics.numNotificationSuppressedTotal))
1028-
if (len(cases) - nonMuteCount) != suppressed {
1029-
t.Fatalf("Expected %d alerts counted in suppressed metric but got %d", (len(cases) - nonMuteCount), suppressed)
1005+
ctx := context.Background()
1006+
ctx = WithNow(ctx, test.now)
1007+
ctx = WithActiveTimeIntervals(ctx, activeTimeIntervalNames)
1008+
ctx = WithMuteTimeIntervals(ctx, nil)
1009+
1010+
_, active, err := st.Exec(ctx, log.NewNopLogger(), test.alerts...)
1011+
require.NoError(t, err)
1012+
1013+
if len(test.mutedBy) == 0 {
1014+
// All alerts should be active.
1015+
require.Equal(t, len(test.alerts), len(active))
1016+
// The metric for total suppressed notifications should not
1017+
// have been incremented, which means it will not be collected.
1018+
require.NoError(t, prom_testutil.GatherAndCompare(r, strings.NewReader("")))
1019+
} else {
1020+
// All alerts should be muted.
1021+
require.Empty(t, active)
1022+
// Gets the metric for total suppressed notifications.
1023+
require.NoError(t, prom_testutil.GatherAndCompare(r, strings.NewReader(fmt.Sprintf(`
1024+
# HELP alertmanager_notifications_suppressed_total The total number of notifications suppressed for being silenced, inhibited, outside of active time intervals or within muted time intervals.
1025+
# TYPE alertmanager_notifications_suppressed_total counter
1026+
alertmanager_notifications_suppressed_total{reason="active_time_interval"} %d
1027+
`, len(test.alerts)))))
1028+
}
1029+
})
10301030
}
10311031
}
10321032

0 commit comments

Comments
 (0)