Files
ITWorx Pulse release export bd774932d5
Public source validation / validate (push) Failing after 3m8s
Publish ITWorx Pulse source
2026-09-03 02:09:19 +02:00

196 lines
8.8 KiB
Go

package systemstatus
import (
"errors"
"testing"
"time"
"github.com/itworx/pulse/internal/config"
"github.com/itworx/pulse/internal/datasource"
)
func TestBuildNeverReportsUnsampledSourcesAsHealthy(t *testing.T) {
snapshot := Build(config.Config{AuthMode: "mock", PrometheusURL: "http://prometheus", UnraidURL: "http://unraid", UnraidAPIToken: "configured"}, true, time.Unix(100, 0), nil)
if snapshot.OverallState != StateUnknown {
t.Fatalf("overall state = %q, want unknown", snapshot.OverallState)
}
for _, component := range snapshot.Components {
if component.ID == "prometheus" || component.ID == "unraid" {
if component.State == StateHealthy {
t.Fatalf("unsampled source %s reported healthy", component.ID)
}
}
}
if snapshot.Backup.State != StateUnknown || snapshot.Backup.Reason != "no_verified_backup" {
t.Fatalf("backup status = %#v", snapshot.Backup)
}
}
func TestObservedAgentSourcesOverrideMissingAPILocalCredentials(t *testing.T) {
now := time.Date(2026, 8, 12, 1, 0, 0, 0, time.UTC)
observed := datasource.SourceHealth{
State: datasource.HealthHealthy, ObservedAt: now.Add(-time.Second), ReceivedAt: now, LastSuccess: now.Add(-time.Second),
Policy: datasource.FreshnessPolicy{MaxAge: time.Minute}, ReasonCode: "source_sampled",
}
snapshot := Build(config.Config{AuthMode: "mock"}, true, now, nil, WithSources(
FromDatasource("unraid", observed, now),
FromDatasource("storage", observed, now),
))
for _, id := range []string{"unraid", "storage"} {
component := componentByID(snapshot, id)
if component.State != StateHealthy || component.Reason != "source_sampled" || component.ObservedAt == nil {
t.Fatalf("component %s = %#v", id, component)
}
}
if len(snapshot.SourceLag) != 1 || snapshot.SourceLag[0].SourceID != "unraid" || snapshot.SourceLag[0].State != StateHealthy {
t.Fatalf("source lag = %#v", snapshot.SourceLag)
}
}
func TestConfiguredOIDCReflectsAuthenticatedProtectedRequest(t *testing.T) {
now := time.Date(2026, 8, 12, 9, 0, 0, 0, time.UTC)
cfg := config.Config{AuthMode: "oidc", OIDCIssuer: "https://auth.example", OIDCClientID: "pulse"}
withoutSession := Build(cfg, true, now, nil)
if got := componentByID(withoutSession, "oidc"); got.State != StateUnknown || got.Reason != "authenticated_session_not_observed" {
t.Fatalf("OIDC without request-local session proof = %#v", got)
}
withSession := Build(cfg, true, now, nil, WithAuthenticatedSession(true))
got := componentByID(withSession, "oidc")
if got.State != StateHealthy || got.Reason != "authenticated_session" || got.ObservedAt == nil || got.LastSuccessAt == nil {
t.Fatalf("OIDC with authenticated session = %#v", got)
}
}
func TestRequiredDisabledSourcePreventsHealthyOverallState(t *testing.T) {
now := time.Date(2026, 8, 12, 1, 0, 0, 0, time.UTC)
jobs := []JobHealth{
{Component: ComponentWorker, Status: JobCompleted, LastRunAt: now, LastSuccessAt: now},
{Component: ComponentProbes, Status: JobCompleted, LastRunAt: now, LastSuccessAt: now},
{Component: ComponentNotifications, Status: JobCompleted, LastRunAt: now, LastSuccessAt: now},
}
snapshot := Build(config.Config{AuthMode: "mock"}, true, now, nil, WithJobs(time.Minute, jobs...))
if snapshot.OverallState != StateUnknown {
t.Fatalf("overall state = %q, want unknown", snapshot.OverallState)
}
}
func TestBackupObservationNeverTreatsStaleOrFailedAsHealthy(t *testing.T) {
now := time.Date(2026, 8, 12, 4, 0, 0, 0, time.UTC)
fresh := Build(config.Config{AuthMode: "mock"}, true, now, nil, WithBackupObservation(now.Add(-time.Hour), now.Add(-time.Minute), nil), WithMigrationVersion("0019_capacity_history"))
if fresh.Backup.State != StateHealthy || fresh.Backup.Reason != "backup_verified" || fresh.Backup.AgeSeconds == nil || fresh.Backup.VerifiedAt == nil || fresh.Backup.VerifiedAt.Equal(*fresh.Backup.LastSuccessAt) || fresh.Release.MigrationVersion != "0019_capacity_history" {
t.Fatalf("fresh snapshot = %#v", fresh)
}
stale := Build(config.Config{AuthMode: "mock"}, true, now, nil, WithBackupObservation(now.Add(-48*time.Hour), now, nil))
if stale.Backup.State == StateHealthy || stale.Backup.Reason != "backup_stale" {
t.Fatalf("stale backup = %#v", stale.Backup)
}
failed := Build(config.Config{AuthMode: "mock"}, true, now, nil, WithBackupObservation(time.Time{}, time.Time{}, errors.New("checksum mismatch")))
if failed.Backup.State != StateDegraded || failed.Backup.Reason != "backup_verification_failed" || failed.OverallState != StateDegraded {
t.Fatalf("failed backup = %#v", failed.Backup)
}
}
func TestBuildSortsComponentsAndDisablesUnconfiguredSources(t *testing.T) {
snapshot := Build(config.Config{AuthMode: "mock"}, false, time.Unix(100, 0), nil)
if snapshot.Components[0].ID != "database" {
t.Fatalf("components are not deterministic: %#v", snapshot.Components)
}
for _, component := range snapshot.Components {
if component.ID == "prometheus" || component.ID == "unraid" {
if component.State != StateDisabled {
t.Fatalf("component %s = %q, want disabled", component.ID, component.State)
}
}
}
}
func componentByID(snapshot Snapshot, id string) Component {
for _, component := range snapshot.Components {
if component.ID == id {
return component
}
}
return Component{}
}
func TestBackgroundJobComponentsReportRealOutcomes(t *testing.T) {
now := time.Date(2026, 8, 4, 12, 0, 0, 0, time.UTC)
cfg := config.Config{AuthMode: "mock"}
for name, testCase := range map[string]struct {
jobs []JobHealth
wantState State
wantReason string
}{
"never reported": {
jobs: nil,
wantState: StateUnknown, wantReason: "heartbeat_not_recorded",
},
"recent success": {
jobs: []JobHealth{{Component: ComponentWorker, JobKey: "discovery", Status: JobCompleted, LastRunAt: now.Add(-time.Minute), LastSuccessAt: now.Add(-time.Minute)}},
wantState: StateHealthy, wantReason: "last_run_succeeded",
},
"failure is degraded": {
jobs: []JobHealth{{Component: ComponentWorker, JobKey: "discovery", Status: JobFailed, ErrorCode: "lease_unavailable", LastRunAt: now}},
wantState: StateDegraded, wantReason: "lease_unavailable",
},
"stale success is unknown": {
jobs: []JobHealth{{Component: ComponentWorker, JobKey: "discovery", Status: JobCompleted, LastRunAt: now.Add(-time.Hour), LastSuccessAt: now.Add(-time.Hour)}},
wantState: StateUnknown, wantReason: "last_success_stale",
},
"completed without a success is unknown": {
jobs: []JobHealth{{Component: ComponentWorker, JobKey: "discovery", Status: JobCompleted, LastRunAt: now}},
wantState: StateUnknown, wantReason: "last_success_not_recorded",
},
"not configured is disabled": {
jobs: []JobHealth{{Component: ComponentWorker, JobKey: "discovery", Status: JobDisabled, Reason: "container_source_not_registered", LastRunAt: now}},
wantState: StateDisabled, wantReason: "container_source_not_registered",
},
"worst job wins": {
jobs: []JobHealth{
{Component: ComponentWorker, JobKey: "discovery", Status: JobCompleted, LastRunAt: now, LastSuccessAt: now},
{Component: ComponentWorker, JobKey: "alert-evaluation", Status: JobFailed, ErrorCode: "run_failed", LastRunAt: now},
},
wantState: StateDegraded, wantReason: "run_failed",
},
} {
t.Run(name, func(t *testing.T) {
snapshot := Build(cfg, true, now, nil, WithJobs(5*time.Minute, testCase.jobs...))
worker := componentByID(snapshot, ComponentWorker)
if worker.State != testCase.wantState || worker.Reason != testCase.wantReason {
t.Fatalf("worker component = %#v", worker)
}
if worker.State == StateHealthy && worker.LastSuccessAt == nil {
t.Fatal("a healthy component must carry the observation that justifies it")
}
// Components without a reported job keep their conservative default.
probes := componentByID(snapshot, ComponentProbes)
if probes.State != StateUnknown || probes.Reason != "probe_heartbeat_not_recorded" {
t.Fatalf("probes component = %#v", probes)
}
})
}
}
func TestOverallStateStaysUnknownUntilEveryComponentReports(t *testing.T) {
now := time.Date(2026, 8, 4, 12, 0, 0, 0, time.UTC)
cfg := config.Config{AuthMode: "mock"}
jobs := []JobHealth{
{Component: ComponentWorker, Status: JobCompleted, LastRunAt: now, LastSuccessAt: now},
{Component: ComponentProbes, Status: JobCompleted, LastRunAt: now, LastSuccessAt: now},
{Component: ComponentNotifications, Status: JobCompleted, LastRunAt: now, LastSuccessAt: now},
}
if state := Build(cfg, true, now, nil, WithJobs(time.Minute, jobs[:1]...)).OverallState; state != StateUnknown {
t.Fatalf("overall state with one reporting job = %q", state)
}
snapshot := Build(cfg, true, now, nil, WithJobs(time.Minute, jobs...))
for _, component := range snapshot.Components {
if component.ID == ComponentWorker || component.ID == ComponentProbes || component.ID == ComponentNotifications {
if component.State != StateHealthy {
t.Fatalf("component %s = %#v", component.ID, component)
}
}
}
}