Files
2026-08-22 11:32:50 +12:00

138 lines
4.7 KiB
Go

package api
import (
"testing"
"time"
)
func TestEmbyHealthWaitsForTheThresholdBeforeReportingAnOutage(t *testing.T) {
var health embyHealth
start := time.Now().UTC()
health.begin(60*time.Second, start)
// One failed probe is a hiccup — a restart, a slow scan. A red bar across somebody's
// film for that is worse than saying nothing.
health.record(false, "", start.Add(time.Minute))
if !health.get().reachable {
t.Fatal("a single failure raised an outage")
}
health.record(false, "", start.Add(2*time.Minute))
state := health.get()
if state.reachable {
t.Fatal("two consecutive failures did not raise an outage")
}
if !state.since.Equal(start.Add(2 * time.Minute)) {
t.Errorf("since = %v, want the probe that crossed the threshold", state.since)
}
}
// The bar clears itself. One good answer is enough — the failure mode of clearing early
// is a bar that comes back, which is far better than one that outlives the outage.
func TestEmbyHealthRecoversOnTheFirstSuccess(t *testing.T) {
var health embyHealth
start := time.Now().UTC()
health.begin(60*time.Second, start)
health.record(false, "", start.Add(time.Minute))
health.record(false, "", start.Add(2*time.Minute))
health.record(true, "", start.Add(3*time.Minute))
state := health.get()
if !state.reachable {
t.Fatal("a successful probe did not clear the outage")
}
if !state.since.Equal(start.Add(3 * time.Minute)) {
t.Errorf("since = %v, want the recovery time", state.since)
}
}
// A failure that does not reach the threshold must not accumulate across a recovery, or a
// server with one flaky probe an hour eventually reports an outage that never happened.
func TestEmbyHealthForgetsIsolatedFailures(t *testing.T) {
var health embyHealth
start := time.Now().UTC()
health.begin(60*time.Second, start)
for minute := 1; minute <= 10; minute++ {
health.record(minute%2 == 0, "", start.Add(time.Duration(minute)*time.Minute))
}
if !health.get().reachable {
t.Fatal("alternating failures were reported as an outage")
}
}
// With the probe switched off there is nothing to report, and the client must be told to
// ignore the field rather than trust a value nothing updates.
func TestEmbyHealthPayloadIsReachableWhenUnmonitored(t *testing.T) {
payload := embyHealthFor(embyHealthState{})
if payload.Monitored {
t.Error("an unstarted probe reported itself as monitored")
}
if !payload.Reachable {
t.Error("an unmonitored server was reported unreachable")
}
if payload.Since != "" || payload.CheckedAt != "" {
t.Error("an unstarted probe reported timestamps it never took")
}
}
func TestEmbyHealthPayloadCarriesTheRetryInterval(t *testing.T) {
var health embyHealth
start := time.Now().UTC()
health.begin(60*time.Second, start)
health.record(false, "", start.Add(time.Minute))
health.record(false, "", start.Add(2*time.Minute))
payload := embyHealthFor(health.get())
if payload.RetrySeconds != 60 {
t.Errorf("retrySeconds = %d, want 60", payload.RetrySeconds)
}
if payload.Reachable {
t.Error("payload reported a reachable server during an outage")
}
if payload.CheckedAt == "" {
t.Error("payload omitted the last probe time the bar counts down from")
}
}
// The Settings rail prints Emby's version beside the gateway's, so it has to survive the
// outage that is exactly when somebody goes looking at that page. A probe that fails
// carries no version, and blanking the last known one would replace a fact that is still
// true with nothing.
func TestEmbyHealthKeepsTheLastKnownVersionThroughAnOutage(t *testing.T) {
var health embyHealth
start := time.Now().UTC()
health.begin(60*time.Second, start)
health.record(true, "4.10.0.21", start.Add(time.Minute))
if got := embyHealthFor(health.get()).Version; got != "4.10.0.21" {
t.Fatalf("version = %q, want the version the probe reported", got)
}
health.record(false, "", start.Add(2*time.Minute))
health.record(false, "", start.Add(3*time.Minute))
payload := embyHealthFor(health.get())
if payload.Reachable {
t.Fatal("the outage was not raised")
}
if payload.Version != "4.10.0.21" {
t.Errorf("version = %q, want it kept through the outage", payload.Version)
}
// And an upgrade is picked up on the probe that sees it.
health.record(true, "4.11.0.1", start.Add(4*time.Minute))
if got := embyHealthFor(health.get()).Version; got != "4.11.0.1" {
t.Errorf("version = %q, want the newly reported one", got)
}
}
// Omitted rather than empty, so a television can tell "no probe has completed yet" from
// "Emby answered without one".
func TestEmbyHealthOmitsAnUnknownVersion(t *testing.T) {
var health embyHealth
start := time.Now().UTC()
health.begin(60*time.Second, start)
if got := embyHealthFor(health.get()).Version; got != "" {
t.Errorf("version = %q, want empty before any probe has answered", got)
}
}