diff --git a/cli/tui/brokers.go b/cli/tui/brokers.go index 6c28ffa0..1743c3a4 100644 --- a/cli/tui/brokers.go +++ b/cli/tui/brokers.go @@ -46,6 +46,11 @@ func (m brokersModel) Update(msg tea.Msg) (brokersModel, tea.Cmd) { m.loading = false m.info = msg.info m.err = msg.err + // A broker gone since the last refresh left the cursor past the end, + // and View panicked indexing it. + if m.info != nil && m.cursor >= len(m.info.Brokers) { + m.cursor = max(0, len(m.info.Brokers)-1) + } case tea.KeyMsg: if m.info == nil { break diff --git a/cli/tui/brokers_test.go b/cli/tui/brokers_test.go new file mode 100644 index 00000000..a47ed0a3 --- /dev/null +++ b/cli/tui/brokers_test.go @@ -0,0 +1,36 @@ +package tui + +import ( + "strings" + "testing" + + "github.com/bmscomp/kates/cli/client" +) + +// The cursor on the last broker, and a refresh with one broker fewer: View +// indexed past the end and the dashboard died with the broker. +func TestBrokersRefreshWithFewerBrokers(t *testing.T) { + brokers := func(n int) *client.ClusterInfo { + info := &client.ClusterInfo{ClusterID: "c1", BrokerCount: n} + for i := range n { + info.Brokers = append(info.Brokers, client.BrokerNode{ID: i, Host: "kafka", Port: 9092}) + } + return info + } + m := brokersModel{width: 120, height: 40} + m, _ = m.Update(brokersLoadedMsg{info: brokers(3)}) + m.cursor = 2 + + m, _ = m.Update(brokersLoadedMsg{info: brokers(2)}) + if m.cursor != 1 { + t.Errorf("cursor = %d after the refresh, want 1, the last broker", m.cursor) + } + if view := m.View(); !strings.Contains(view, "Broker 1") { + t.Errorf("the view does not show broker 1:\n%s", view) + } + + m, _ = m.Update(brokersLoadedMsg{info: brokers(0)}) + if m.cursor != 0 || !strings.Contains(m.View(), "No brokers found") { + t.Errorf("cursor = %d with no brokers, want 0 and the empty view", m.cursor) + } +}