summaryrefslogtreecommitdiff
path: root/internal/tui/dashboard/histogram_test.go
blob: 48297a2eae993acf8f64fee33b15a9d395e1427b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package dashboard

import (
	"strings"
	"testing"

	"ior/internal/statsengine"

	"charm.land/lipgloss/v2"
)

func TestRenderHistogramNoBuckets(t *testing.T) {
	out := renderHistogram(statsengine.HistogramSnapshot{}, "Latency Histogram", 80, 20)
	if !strings.Contains(out, "no data") {
		t.Fatalf("expected no data placeholder, got %q", out)
	}
}

func TestRenderHistogramIncludesLabelsCountsAndScale(t *testing.T) {
	hist := statsengine.NewHistogramSnapshot(13, []statsengine.HistogramBucketSnapshot{
		{Label: "[0,1us)", Count: 1},
		{Label: "[1us,10us)", Count: 3},
		{Label: "[10us,100us)", Count: 9},
	})

	out := renderHistogram(hist, "Latency Histogram", 100, 20)
	for _, token := range []string{"Latency Histogram", "[0,1us)", "[1us,10us)", "[10us,100us)", "9", "Scale: █▓▒░"} {
		if !strings.Contains(out, token) {
			t.Fatalf("expected token %q in histogram output", token)
		}
	}
	if !strings.Contains(out, "█") {
		t.Fatalf("expected high-intensity bar rune in histogram output")
	}
}

func TestRenderLatencyAndGapsTabIncludeSparkline(t *testing.T) {
	snap := statsengine.NewSnapshot(
		[]float64{10, 20, 15, 30},
		[]float64{2, 4, 3, 5},
		nil,
		nil,
		nil,
		nil,
		statsengine.NewHistogramSnapshot(2, []statsengine.HistogramBucketSnapshot{
			{Label: "[0,1us)", Count: 2},
		}),
		statsengine.NewHistogramSnapshot(1, []statsengine.HistogramBucketSnapshot{
			{Label: "[1us,10us)", Count: 1},
		}),
	)

	lat := renderLatencyTab(&snap, 100, 24)
	if !strings.Contains(lat, "Latency Histogram") || !strings.Contains(lat, "Latency sparkline:") {
		t.Fatalf("latency tab missing expected sections: %q", lat)
	}

	gap := renderGapsTab(&snap, 100, 24)
	if !strings.Contains(gap, "Gap Histogram") || !strings.Contains(gap, "Gap sparkline:") {
		t.Fatalf("gaps tab missing expected sections: %q", gap)
	}
}

func TestRenderHistogramTruncatesForSmallHeight(t *testing.T) {
	hist := statsengine.NewHistogramSnapshot(3, []statsengine.HistogramBucketSnapshot{
		{Label: "[0,1us)", Count: 1},
		{Label: "[1us,10us)", Count: 1},
		{Label: "[10us,100us)", Count: 1},
	})

	out := renderHistogram(hist, "Latency Histogram", 100, 3)
	if !strings.Contains(out, "[0,1us)") {
		t.Fatalf("expected first bucket in output: %q", out)
	}
	if strings.Contains(out, "[1us,10us)") || strings.Contains(out, "[10us,100us)") {
		t.Fatalf("expected histogram rows to be truncated for small height: %q", out)
	}
}

func TestRenderLatencyGapsTabDoesNotOverflowWidth(t *testing.T) {
	snap := statsengine.NewSnapshot(
		[]float64{10, 20, 15, 30, 18, 35},
		[]float64{2, 4, 3, 5, 7, 6},
		nil,
		nil,
		nil,
		nil,
		statsengine.NewHistogramSnapshot(6, []statsengine.HistogramBucketSnapshot{
			{Label: "[0,1us)", Count: 1},
			{Label: "[1us,10us)", Count: 2},
			{Label: "[10us,100us)", Count: 3},
		}),
		statsengine.NewHistogramSnapshot(6, []statsengine.HistogramBucketSnapshot{
			{Label: "[0,1us)", Count: 1},
			{Label: "[1us,10us)", Count: 2},
			{Label: "[10us,100us)", Count: 3},
		}),
	)

	const width = 100
	out := renderLatencyGapsTab(&snap, width, 24)
	for _, line := range strings.Split(out, "\n") {
		if lipgloss.Width(line) > width {
			t.Fatalf("latency/gaps line exceeds width %d: got %d in %q", width, lipgloss.Width(line), line)
		}
	}
}