package dashboard import ( "errors" "strings" "testing" "github.com/MDietermann/ArduinoArchSetup/internal/engine" "github.com/MDietermann/ArduinoArchSetup/internal/tui" ) func TestProgressUpdatesRow(t *testing.T) { m := NewModel([]string{"a"}, map[string]string{"a": "Alpha"}) m.Update(tui.ProgressMsg{ModuleID: "a", Current: 3, Total: 10, Message: "doing"}) if r := m.rowIndex["a"]; r.Current != 3 || r.Total != 10 || r.LastMsg != "doing" { t.Fatalf("row state wrong: %+v", r) } } func TestLogAppendedBounded(t *testing.T) { m := NewModel([]string{"a"}, nil) for i := 0; i < 300; i++ { m.Update(tui.LogMsg{ModuleID: "a", Line: "x"}) } if got := len(m.rowIndex["a"].LogTail); got != 200 { t.Fatalf("log tail = %d, want 200", got) } } func TestDoneMsgFlipsIcons(t *testing.T) { m := NewModel([]string{"ok", "bad", "skip"}, nil) m.Update(DoneMsg{Summary: engine.Summary{Results: []engine.ModuleResult{ {ID: "ok", Status: engine.StatusSucceeded}, {ID: "bad", Status: engine.StatusFailed, Err: errors.New("boom")}, {ID: "skip", Status: engine.StatusSkipped}, }}}) if m.rowIndex["ok"].StatusIcon != "✓" { t.Errorf("ok icon = %q", m.rowIndex["ok"].StatusIcon) } if m.rowIndex["bad"].StatusIcon != "✗" { t.Errorf("bad icon = %q", m.rowIndex["bad"].StatusIcon) } if m.rowIndex["skip"].StatusIcon != "⊘" { t.Errorf("skip icon = %q", m.rowIndex["skip"].StatusIcon) } } func TestProgressAfterDoneDoesNotRevertIcon(t *testing.T) { m := NewModel([]string{"a"}, nil) m.Update(DoneMsg{Summary: engine.Summary{Results: []engine.ModuleResult{ {ID: "a", Status: engine.StatusSucceeded}, }}}) if got := m.rowIndex["a"].StatusIcon; got != "✓" { t.Fatalf("after Done icon = %q, want ✓", got) } // Late Progress event — tea.Program.Send has no ordering guarantee across goroutines. m.Update(tui.ProgressMsg{ModuleID: "a", Current: 9, Total: 10, Message: "late"}) if got := m.rowIndex["a"].StatusIcon; got != "✓" { t.Fatalf("late Progress reverted icon to %q", got) } // Late Log event must also be dropped — it would otherwise pollute the // log tail of a completed row. before := len(m.rowIndex["a"].LogTail) m.Update(tui.LogMsg{ModuleID: "a", Line: "late log"}) if after := len(m.rowIndex["a"].LogTail); after != before { t.Fatalf("late Log appended to tail: before=%d after=%d", before, after) } } func TestViewMentionsModules(t *testing.T) { m := NewModel([]string{"alpha", "bravo"}, nil) out := m.View() for _, want := range []string{"alpha", "bravo", "archduino install"} { if !strings.Contains(out, want) { t.Errorf("view missing %q:\n%s", want, out) } } }