package orchestrator import ( "os" "path/filepath" "strings" "testing" "vetting/internal/events" "vetting/internal/logs" ) // TestDispatcher_RunLogWritesToHub verifies the plumbing between the // dispatcher and the per-run log hub: runLog must persist to the on-disk // file so the detail page's replay + SSE fan-out see the same // pre-stage diagnostics (picked / sent WoL / heartbeat). func TestDispatcher_RunLogWritesToHub(t *testing.T) { dir := t.TempDir() ev := events.NewHub() lh, err := logs.NewHub(dir, ev) if err != nil { t.Fatalf("NewHub: %v", err) } defer lh.Close() d := &Dispatcher{Logs: lh} d.runLog(7, "info", "dispatcher: sent WoL packet to aa:bb:cc:dd:ee:ff via 10.0.0.255:9") body, err := os.ReadFile(filepath.Join(dir, "run-7.log")) if err != nil { t.Fatalf("read run log: %v", err) } if !strings.Contains(string(body), "dispatcher: sent WoL packet") { t.Fatalf("run log missing dispatcher line: %q", body) } if !strings.Contains(string(body), "INFO") { t.Fatalf("run log missing level: %q", body) } } // TestDispatcher_RunLogNilHubDoesNotPanic: tests construct Dispatcher // directly without a hub. runLog must degrade to stderr rather than // panicking so the dispatcher loop stays alive. func TestDispatcher_RunLogNilHubDoesNotPanic(t *testing.T) { d := &Dispatcher{} d.runLog(1, "info", "fallback path") }