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) } } } }