70 lines
2.6 KiB
Go
70 lines
2.6 KiB
Go
package notify
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"testing"
|
|
)
|
|
|
|
// fix-3: app_start_failed fires ONCE per running→down transition. down→down cycles are silent (the
|
|
// anti-spam guarantee); down→running clears the tracker so a later re-failure re-notifies.
|
|
func TestNotifyAppStartFailures_OneEventPerTransition(t *testing.T) {
|
|
n := New("http://hub", "key", "cust", nil, log.New(io.Discard, "", 0), false)
|
|
var events []string
|
|
n.pushFn = func(eventType, _, msg string, _ interface{}) {
|
|
if eventType == "app_start_failed" {
|
|
events = append(events, msg)
|
|
}
|
|
}
|
|
|
|
up := []AppRunState{{Name: "radarr", DisplayName: "Radarr", Down: false}}
|
|
down := []AppRunState{{Name: "radarr", DisplayName: "Radarr", Down: true}}
|
|
|
|
// running → down: exactly one event.
|
|
n.NotifyAppStartFailures(up)
|
|
n.NotifyAppStartFailures(down)
|
|
if len(events) != 1 {
|
|
t.Fatalf("running→down must fire exactly one event, got %d: %v", len(events), events)
|
|
}
|
|
// down → down (two more cycles): SILENT (companion: drop the n.appDown tracking → fires each cycle → fail).
|
|
n.NotifyAppStartFailures(down)
|
|
n.NotifyAppStartFailures(down)
|
|
if len(events) != 1 {
|
|
t.Fatalf("down→down must be silent, got %d events: %v", len(events), events)
|
|
}
|
|
// down → running → down: a fresh transition re-notifies.
|
|
n.NotifyAppStartFailures(up)
|
|
n.NotifyAppStartFailures(down)
|
|
if len(events) != 2 {
|
|
t.Fatalf("a fresh down transition must re-notify, got %d: %v", len(events), events)
|
|
}
|
|
}
|
|
|
|
// An app that is down on the FIRST evaluation (the F11 dead-at-boot case, after the boot grace) still
|
|
// fires — it is a running→down transition from the tracker's empty initial state.
|
|
func TestNotifyAppStartFailures_FirstSeenDownFires(t *testing.T) {
|
|
n := New("http://hub", "key", "cust", nil, log.New(io.Discard, "", 0), false)
|
|
var count int
|
|
n.pushFn = func(eventType, _, _ string, _ interface{}) {
|
|
if eventType == "app_start_failed" {
|
|
count++
|
|
}
|
|
}
|
|
n.NotifyAppStartFailures([]AppRunState{{Name: "jellyfin", DisplayName: "Jellyfin", Down: true}})
|
|
if count != 1 {
|
|
t.Fatalf("a first-seen dead app (dead-at-boot) must fire once, got %d", count)
|
|
}
|
|
}
|
|
|
|
// A deployed app that is up never fires.
|
|
func TestNotifyAppStartFailures_HealthyNeverFires(t *testing.T) {
|
|
n := New("http://hub", "key", "cust", nil, log.New(io.Discard, "", 0), false)
|
|
var count int
|
|
n.pushFn = func(string, string, string, interface{}) { count++ }
|
|
n.NotifyAppStartFailures([]AppRunState{{Name: "radarr", Down: false}})
|
|
n.NotifyAppStartFailures([]AppRunState{{Name: "radarr", Down: false}})
|
|
if count != 0 {
|
|
t.Fatalf("a healthy app must never fire, got %d", count)
|
|
}
|
|
}
|