135 lines
4.9 KiB
Go
135 lines
4.9 KiB
Go
|
|
package orchestrator
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"scm.bstein.dev/bstein/ananke/internal/state"
|
||
|
|
)
|
||
|
|
|
||
|
|
// TestHookReportWrappersCoverage runs one orchestration or CLI step.
|
||
|
|
// Signature: TestHookReportWrappersCoverage(t *testing.T).
|
||
|
|
// Why: ensures every report-specific testing hook is exercised from the top-level
|
||
|
|
// testing module so hook coverage never drifts below quality gates.
|
||
|
|
func TestHookReportWrappersCoverage(t *testing.T) {
|
||
|
|
cfg := lifecycleConfig(t)
|
||
|
|
orch := newLiveHookOrchestrator(t, cfg)
|
||
|
|
|
||
|
|
if got := orch.TestHookReportArchiveDir(); got == "" {
|
||
|
|
t.Fatalf("expected non-empty report archive dir")
|
||
|
|
}
|
||
|
|
if got := orch.TestHookStartupReportPath(); got == "" {
|
||
|
|
t.Fatalf("expected startup report path")
|
||
|
|
}
|
||
|
|
if got := orch.TestHookStartupProgressPath(); got == "" {
|
||
|
|
t.Fatalf("expected startup progress path")
|
||
|
|
}
|
||
|
|
if got := orch.TestHookLastShutdownReportPath(); got == "" {
|
||
|
|
t.Fatalf("expected last shutdown report path")
|
||
|
|
}
|
||
|
|
|
||
|
|
orch.TestHookBeginStartupReport("wrapper-coverage")
|
||
|
|
orch.TestHookSetStartupPhase("wrapper-phase", "coverage detail")
|
||
|
|
orch.TestHookNoteStartupCheckState("wrapper-check", "running", "detail")
|
||
|
|
orch.TestHookNoteStartupCheck("wrapper-final", true, "ok")
|
||
|
|
orch.TestHookNoteStartupAutoHeal("auto-heal detail")
|
||
|
|
orch.TestHookPersistStartupProgress("running")
|
||
|
|
orch.TestHookPersistStartupProgressNil()
|
||
|
|
orch.TestHookFinalizeStartupReport(fmt.Errorf("intentional failure"))
|
||
|
|
if !orch.TestHookFinalizeStartupReportSnapshot(nil) {
|
||
|
|
t.Fatalf("expected finalize snapshot wrapper success")
|
||
|
|
}
|
||
|
|
orch.TestHookFinalizeStartupReport(nil) // nil-active-report branch
|
||
|
|
|
||
|
|
progressPath := orch.TestHookStartupProgressPath()
|
||
|
|
if _, err := os.Stat(progressPath); err != nil {
|
||
|
|
t.Fatalf("expected startup progress at %s: %v", progressPath, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := orch.TestHookWriteStartupReportFileNil(filepath.Join(cfg.State.Dir, "nil-report.json")); err != nil {
|
||
|
|
t.Fatalf("expected nil report write no-op, got %v", err)
|
||
|
|
}
|
||
|
|
if err := orch.TestHookWriteStartupReportFile(filepath.Join(cfg.State.Dir, "wrapper-report.json"), "running"); err != nil {
|
||
|
|
t.Fatalf("expected startup report write success, got %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
recStartup := state.RunRecord{
|
||
|
|
ID: "wrapper-startup-1",
|
||
|
|
Action: "startup",
|
||
|
|
Reason: "wrapper",
|
||
|
|
StartedAt: time.Now().UTC().Add(-2 * time.Second),
|
||
|
|
EndedAt: time.Now().UTC(),
|
||
|
|
Success: true,
|
||
|
|
}
|
||
|
|
if err := orch.TestHookWriteRunRecordArtifact(recStartup); err != nil {
|
||
|
|
t.Fatalf("expected startup artifact write success, got %v", err)
|
||
|
|
}
|
||
|
|
recShutdown := state.RunRecord{
|
||
|
|
ID: "wrapper-shutdown-1",
|
||
|
|
Action: "shutdown",
|
||
|
|
Reason: "wrapper",
|
||
|
|
StartedAt: time.Now().UTC().Add(-2 * time.Second),
|
||
|
|
EndedAt: time.Now().UTC(),
|
||
|
|
Success: true,
|
||
|
|
}
|
||
|
|
if err := orch.TestHookWriteRunRecordArtifact(recShutdown); err != nil {
|
||
|
|
t.Fatalf("expected shutdown artifact write success, got %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
orch.TestHookFinalizeRecord("startup", "wrapper-finalize-ok", "")
|
||
|
|
orch.TestHookFinalizeRecord("startup", "wrapper-finalize-fail", "boom")
|
||
|
|
|
||
|
|
t.Run("report-writer-error-branches", func(t *testing.T) {
|
||
|
|
cfgErr := lifecycleConfig(t)
|
||
|
|
root := t.TempDir()
|
||
|
|
reportDirFile := filepath.Join(root, "reports-file")
|
||
|
|
if err := os.WriteFile(reportDirFile, []byte("x"), 0o644); err != nil {
|
||
|
|
t.Fatalf("write report dir file: %v", err)
|
||
|
|
}
|
||
|
|
cfgErr.State.ReportsDir = reportDirFile
|
||
|
|
orchErr := newLiveHookOrchestrator(t, cfgErr)
|
||
|
|
if err := orchErr.TestHookWriteRunRecordArtifact(state.RunRecord{
|
||
|
|
ID: "startup-error-path",
|
||
|
|
Action: "startup",
|
||
|
|
Reason: "test",
|
||
|
|
StartedAt: time.Now().UTC(),
|
||
|
|
}); err == nil {
|
||
|
|
t.Fatalf("expected report archive dir creation failure")
|
||
|
|
}
|
||
|
|
|
||
|
|
cfgWriteErr := lifecycleConfig(t)
|
||
|
|
cfgWriteErr.State.ReportsDir = filepath.Join(root, "reports-ok")
|
||
|
|
orchWriteErr := newLiveHookOrchestrator(t, cfgWriteErr)
|
||
|
|
if err := os.MkdirAll(cfgWriteErr.State.ReportsDir, 0o755); err != nil {
|
||
|
|
t.Fatalf("mkdir reports dir: %v", err)
|
||
|
|
}
|
||
|
|
blockPath := filepath.Join(cfgWriteErr.State.ReportsDir, "collision.json")
|
||
|
|
if err := os.Mkdir(blockPath, 0o755); err != nil {
|
||
|
|
t.Fatalf("mkdir collision path: %v", err)
|
||
|
|
}
|
||
|
|
if err := orchWriteErr.TestHookWriteRunRecordArtifact(state.RunRecord{
|
||
|
|
ID: "collision",
|
||
|
|
Action: "startup",
|
||
|
|
Reason: "test",
|
||
|
|
StartedAt: time.Now().UTC(),
|
||
|
|
}); err == nil {
|
||
|
|
t.Fatalf("expected artifact write collision error")
|
||
|
|
}
|
||
|
|
|
||
|
|
cfgStartupWriteErr := lifecycleConfig(t)
|
||
|
|
parentFile := filepath.Join(root, "startup-parent-file")
|
||
|
|
if err := os.WriteFile(parentFile, []byte("x"), 0o644); err != nil {
|
||
|
|
t.Fatalf("write parent file: %v", err)
|
||
|
|
}
|
||
|
|
cfgStartupWriteErr.State.Dir = filepath.Join(root, "startup-state-ok")
|
||
|
|
orchStartupWriteErr := newLiveHookOrchestrator(t, cfgStartupWriteErr)
|
||
|
|
if err := orchStartupWriteErr.TestHookWriteStartupReportFile(filepath.Join(parentFile, "report.json"), "running"); err == nil {
|
||
|
|
t.Fatalf("expected startup report mkdir error")
|
||
|
|
}
|
||
|
|
orchStartupWriteErr.TestHookPersistStartupProgress("running")
|
||
|
|
})
|
||
|
|
}
|