mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 18:59:10 +08:00
fix: handle maintenance windows that cross day boundaries (#7494)
This commit is contained in:
parent
7972261237
commit
f0a4c37073
@ -3,8 +3,6 @@ package rules
|
|||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -84,6 +82,16 @@ const (
|
|||||||
RepeatOnSaturday RepeatOn = "saturday"
|
RepeatOnSaturday RepeatOn = "saturday"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var RepeatOnAllMap = map[RepeatOn]time.Weekday{
|
||||||
|
RepeatOnSunday: time.Sunday,
|
||||||
|
RepeatOnMonday: time.Monday,
|
||||||
|
RepeatOnTuesday: time.Tuesday,
|
||||||
|
RepeatOnWednesday: time.Wednesday,
|
||||||
|
RepeatOnThursday: time.Thursday,
|
||||||
|
RepeatOnFriday: time.Friday,
|
||||||
|
RepeatOnSaturday: time.Saturday,
|
||||||
|
}
|
||||||
|
|
||||||
type Recurrence struct {
|
type Recurrence struct {
|
||||||
StartTime time.Time `json:"startTime"`
|
StartTime time.Time `json:"startTime"`
|
||||||
EndTime *time.Time `json:"endTime,omitempty"`
|
EndTime *time.Time `json:"endTime,omitempty"`
|
||||||
@ -211,7 +219,7 @@ func (s *Schedule) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *PlannedMaintenance) shouldSkip(ruleID string, now time.Time) bool {
|
func (m *PlannedMaintenance) shouldSkip(ruleID string, now time.Time) bool {
|
||||||
|
// Check if the alert ID is in the maintenance window
|
||||||
found := false
|
found := false
|
||||||
if m.AlertIds != nil {
|
if m.AlertIds != nil {
|
||||||
for _, alertID := range *m.AlertIds {
|
for _, alertID := range *m.AlertIds {
|
||||||
@ -227,97 +235,162 @@ func (m *PlannedMaintenance) shouldSkip(ruleID string, now time.Time) bool {
|
|||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if found {
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
zap.L().Info("alert found in maintenance", zap.String("alert", ruleID), zap.Any("maintenance", m.Name))
|
zap.L().Info("alert found in maintenance", zap.String("alert", ruleID), zap.String("maintenance", m.Name))
|
||||||
|
|
||||||
// If alert is found, we check if it should be skipped based on the schedule
|
// If alert is found, we check if it should be skipped based on the schedule
|
||||||
// If it should be skipped, we return true
|
loc, err := time.LoadLocation(m.Schedule.Timezone)
|
||||||
// If it should not be skipped, we return false
|
if err != nil {
|
||||||
|
zap.L().Error("Error loading location", zap.String("timezone", m.Schedule.Timezone), zap.Error(err))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// fixed schedule
|
currentTime := now.In(loc)
|
||||||
if !m.Schedule.StartTime.IsZero() && !m.Schedule.EndTime.IsZero() {
|
|
||||||
// if the current time in the timezone is between the start and end time
|
|
||||||
loc, err := time.LoadLocation(m.Schedule.Timezone)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("Error loading location", zap.String("timezone", m.Schedule.Timezone), zap.Error(err))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTime := now.In(loc)
|
// fixed schedule
|
||||||
zap.L().Info("checking fixed schedule", zap.Any("rule", ruleID), zap.String("maintenance", m.Name), zap.Time("currentTime", currentTime), zap.Time("startTime", m.Schedule.StartTime), zap.Time("endTime", m.Schedule.EndTime))
|
if !m.Schedule.StartTime.IsZero() && !m.Schedule.EndTime.IsZero() {
|
||||||
if currentTime.After(m.Schedule.StartTime) && currentTime.Before(m.Schedule.EndTime) {
|
zap.L().Info("checking fixed schedule",
|
||||||
return true
|
zap.String("rule", ruleID),
|
||||||
}
|
zap.String("maintenance", m.Name),
|
||||||
}
|
zap.Time("currentTime", currentTime),
|
||||||
|
zap.Time("startTime", m.Schedule.StartTime),
|
||||||
|
zap.Time("endTime", m.Schedule.EndTime))
|
||||||
|
|
||||||
// recurring schedule
|
startTime := m.Schedule.StartTime.In(loc)
|
||||||
if m.Schedule.Recurrence != nil {
|
endTime := m.Schedule.EndTime.In(loc)
|
||||||
zap.L().Info("evaluating recurrence schedule")
|
if currentTime.Equal(startTime) || currentTime.Equal(endTime) ||
|
||||||
start := m.Schedule.Recurrence.StartTime
|
(currentTime.After(startTime) && currentTime.Before(endTime)) {
|
||||||
end := m.Schedule.Recurrence.StartTime.Add(time.Duration(m.Schedule.Recurrence.Duration))
|
return true
|
||||||
// if the current time in the timezone is between the start and end time
|
|
||||||
loc, err := time.LoadLocation(m.Schedule.Timezone)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("Error loading location", zap.String("timezone", m.Schedule.Timezone), zap.Error(err))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
currentTime := now.In(loc)
|
|
||||||
|
|
||||||
zap.L().Info("checking recurring schedule", zap.Any("rule", ruleID), zap.String("maintenance", m.Name), zap.Time("currentTime", currentTime), zap.Time("startTime", start), zap.Time("endTime", end))
|
|
||||||
|
|
||||||
// make sure the start time is not after the current time
|
|
||||||
if currentTime.Before(start.In(loc)) {
|
|
||||||
zap.L().Info("current time is before start time", zap.Any("rule", ruleID), zap.String("maintenance", m.Name), zap.Time("currentTime", currentTime), zap.Time("startTime", start.In(loc)))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var endTime time.Time
|
|
||||||
if m.Schedule.Recurrence.EndTime != nil {
|
|
||||||
endTime = *m.Schedule.Recurrence.EndTime
|
|
||||||
}
|
|
||||||
if !endTime.IsZero() && currentTime.After(endTime.In(loc)) {
|
|
||||||
zap.L().Info("current time is after end time", zap.Any("rule", ruleID), zap.String("maintenance", m.Name), zap.Time("currentTime", currentTime), zap.Time("endTime", end.In(loc)))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch m.Schedule.Recurrence.RepeatType {
|
|
||||||
case RepeatTypeDaily:
|
|
||||||
// take the hours and minutes from the start time and add them to the current time
|
|
||||||
startTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), start.Hour(), start.Minute(), 0, 0, loc)
|
|
||||||
endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), end.Hour(), end.Minute(), 0, 0, loc)
|
|
||||||
zap.L().Info("checking daily schedule", zap.Any("rule", ruleID), zap.String("maintenance", m.Name), zap.Time("currentTime", currentTime), zap.Time("startTime", startTime), zap.Time("endTime", endTime))
|
|
||||||
|
|
||||||
if currentTime.After(startTime) && currentTime.Before(endTime) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
case RepeatTypeWeekly:
|
|
||||||
// if the current time in the timezone is between the start and end time on the RepeatOn day
|
|
||||||
startTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), start.Hour(), start.Minute(), 0, 0, loc)
|
|
||||||
endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), end.Hour(), end.Minute(), 0, 0, loc)
|
|
||||||
zap.L().Info("checking weekly schedule", zap.Any("rule", ruleID), zap.String("maintenance", m.Name), zap.Time("currentTime", currentTime), zap.Time("startTime", startTime), zap.Time("endTime", endTime))
|
|
||||||
if currentTime.After(startTime) && currentTime.Before(endTime) {
|
|
||||||
if len(m.Schedule.Recurrence.RepeatOn) == 0 {
|
|
||||||
return true
|
|
||||||
} else if slices.Contains(m.Schedule.Recurrence.RepeatOn, RepeatOn(strings.ToLower(currentTime.Weekday().String()))) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case RepeatTypeMonthly:
|
|
||||||
// if the current time in the timezone is between the start and end time on the day of the current month
|
|
||||||
startTime := time.Date(currentTime.Year(), currentTime.Month(), start.Day(), start.Hour(), start.Minute(), 0, 0, loc)
|
|
||||||
endTime := time.Date(currentTime.Year(), currentTime.Month(), end.Day(), end.Hour(), end.Minute(), 0, 0, loc)
|
|
||||||
zap.L().Info("checking monthly schedule", zap.Any("rule", ruleID), zap.String("maintenance", m.Name), zap.Time("currentTime", currentTime), zap.Time("startTime", startTime), zap.Time("endTime", endTime))
|
|
||||||
if currentTime.After(startTime) && currentTime.Before(endTime) && currentTime.Day() == start.Day() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If alert is not found, we return false
|
|
||||||
|
// recurring schedule
|
||||||
|
if m.Schedule.Recurrence != nil {
|
||||||
|
start := m.Schedule.Recurrence.StartTime
|
||||||
|
duration := time.Duration(m.Schedule.Recurrence.Duration)
|
||||||
|
|
||||||
|
zap.L().Info("checking recurring schedule base info",
|
||||||
|
zap.String("rule", ruleID),
|
||||||
|
zap.String("maintenance", m.Name),
|
||||||
|
zap.Time("startTime", start),
|
||||||
|
zap.Duration("duration", duration))
|
||||||
|
|
||||||
|
// Make sure the recurrence has started
|
||||||
|
if currentTime.Before(start.In(loc)) {
|
||||||
|
zap.L().Info("current time is before recurrence start time",
|
||||||
|
zap.String("rule", ruleID),
|
||||||
|
zap.String("maintenance", m.Name))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if recurrence has expired
|
||||||
|
if m.Schedule.Recurrence.EndTime != nil {
|
||||||
|
endTime := *m.Schedule.Recurrence.EndTime
|
||||||
|
if !endTime.IsZero() && currentTime.After(endTime.In(loc)) {
|
||||||
|
zap.L().Info("current time is after recurrence end time",
|
||||||
|
zap.String("rule", ruleID),
|
||||||
|
zap.String("maintenance", m.Name))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m.Schedule.Recurrence.RepeatType {
|
||||||
|
case RepeatTypeDaily:
|
||||||
|
return m.checkDaily(currentTime, m.Schedule.Recurrence, loc)
|
||||||
|
case RepeatTypeWeekly:
|
||||||
|
return m.checkWeekly(currentTime, m.Schedule.Recurrence, loc)
|
||||||
|
case RepeatTypeMonthly:
|
||||||
|
return m.checkMonthly(currentTime, m.Schedule.Recurrence, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkDaily rebases the recurrence start to today (or yesterday if needed)
|
||||||
|
// and returns true if currentTime is within [candidate, candidate+Duration].
|
||||||
|
func (m *PlannedMaintenance) checkDaily(currentTime time.Time, rec *Recurrence, loc *time.Location) bool {
|
||||||
|
candidate := time.Date(
|
||||||
|
currentTime.Year(), currentTime.Month(), currentTime.Day(),
|
||||||
|
rec.StartTime.Hour(), rec.StartTime.Minute(), 0, 0,
|
||||||
|
loc,
|
||||||
|
)
|
||||||
|
if candidate.After(currentTime) {
|
||||||
|
candidate = candidate.AddDate(0, 0, -1)
|
||||||
|
}
|
||||||
|
return currentTime.Sub(candidate) <= time.Duration(rec.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkWeekly finds the most recent allowed occurrence by rebasing the recurrence’s
|
||||||
|
// time-of-day onto the allowed weekday. It does this for each allowed day and returns true
|
||||||
|
// if the current time falls within the candidate window.
|
||||||
|
func (m *PlannedMaintenance) checkWeekly(currentTime time.Time, rec *Recurrence, loc *time.Location) bool {
|
||||||
|
// If no days specified, treat as every day (like daily).
|
||||||
|
if len(rec.RepeatOn) == 0 {
|
||||||
|
return m.checkDaily(currentTime, rec, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, day := range rec.RepeatOn {
|
||||||
|
allowedDay, ok := RepeatOnAllMap[day]
|
||||||
|
if !ok {
|
||||||
|
continue // skip invalid days
|
||||||
|
}
|
||||||
|
// Compute the day difference: allowedDay - current weekday.
|
||||||
|
delta := int(allowedDay) - int(currentTime.Weekday())
|
||||||
|
// Build a candidate occurrence by rebasing today's date to the allowed weekday.
|
||||||
|
candidate := time.Date(
|
||||||
|
currentTime.Year(), currentTime.Month(), currentTime.Day(),
|
||||||
|
rec.StartTime.Hour(), rec.StartTime.Minute(), 0, 0,
|
||||||
|
loc,
|
||||||
|
).AddDate(0, 0, delta)
|
||||||
|
// If the candidate is in the future, subtract 7 days.
|
||||||
|
if candidate.After(currentTime) {
|
||||||
|
candidate = candidate.AddDate(0, 0, -7)
|
||||||
|
}
|
||||||
|
if currentTime.Sub(candidate) <= time.Duration(rec.Duration) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkMonthly rebases the candidate occurrence using the recurrence's day-of-month.
|
||||||
|
// If the candidate for the current month is in the future, it uses the previous month.
|
||||||
|
func (m *PlannedMaintenance) checkMonthly(currentTime time.Time, rec *Recurrence, loc *time.Location) bool {
|
||||||
|
refDay := rec.StartTime.Day()
|
||||||
|
year, month, _ := currentTime.Date()
|
||||||
|
lastDay := time.Date(year, month+1, 0, 0, 0, 0, 0, loc).Day()
|
||||||
|
day := refDay
|
||||||
|
if refDay > lastDay {
|
||||||
|
day = lastDay
|
||||||
|
}
|
||||||
|
candidate := time.Date(year, month, day,
|
||||||
|
rec.StartTime.Hour(), rec.StartTime.Minute(), rec.StartTime.Second(), rec.StartTime.Nanosecond(),
|
||||||
|
loc,
|
||||||
|
)
|
||||||
|
if candidate.After(currentTime) {
|
||||||
|
// Use previous month.
|
||||||
|
candidate = candidate.AddDate(0, -1, 0)
|
||||||
|
y, m, _ := candidate.Date()
|
||||||
|
lastDayPrev := time.Date(y, m+1, 0, 0, 0, 0, 0, loc).Day()
|
||||||
|
if refDay > lastDayPrev {
|
||||||
|
candidate = time.Date(y, m, lastDayPrev,
|
||||||
|
rec.StartTime.Hour(), rec.StartTime.Minute(), rec.StartTime.Second(), rec.StartTime.Nanosecond(),
|
||||||
|
loc,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
candidate = time.Date(y, m, refDay,
|
||||||
|
rec.StartTime.Hour(), rec.StartTime.Minute(), rec.StartTime.Second(), rec.StartTime.Nanosecond(),
|
||||||
|
loc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentTime.Sub(candidate) <= time.Duration(rec.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *PlannedMaintenance) IsActive(now time.Time) bool {
|
func (m *PlannedMaintenance) IsActive(now time.Time) bool {
|
||||||
ruleID := "maintenance"
|
ruleID := "maintenance"
|
||||||
if m.AlertIds != nil && len(*m.AlertIds) > 0 {
|
if m.AlertIds != nil && len(*m.AlertIds) > 0 {
|
||||||
@ -327,7 +400,14 @@ func (m *PlannedMaintenance) IsActive(now time.Time) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *PlannedMaintenance) IsUpcoming() bool {
|
func (m *PlannedMaintenance) IsUpcoming() bool {
|
||||||
now := time.Now().In(time.FixedZone(m.Schedule.Timezone, 0))
|
loc, err := time.LoadLocation(m.Schedule.Timezone)
|
||||||
|
if err != nil {
|
||||||
|
// handle error appropriately, for example log and return false or fallback to UTC
|
||||||
|
zap.L().Error("Error loading timezone", zap.String("timezone", m.Schedule.Timezone), zap.Error(err))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
now := time.Now().In(loc)
|
||||||
|
|
||||||
if !m.Schedule.StartTime.IsZero() && !m.Schedule.EndTime.IsZero() {
|
if !m.Schedule.StartTime.IsZero() && !m.Schedule.EndTime.IsZero() {
|
||||||
return now.Before(m.Schedule.StartTime)
|
return now.Before(m.Schedule.StartTime)
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,404 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Helper function to create a time pointer
|
||||||
|
func timePtr(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldSkipMaintenance(t *testing.T) {
|
func TestShouldSkipMaintenance(t *testing.T) {
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
maintenance *PlannedMaintenance
|
maintenance *PlannedMaintenance
|
||||||
ts time.Time
|
ts time.Time
|
||||||
expected bool
|
skip bool
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "only-on-saturday",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "Europe/London",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2025, 3, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 24),
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnMonday, RepeatOnTuesday, RepeatOnWednesday, RepeatOnThursday, RepeatOnFriday, RepeatOnSunday},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2025, 3, 20, 12, 0, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Testing weekly recurrence with midnight crossing
|
||||||
|
{
|
||||||
|
name: "weekly-across-midnight-previous-day",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 4, 1, 22, 0, 0, 0, time.UTC), // Monday 22:00
|
||||||
|
Duration: Duration(time.Hour * 4), // Until Tuesday 02:00
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnMonday}, // Only Monday
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 2, 1, 30, 0, 0, time.UTC), // Tuesday 01:30
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Testing weekly recurrence with midnight crossing
|
||||||
|
{
|
||||||
|
name: "weekly-across-midnight-previous-day",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 4, 1, 22, 0, 0, 0, time.UTC), // Monday 22:00
|
||||||
|
Duration: Duration(time.Hour * 4), // Until Tuesday 02:00
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnMonday}, // Only Monday
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 23, 1, 30, 0, 0, time.UTC), // Tuesday 01:30
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Testing weekly recurrence with multi day duration
|
||||||
|
{
|
||||||
|
name: "weekly-across-midnight-previous-day",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 4, 1, 22, 0, 0, 0, time.UTC), // Monday 22:00
|
||||||
|
Duration: Duration(time.Hour * 52), // Until Thursday 02:00
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnMonday}, // Only Monday
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 25, 1, 30, 0, 0, time.UTC), // Tuesday 01:30
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Weekly recurrence where the previous day is not in RepeatOn
|
||||||
|
{
|
||||||
|
name: "weekly-across-midnight-previous-day-not-in-repeaton",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 4, 2, 22, 0, 0, 0, time.UTC), // Tuesday 22:00
|
||||||
|
Duration: Duration(time.Hour * 4), // Until Wednesday 02:00
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnTuesday}, // Only Tuesday
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 3, 1, 30, 0, 0, time.UTC), // Wednesday 01:30
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Daily recurrence with midnight crossing
|
||||||
|
{
|
||||||
|
name: "daily-maintenance-across-midnight",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 23, 0, 0, 0, time.UTC), // 23:00
|
||||||
|
Duration: Duration(time.Hour * 2), // Until 01:00 next day
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 2, 0, 30, 0, 0, time.UTC), // 00:30 next day
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Exactly at start time boundary
|
||||||
|
{
|
||||||
|
name: "at-start-time-boundary",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), // Exactly at start time
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Exactly at end time boundary
|
||||||
|
{
|
||||||
|
name: "at-end-time-boundary",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), // Exactly at end time
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Monthly maintenance with multi-day duration
|
||||||
|
{
|
||||||
|
name: "monthly-multi-day-duration",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 28, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 72), // 3 days
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 30, 12, 30, 0, 0, time.UTC), // Within the 3-day window
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Weekly maintenance with multi-day duration
|
||||||
|
{
|
||||||
|
name: "weekly-multi-day-duration",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 28, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 72), // 3 days
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnSunday},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 30, 12, 30, 0, 0, time.UTC), // Within the 3-day window
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Monthly maintenance that crosses to next month
|
||||||
|
{
|
||||||
|
name: "monthly-crosses-to-next-month",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 30, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 48), // 2 days, crosses to Feb 1
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 2, 1, 11, 0, 0, 0, time.UTC), // Feb 1, 11:00
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Different timezone tests
|
||||||
|
{
|
||||||
|
name: "timezone-offset-test",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "America/New_York", // UTC-5 or UTC-4 depending on DST
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 22, 0, 0, 0, time.FixedZone("America/New_York", -5*3600)),
|
||||||
|
Duration: Duration(time.Hour * 4),
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 2, 3, 30, 0, 0, time.UTC), // 22:30 NY time on Jan 1
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Test negative case - time well outside window
|
||||||
|
{
|
||||||
|
name: "daily-maintenance-time-outside-window",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 1, 16, 0, 0, 0, time.UTC), // 4 hours after start, 2 hours after end
|
||||||
|
skip: false,
|
||||||
|
},
|
||||||
|
// Test for recurring maintenance with an end date that is before the current time
|
||||||
|
{
|
||||||
|
name: "recurring-maintenance-with-past-end-date",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC),
|
||||||
|
EndTime: timePtr(time.Date(2024, 1, 10, 12, 0, 0, 0, time.UTC)),
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 15, 12, 30, 0, 0, time.UTC), // After the end date
|
||||||
|
skip: false,
|
||||||
|
},
|
||||||
|
// Monthly recurring maintenance spanning end of month into beginning of next month
|
||||||
|
{
|
||||||
|
name: "monthly-maintenance-spans-month-end",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 3, 31, 22, 0, 0, 0, time.UTC), // March 31, 22:00
|
||||||
|
Duration: Duration(time.Hour * 6), // Until April 1, 04:00
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 1, 2, 0, 0, 0, time.UTC), // April 1, 02:00
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// Test for RepeatOn with empty array (should apply to all days)
|
||||||
|
{
|
||||||
|
name: "weekly-empty-repeaton",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 4, 1, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{}, // Empty - should apply to all days
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 7, 12, 30, 0, 0, time.UTC), // Sunday
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
// February has fewer days than January - test the edge case when maintenance is on 31st
|
||||||
|
{
|
||||||
|
name: "monthly-maintenance-february-fewer-days",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 31, 12, 0, 0, 0, time.UTC), // January 31st
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 2, 28, 12, 30, 0, 0, time.UTC), // February 28th (not 29th in this test)
|
||||||
|
skip: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "daily-maintenance-crosses-midnight",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 23, 30, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 1), // Crosses to 00:30 next day
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 2, 0, 15, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "monthly-maintenance-crosses-month-end",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 31, 12, 0, 0, 0, time.UTC), // January 31st
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 2, 29, 12, 30, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "monthly-maintenance-crosses-month-end-and-duration-is-2-days",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 30, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 48), // 2 days duration
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 2, 1, 11, 0, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "weekly-maintenance-crosses-midnight",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 4, 1, 23, 0, 0, 0, time.UTC), // Monday 23:00
|
||||||
|
Duration: Duration(time.Hour * 2), // Until Tuesday 01:00
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnMonday}, // Only Monday
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 2, 0, 30, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "monthly-maintenance-crosses-month-end-and-duration-is-2-days",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 31, 12, 0, 0, 0, time.UTC), // January 31st
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 4, 30, 12, 30, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "daily-maintenance-crosses-midnight",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 1, 22, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 4), // Until 02:00 next day
|
||||||
|
RepeatType: RepeatTypeDaily,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 1, 2, 1, 0, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "monthly-maintenance-crosses-month-end-and-duration-is-2-hours",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "UTC",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2024, 1, 31, 12, 0, 0, 0, time.UTC),
|
||||||
|
Duration: Duration(time.Hour * 2),
|
||||||
|
RepeatType: RepeatTypeMonthly,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Date(2024, 2, 29, 12, 30, 0, 0, time.UTC),
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "fixed planned maintenance start <= ts <= end",
|
name: "fixed planned maintenance start <= ts <= end",
|
||||||
maintenance: &PlannedMaintenance{
|
maintenance: &PlannedMaintenance{
|
||||||
@ -22,8 +412,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
EndTime: time.Now().UTC().Add(time.Hour * 2),
|
EndTime: time.Now().UTC().Add(time.Hour * 2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Now().UTC(),
|
ts: time.Now().UTC(),
|
||||||
expected: true,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fixed planned maintenance start >= ts",
|
name: "fixed planned maintenance start >= ts",
|
||||||
@ -34,8 +424,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
EndTime: time.Now().UTC().Add(time.Hour * 2),
|
EndTime: time.Now().UTC().Add(time.Hour * 2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Now().UTC(),
|
ts: time.Now().UTC(),
|
||||||
expected: false,
|
skip: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fixed planned maintenance ts < start",
|
name: "fixed planned maintenance ts < start",
|
||||||
@ -46,8 +436,24 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
EndTime: time.Now().UTC().Add(time.Hour * 2),
|
EndTime: time.Now().UTC().Add(time.Hour * 2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Now().UTC().Add(-time.Hour),
|
ts: time.Now().UTC().Add(-time.Hour),
|
||||||
expected: false,
|
skip: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "recurring maintenance, repeat sunday, saturday, weekly for 24 hours, in Us/Eastern timezone",
|
||||||
|
maintenance: &PlannedMaintenance{
|
||||||
|
Schedule: &Schedule{
|
||||||
|
Timezone: "US/Eastern",
|
||||||
|
Recurrence: &Recurrence{
|
||||||
|
StartTime: time.Date(2025, 3, 29, 20, 0, 0, 0, time.FixedZone("US/Eastern", -4*3600)),
|
||||||
|
Duration: Duration(time.Hour * 24),
|
||||||
|
RepeatType: RepeatTypeWeekly,
|
||||||
|
RepeatOn: []RepeatOn{RepeatOnSunday, RepeatOnSaturday},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ts: time.Unix(1743343105, 0),
|
||||||
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat daily from 12:00 to 14:00",
|
name: "recurring maintenance, repeat daily from 12:00 to 14:00",
|
||||||
@ -61,8 +467,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 1, 1, 12, 10, 0, 0, time.UTC),
|
ts: time.Date(2024, 1, 1, 12, 10, 0, 0, time.UTC),
|
||||||
expected: true,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat daily from 12:00 to 14:00",
|
name: "recurring maintenance, repeat daily from 12:00 to 14:00",
|
||||||
@ -76,8 +482,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC),
|
ts: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC),
|
||||||
expected: false,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat daily from 12:00 to 14:00",
|
name: "recurring maintenance, repeat daily from 12:00 to 14:00",
|
||||||
@ -91,8 +497,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 04, 1, 12, 10, 0, 0, time.UTC),
|
ts: time.Date(2024, 04, 1, 12, 10, 0, 0, time.UTC),
|
||||||
expected: true,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
||||||
@ -107,8 +513,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 04, 15, 12, 10, 0, 0, time.UTC),
|
ts: time.Date(2024, 04, 15, 12, 10, 0, 0, time.UTC),
|
||||||
expected: true,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
||||||
@ -123,8 +529,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 04, 14, 12, 10, 0, 0, time.UTC), // 14th 04 is sunday
|
ts: time.Date(2024, 04, 14, 12, 10, 0, 0, time.UTC), // 14th 04 is sunday
|
||||||
expected: false,
|
skip: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
||||||
@ -139,8 +545,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 04, 16, 12, 10, 0, 0, time.UTC), // 16th 04 is tuesday
|
ts: time.Date(2024, 04, 16, 12, 10, 0, 0, time.UTC), // 16th 04 is tuesday
|
||||||
expected: false,
|
skip: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
||||||
@ -155,8 +561,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 05, 06, 12, 10, 0, 0, time.UTC),
|
ts: time.Date(2024, 05, 06, 12, 10, 0, 0, time.UTC),
|
||||||
expected: true,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
name: "recurring maintenance, repeat weekly on monday from 12:00 to 14:00",
|
||||||
@ -171,8 +577,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 05, 06, 14, 00, 0, 0, time.UTC),
|
ts: time.Date(2024, 05, 06, 14, 00, 0, 0, time.UTC),
|
||||||
expected: false,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat monthly on 4th from 12:00 to 14:00",
|
name: "recurring maintenance, repeat monthly on 4th from 12:00 to 14:00",
|
||||||
@ -186,8 +592,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 04, 04, 12, 10, 0, 0, time.UTC),
|
ts: time.Date(2024, 04, 04, 12, 10, 0, 0, time.UTC),
|
||||||
expected: true,
|
skip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat monthly on 4th from 12:00 to 14:00",
|
name: "recurring maintenance, repeat monthly on 4th from 12:00 to 14:00",
|
||||||
@ -201,8 +607,8 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 04, 04, 14, 10, 0, 0, time.UTC),
|
ts: time.Date(2024, 04, 04, 14, 10, 0, 0, time.UTC),
|
||||||
expected: false,
|
skip: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "recurring maintenance, repeat monthly on 4th from 12:00 to 14:00",
|
name: "recurring maintenance, repeat monthly on 4th from 12:00 to 14:00",
|
||||||
@ -216,15 +622,15 @@ func TestShouldSkipMaintenance(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ts: time.Date(2024, 05, 04, 12, 10, 0, 0, time.UTC),
|
ts: time.Date(2024, 05, 04, 12, 10, 0, 0, time.UTC),
|
||||||
expected: true,
|
skip: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for idx, c := range cases {
|
||||||
result := c.maintenance.shouldSkip(c.name, c.ts)
|
result := c.maintenance.shouldSkip(c.name, c.ts)
|
||||||
if result != c.expected {
|
if result != c.skip {
|
||||||
t.Errorf("expected %v, got %v", c.expected, result)
|
t.Errorf("skip %v, got %v, case:%d - %s", c.skip, result, idx, c.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user