Files
llmgw/internal/web/charts2_test.go
Ilker ManapandClaude Opus 5.5 be9f45aa94 Write the bar chart's latency the way its table does
The ranked bars said "8825 ms" beside a table that said "8.8 s". One formatter, humanLatency, now serves both, with a test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 18:11:27 +02:00

156 lines
5.6 KiB
Go

package web
import (
"strings"
"testing"
)
func TestRankedBarsLabelEveryRowDirectly(t *testing.T) {
// The label is the point of this chart: the question is which person or
// which model, not what shape the distribution is. Rotated or truncated
// axis labels would say the chart matters more than reading it.
out := string(HBarChart([]Row{
{Label: "ilker@example.com", Value: 1200},
{Label: "deniz@example.com", Value: 300},
}, "--viz-1", "tokens"))
for _, want := range []string{"ilker@example.com", "deniz@example.com", "1.2k", "300"} {
if !strings.Contains(out, want) {
t.Errorf("the chart does not show %q:\n%s", want, out)
}
}
}
func TestASmallValueStillDrawsAMark(t *testing.T) {
// A bar that rounds to zero width reads as no data rather than as a small
// number, which is a different and wrong claim.
out := string(HBarChart([]Row{
{Label: "huge", Value: 1_000_000},
{Label: "tiny", Value: 1},
}, "--viz-1", ""))
// Two bars, one per row, and the tiny one has a width.
if n := strings.Count(out, `class="rk-bar"`); n != 2 {
t.Errorf("drew %d marks for two rows", n)
}
if strings.Contains(out, "width:0.0%") {
t.Error("the tiny value was drawn with no width")
}
}
func TestAnEmptyChartSaysSoRatherThanRenderingNothing(t *testing.T) {
// A missing chart reads as a broken page.
out := string(HBarChart(nil, "--viz-1", ""))
if !strings.Contains(out, "Nothing recorded") {
t.Errorf("an empty series rendered as %q", out)
}
}
func TestChartTextUsesTextColoursNotTheSeriesColour(t *testing.T) {
// Values and labels wear text tokens; the mark beside them carries the
// identity. Colouring the text too makes the palette do two jobs and reads
// badly for anyone the hue separation was chosen to protect.
out := string(HBarChart([]Row{{Label: "a", Value: 5}}, "--viz-2", "req"))
if strings.Count(out, "var(--viz-2)") != 1 || !strings.Contains(out, `class="rk-bar"`) {
t.Errorf("the series colour should be on the bar and nowhere else:\n%s", out)
}
}
func TestEveryBarCarriesItsOwnHoverTitle(t *testing.T) {
// The chart is in a browser, so it should answer a pointer. A <title> does
// that with no JavaScript, which is the right trade for a page this project
// serves from a single binary.
out := string(HBarChart([]Row{
{Label: "a", Value: 1}, {Label: "b", Value: 2},
}, "--viz-1", "tokens"))
if n := strings.Count(out, `title="`); n != 2 {
t.Errorf("%d hover titles for two bars", n)
}
}
func TestALongLabelIsTrimmedRatherThanOverflowing(t *testing.T) {
long := strings.Repeat("a", 80) + "@example.com"
out := string(HBarChart([]Row{{Label: long, Value: 1}}, "--viz-1", ""))
// Cut where it is drawn — the label cell ellipsises in CSS — and in full
// where it is hovered, so the reader can still find out whose row it is.
if !strings.Contains(out, `title="`+long+`: `) {
t.Error("the full label is not available on hover")
}
css, err := staticFiles.ReadFile("static/base.css")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(css), ".ranked .rk-label { font-size: .82rem; overflow: hidden; text-overflow: ellipsis;") {
t.Error("the label cell no longer cuts a long label with an ellipsis")
}
}
func TestASparklineOfNothingIsNotDrawn(t *testing.T) {
// One point is not a trend, and a single dot invites reading one.
if got := Sparkline([]float64{5}, ""); got != "" {
t.Errorf("a one-point sparkline rendered: %q", got)
}
if got := Sparkline(nil, ""); got != "" {
t.Errorf("an empty sparkline rendered: %q", got)
}
}
func TestAFlatSparklineIsDrawnAlongTheBottom(t *testing.T) {
// No traffic is a real answer. An empty box would read as a broken chart.
out := string(Sparkline([]float64{0, 0, 0}, ""))
if !strings.Contains(out, "<polyline") {
t.Errorf("a flat series did not draw: %q", out)
}
}
func TestTheChartScalesToItsContainer(t *testing.T) {
// The dashboard is read on laptops and phones. A fixed pixel width is how a
// table ends up scrolling sideways.
out := string(HBarChart([]Row{{Label: "a", Value: 1}}, "", ""))
// Bars are a share of whatever width the grid gives them, never pixels.
if !strings.Contains(out, "width:100.0%") {
t.Errorf("the bar is not sized as a share of its track:\n%s", out)
}
if strings.Contains(out, "px") {
t.Error("a fixed pixel size was set on the chart")
}
}
func TestTheRankedWrapperIsNotGivenAFixedHeight(t *testing.T) {
// This chart grows a row at a time, so whatever class it wears must not be
// one the stylesheet caps. It wore "chart", which is the fixed-height box
// the time-series charts live in, and the bars spilled over the table
// underneath them.
out := string(HBarChart([]Row{{Label: "a", Value: 1}}, "", ""))
class := "" // the wrapper's class, read back out of the markup
if i := strings.Index(out, `<div class="`); i >= 0 {
rest := out[i+len(`<div class="`):]
class = rest[:strings.Index(rest, `"`)]
}
if class == "" {
t.Fatalf("no wrapper to check:\n%s", out)
}
css, err := staticFiles.ReadFile("static/base.css")
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(css), "\n") {
decl, ok := strings.CutPrefix(strings.TrimSpace(line), "."+class+" ")
if !ok || !strings.HasPrefix(decl, "{") {
continue
}
if strings.Contains(decl, "height:") && !strings.Contains(decl, "height: auto") {
t.Errorf("%q is capped by the stylesheet, so taller charts overflow it: %s",
class, strings.TrimSpace(line))
}
}
}
func TestLatencyReadsAsAPersonWouldSayIt(t *testing.T) {
for ms, want := range map[float64]string{5: "5 ms", 8825: "8.8 s", 110695: "1m 50s", 225634: "3m 45s"} {
if got := humanLatency(ms); got != want {
t.Errorf("%v ms: got %q, want %q", ms, got, want)
}
}
}