Files
llmgw/internal/web/chart_test.go
Ilker ManapandClaude Opus 5 07809490e3
CI / test (push) Has been cancelled
Show usage over time, per organization, team, project and person
Usage could be read as a total — per key, per user, per model, per group —
and the cluster's own charts covered the fleet's hardware. What nobody could
see was when a group used it. "Research doubled its traffic three weeks ago"
is not a question a column of totals can be asked, and it is the question
somebody asks before a quota conversation.

/admin/usage draws requests, tokens and spend over a chosen window, grouped
by any level of the tenancy tree or by person. Person is not a level of that
tree — one person holds keys in several projects — so the grouping is its
own type rather than store.Scope with a fourth value nothing else could use.

Three things learned from the charts already here. Buckets are gap-filled
per group, because a line drawn straight across an absence claims traffic
that did not happen, and because the chart sums its tail by timestamp and
can only do that if every series shares a clock. Series are ranked by total
rather than by peak — a counter's question is "who used the most", and
ranking by peak would colour the team that made one burst over the team that
ran steadily all week. And the spend chart is replaced by a sentence when no
model has a price, rather than drawn flat along the bottom.

The bucket query groups by ordinal and was planned against the cluster's
PostgreSQL before being believed, with a test alongside the others that run
there: three reports have shipped working on SQLite and failing on it.

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

127 lines
3.7 KiB
Go

package web
import (
"strings"
"testing"
"time"
)
func TestABarChartDrawsOneBarPerValue(t *testing.T) {
out := string(BarChart(Series{Values: []float64{1, 5, 3}}, "requests"))
if n := strings.Count(out, "<rect"); n != 3 {
t.Errorf("%d bars for 3 values", n)
}
if !strings.Contains(out, "peak 5 requests") {
t.Errorf("the peak is not labelled: %s", out)
}
}
func TestAnHourWithTrafficNeverLooksLikeOneWithout(t *testing.T) {
// One request in an hour against a peak of ten thousand rounds to a bar of
// zero height, and a chart where a busy hour and an idle one look identical
// is worse than no chart.
out := string(BarChart(Series{Values: []float64{10000, 1}}, "requests"))
for _, frag := range []string{`height="0.00"`, `height="0"`} {
if strings.Contains(out, frag) {
t.Errorf("a non-zero value was drawn with no height: %s", out)
}
}
}
func TestAnEmptySeriesStillRendersAFrame(t *testing.T) {
// A missing chart reads as a broken page; a flat one reads as no traffic,
// which is the truth.
for _, s := range []Series{{}, {Values: []float64{0, 0, 0}}} {
out := string(BarChart(s, "requests"))
if !strings.Contains(out, "<svg") {
t.Error("no frame was drawn")
}
if !strings.Contains(out, "nothing recorded") {
t.Errorf("an empty chart does not say so: %s", out)
}
if strings.Contains(out, "<rect") {
t.Error("bars were drawn for an empty series")
}
}
}
func TestLabelsAreEscaped(t *testing.T) {
// Tooltips carry text that came from somewhere else, and the output of this
// function is marked as trusted HTML.
out := string(BarChart(Series{
Values: []float64{1},
Labels: []string{`</title><script>alert(1)</script>`},
}, "requests"))
if strings.Contains(out, "<script>") {
t.Fatalf("a label reached the markup unescaped: %s", out)
}
if !strings.Contains(out, "&lt;script&gt;") {
t.Errorf("the label was dropped rather than escaped: %s", out)
}
}
func TestTheUnitIsEscapedToo(t *testing.T) {
out := string(BarChart(Series{Values: []float64{1}}, `"><script>alert(1)</script>`))
if strings.Contains(out, "<script>") {
t.Fatalf("the unit reached the markup unescaped: %s", out)
}
}
func TestOnlyKnownColoursReachTheStyleAttribute(t *testing.T) {
// A colour that could be anything is a way to write CSS into every page the
// chart appears on.
out := string(BarChart(Series{Values: []float64{1}, Colour: "red;}</style><script>x"}, "u"))
if strings.Contains(out, "</style>") || strings.Contains(out, "script") {
t.Fatalf("a colour escaped its attribute: %s", out)
}
if !strings.Contains(out, "var(--accent)") {
t.Errorf("an unknown colour did not fall back: %s", out)
}
for _, ok := range []string{"--accent", "--ok", "--warn", "--bad"} {
if got := cssVar(ok); got != ok {
t.Errorf("cssVar(%q) = %q", ok, got)
}
}
}
func TestCompactNumbers(t *testing.T) {
cases := map[float64]string{
0: "0", 7: "7", 1500: "1.5k", 2_400_000: "2.4M", 3_100_000_000: "3.1B", 1.25: "1.2",
}
for in, want := range cases {
if got := compact(in); got != want {
t.Errorf("compact(%v) = %q, want %q", in, got, want)
}
}
}
func TestHourLabelsSayWhenAndHowMuch(t *testing.T) {
at := time.Date(2026, 9, 16, 14, 0, 0, 0, time.UTC)
got := hourLabel(at, 1500, "requests")
if !strings.Contains(got, "1.5k requests") {
t.Errorf("label = %q", got)
}
}
func TestASmallFigureIsNotRenderedAsZero(t *testing.T) {
// A spend of 0.03 units shown as "0.0" says nothing was spent. On a cluster
// whose prices make a month's traffic a fraction of a unit, that is every
// figure on the page.
for _, c := range []struct {
in float64
want string
}{
{0.03, "0.03"},
{0.5, "0.5"},
{0, "0"},
{1, "1"},
{1500, "1.5k"},
} {
if got := compact(c.in); got != c.want {
t.Errorf("compact(%v) = %q, want %q", c.in, got, c.want)
}
}
}