Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion internal/ui/views_indices.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,63 @@ func (a *App) renderShardsView() string {
primaryCount := 0
replicaCount := 0

// Group shards by index for this node
shardsByIndex := make(map[string]struct {
primaryShards int
replicaShards int
})

for _, shard := range nodeShards {
entry := shardsByIndex[shard.Index]
if shard.Prirep == "p" {
primaryCount++
entry.primaryShards++
} else {
replicaCount++
entry.replicaShards++
}
shardsByIndex[shard.Index] = entry
}

// Node header with IP if available
nodeDisplay := node.Name
if node.IP != "" {
nodeDisplay = fmt.Sprintf("%s (%s)", node.Name, node.IP)
}

b.WriteString(valueStyle.Render(node.Name))
b.WriteString(valueStyle.Render(nodeDisplay))
b.WriteString(fmt.Sprintf(" - %d shards ", len(nodeShards)))
b.WriteString(fmt.Sprintf("(%s %d / %s %d)\n",
statusGreen.Render("P:"), primaryCount,
statusYellow.Render("R:"), replicaCount))

// Show indices on this node
if len(shardsByIndex) > 0 {
// Get sorted list of indices
indices := make([]string, 0, len(shardsByIndex))
for index := range shardsByIndex {
indices = append(indices, index)
}
// Simple bubble sort
for i := 0; i < len(indices); i++ {
for j := i + 1; j < len(indices); j++ {
if indices[j] < indices[i] {
indices[i], indices[j] = indices[j], indices[i]
}
}
}

// Display each index with its shard counts, one per line
for _, index := range indices {
entry := shardsByIndex[index]
b.WriteString(fmt.Sprintf(" %s %s (P:%d/R:%d)\n",
labelStyle.Render("•"),
index,
entry.primaryShards,
entry.replicaShards))
}
}
b.WriteString("\n")
}

// Show unassigned shards if any
Expand Down
86 changes: 86 additions & 0 deletions internal/ui/views_indices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,89 @@ func TestApp_RenderShardsView_PrimaryAndReplicaCounting(t *testing.T) {
t.Error("renderShardsView() should show replica count of 2")
}
}

func TestApp_RenderShardsView_IndicesPerNode(t *testing.T) {
app := &App{
nodes: []NodeInfo{
{Name: "node-1", IP: "10.0.1.1"},
{Name: "node-2", IP: "10.0.1.2"},
},
shards: []ShardInfo{
{Index: "index-a", Shard: "0", Prirep: "p", State: "STARTED", Node: "node-1"},
{Index: "index-a", Shard: "1", Prirep: "r", State: "STARTED", Node: "node-1"},
{Index: "index-b", Shard: "0", Prirep: "p", State: "STARTED", Node: "node-1"},
{Index: "index-c", Shard: "0", Prirep: "p", State: "STARTED", Node: "node-2"},
{Index: "index-c", Shard: "1", Prirep: "p", State: "STARTED", Node: "node-2"},
{Index: "index-c", Shard: "2", Prirep: "r", State: "STARTED", Node: "node-2"},
},
}

result := app.renderShardsView()

// Check that node IP addresses are shown
if !strings.Contains(result, "10.0.1.1") {
t.Error("renderShardsView() should show node IP addresses")
}

// Check that specific indices appear with their counts
if !strings.Contains(result, "index-a") {
t.Error("renderShardsView() should show index-a")
}
if !strings.Contains(result, "index-b") {
t.Error("renderShardsView() should show index-b")
}
if !strings.Contains(result, "index-c") {
t.Error("renderShardsView() should show index-c")
}

// Check for shard count format (P:X/R:Y)
if !strings.Contains(result, "P:") || !strings.Contains(result, "/R:") {
t.Error("renderShardsView() should show shard counts in P:X/R:Y format")
}

// Check for bullet point formatting
if !strings.Contains(result, "•") {
t.Error("renderShardsView() should show bullet points for indices")
}
}

func TestApp_RenderShardsView_MultipleIndicesOnNode(t *testing.T) {
app := &App{
nodes: []NodeInfo{
{Name: "node-1"},
},
shards: []ShardInfo{
{Index: "users", Shard: "0", Prirep: "p", State: "STARTED", Node: "node-1"},
{Index: "users", Shard: "1", Prirep: "p", State: "STARTED", Node: "node-1"},
{Index: "orders", Shard: "0", Prirep: "p", State: "STARTED", Node: "node-1"},
{Index: "products", Shard: "0", Prirep: "r", State: "STARTED", Node: "node-1"},
},
}

result := app.renderShardsView()

// Check that all indices are shown
if !strings.Contains(result, "users") {
t.Error("renderShardsView() should show users index")
}
if !strings.Contains(result, "orders") {
t.Error("renderShardsView() should show orders index")
}
if !strings.Contains(result, "products") {
t.Error("renderShardsView() should show products index")
}

// Check that indices are sorted alphabetically
usersPos := strings.Index(result, "users")
ordersPos := strings.Index(result, "orders")
productsPos := strings.Index(result, "products")

if ordersPos < 0 || productsPos < 0 || usersPos < 0 {
t.Error("renderShardsView() should contain all index names")
}

// orders < products < users (alphabetically)
if ordersPos >= productsPos || productsPos >= usersPos {
t.Error("renderShardsView() should sort indices alphabetically")
}
}