diff --git a/authbridge/cmd/abctl/tui/pipeline_pane.go b/authbridge/cmd/abctl/tui/pipeline_pane.go index 10c08b0a1..39d06b554 100644 --- a/authbridge/cmd/abctl/tui/pipeline_pane.go +++ b/authbridge/cmd/abctl/tui/pipeline_pane.go @@ -40,8 +40,11 @@ func (m *model) rebuildPipelineTable() { for _, p := range m.pipeline.Inbound { rows = append(rows, pipelineRow(p, counts[p.Name], m.pipeline.Inbound)) } - // Divider between inbound and outbound. - rows = append(rows, table.Row{"", "", "── (app) ──", "", "", "", ""}) + // Divider between inbound and outbound. Cell count MUST match the 6 + // columns defined in newPipelineTable (#, DIRECTION, PLUGIN, DEPS, BODY, + // EVENTS) — bubbles' table.renderRow indexes columns by cell position and + // panics on a mismatch. + rows = append(rows, table.Row{"", "", "── (app) ──", "", "", ""}) for _, p := range m.pipeline.Outbound { rows = append(rows, pipelineRow(p, counts[p.Name], m.pipeline.Outbound)) } diff --git a/authbridge/cmd/abctl/tui/pipeline_pane_test.go b/authbridge/cmd/abctl/tui/pipeline_pane_test.go new file mode 100644 index 000000000..d94c07862 --- /dev/null +++ b/authbridge/cmd/abctl/tui/pipeline_pane_test.go @@ -0,0 +1,38 @@ +package tui + +import ( + "testing" + + "github.com/kagenti/kagenti-extensions/authbridge/cmd/abctl/apiclient" +) + +// TestRebuildPipelineTable_AllRowsMatchColumnCount is a regression guard: +// every row added to the pipeline table — including the inbound/outbound +// "(app)" divider — must have exactly as many cells as the table has columns. +// The divider previously carried 7 cells against 6 columns, which made +// bubbles' table.renderRow panic ("index out of range [6] with length 6") +// the moment abctl rendered any pipeline. +func TestRebuildPipelineTable_AllRowsMatchColumnCount(t *testing.T) { + m := &model{ + pipeline: &apiclient.PipelineView{ + Inbound: []apiclient.PipelinePlugin{{Name: "jwt-validation", Direction: "inbound", Position: 1}}, + Outbound: []apiclient.PipelinePlugin{{Name: "token-exchange", Direction: "outbound", Position: 1}}, + }, + pipelineTbl: newPipelineTable(), + } + + // Must not panic: the buggy 7-cell divider panicked here via + // SetRows → UpdateViewport → renderRow. + m.rebuildPipelineTable() + + rows := m.pipelineTbl.Rows() + if len(rows) != 3 { // inbound plugin + divider + outbound plugin + t.Fatalf("rows = %d, want 3 (inbound + divider + outbound)", len(rows)) + } + const wantCells = 6 // #, DIRECTION, PLUGIN, DEPS, BODY, EVENTS + for i, r := range rows { + if len(r) != wantCells { + t.Errorf("row %d has %d cells, want %d (must match column count)", i, len(r), wantCells) + } + } +}