diff --git a/model/alert.go b/model/alert.go index 178fdbaf..21dcf867 100644 --- a/model/alert.go +++ b/model/alert.go @@ -75,7 +75,12 @@ func (a *Alert) ResolvedAt(ts time.Time) bool { // Status returns the status of the alert. func (a *Alert) Status() AlertStatus { - if a.Resolved() { + return a.StatusAt(time.Now()) +} + +// StatusAt returns the status of the alert before the given timestamp. +func (a *Alert) StatusAt(ts time.Time) AlertStatus { + if a.ResolvedAt(ts) { return AlertResolved } return AlertFiring @@ -127,6 +132,17 @@ func (as Alerts) HasFiring() bool { return false } +// HasFiringAt returns true iff one of the alerts is not resolved +// at the time ts. +func (as Alerts) HasFiringAt(ts time.Time) bool { + for _, a := range as { + if !a.ResolvedAt(ts) { + return true + } + } + return false +} + // Status returns StatusFiring iff at least one of the alerts is firing. func (as Alerts) Status() AlertStatus { if as.HasFiring() { @@ -134,3 +150,12 @@ func (as Alerts) Status() AlertStatus { } return AlertResolved } + +// StatusAt returns StatusFiring iff at least one of the alerts is firing +// at the time ts. +func (as Alerts) StatusAt(ts time.Time) AlertStatus { + if as.HasFiringAt(ts) { + return AlertFiring + } + return AlertResolved +} diff --git a/model/alert_test.go b/model/alert_test.go index c140b15e..2a8d7bba 100644 --- a/model/alert_test.go +++ b/model/alert_test.go @@ -133,8 +133,8 @@ func TestAlert(t *testing.T) { t.Errorf("expected %s, but got %s", expected, actual) } - actualStatus := string(alert.Status()) - expectedStatus := "firing" + actualStatus := alert.Status() + expectedStatus := AlertStatus("firing") if actualStatus != expectedStatus { t.Errorf("expected alertStatus %s, but got %s", expectedStatus, actualStatus) @@ -150,6 +150,10 @@ func TestAlert(t *testing.T) { EndsAt: ts2, } + if !alert.Resolved() { + t.Error("expected alert to be resolved, but it was not") + } + actual = fmt.Sprint(alert) expected = "[d181d0f][resolved]" @@ -157,12 +161,44 @@ func TestAlert(t *testing.T) { t.Errorf("expected %s, but got %s", expected, actual) } - actualStatus = string(alert.Status()) + actualStatus = alert.Status() expectedStatus = "resolved" if actualStatus != expectedStatus { t.Errorf("expected alertStatus %s, but got %s", expectedStatus, actualStatus) } + + // Verifying that ResolvedAt works for different times + if alert.ResolvedAt(ts1) { + t.Error("unexpected alert was resolved at start time") + } + if alert.ResolvedAt(ts2.Add(-time.Millisecond)) { + t.Error("unexpected alert was resolved before it ended") + } + if !alert.ResolvedAt(ts2) { + t.Error("expected alert to be resolved at end time") + } + if !alert.ResolvedAt(ts2.Add(time.Millisecond)) { + t.Error("expected alert to be resolved after it ended") + } + + // Verifying that StatusAt works for different times + actualStatus = alert.StatusAt(ts1) + if actualStatus != "firing" { + t.Errorf("expected alert to be firing at start time, but got %s", actualStatus) + } + actualStatus = alert.StatusAt(ts1.Add(-time.Millisecond)) + if actualStatus != "firing" { + t.Errorf("expected alert to be firing before it ended, but got %s", actualStatus) + } + actualStatus = alert.StatusAt(ts2) + if actualStatus != "resolved" { + t.Errorf("expected alert to be resolved at end time, but got %s", actualStatus) + } + actualStatus = alert.StatusAt(ts2.Add(time.Millisecond)) + if actualStatus != "resolved" { + t.Errorf("expected alert to be resolved after it ended, but got %s", actualStatus) + } } func TestSortAlerts(t *testing.T) { @@ -228,18 +264,19 @@ func TestSortAlerts(t *testing.T) { } func TestAlertsStatus(t *testing.T) { + ts := time.Now() firingAlerts := Alerts{ { Labels: LabelSet{ "foo": "bar", }, - StartsAt: time.Now(), + StartsAt: ts, }, { Labels: LabelSet{ "bar": "baz", }, - StartsAt: time.Now(), + StartsAt: ts, }, } @@ -250,7 +287,12 @@ func TestAlertsStatus(t *testing.T) { t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) } - ts := time.Now() + actualStatus = firingAlerts.StatusAt(ts) + if actualStatus != expectedStatus { + t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) + } + + ts = time.Now() resolvedAlerts := Alerts{ { Labels: LabelSet{ @@ -270,7 +312,48 @@ func TestAlertsStatus(t *testing.T) { actualStatus = resolvedAlerts.Status() expectedStatus = AlertResolved + if actualStatus != expectedStatus { + t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) + } + + actualStatus = resolvedAlerts.StatusAt(ts) + expectedStatus = AlertResolved + if actualStatus != expectedStatus { + t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) + } + ts = time.Now() + mixedAlerts := Alerts{ + { + Labels: LabelSet{ + "foo": "bar", + }, + StartsAt: ts.Add(-1 * time.Minute), + EndsAt: ts.Add(5 * time.Minute), + }, + { + Labels: LabelSet{ + "bar": "baz", + }, + StartsAt: ts.Add(-1 * time.Minute), + EndsAt: ts, + }, + } + + actualStatus = mixedAlerts.Status() + expectedStatus = AlertFiring + if actualStatus != expectedStatus { + t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) + } + + actualStatus = mixedAlerts.StatusAt(ts) + expectedStatus = AlertFiring + if actualStatus != expectedStatus { + t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) + } + + actualStatus = mixedAlerts.StatusAt(ts.Add(5 * time.Minute)) + expectedStatus = AlertResolved if actualStatus != expectedStatus { t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) }