Percentiles are read from the histograms kept with the usage sums and shown per group on the usage tab and in the people page's panels. Requests refused by a limit are recorded as 429s against their owner, free of charge, so a team hitting its limits shows as such. Usage can be grouped by model. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
112 lines
4.3 KiB
Go
112 lines
4.3 KiB
Go
package web
|
|
|
|
import (
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
|
|
)
|
|
|
|
// peoplePage mounts all three tabs, as the server does.
|
|
func peoplePage(t *testing.T, q *fakeQuotas, u *fakeScopeSeries) *http.ServeMux {
|
|
t.Helper()
|
|
a := fakeAccounts{user: store.User{ID: "u1", Email: "a@example.com", IsAdmin: true}}
|
|
p := NewAccountPages(a, nil, nil, "v0", slog.New(slog.NewTextHandler(io.Discard, nil)))
|
|
mux := http.NewServeMux()
|
|
p.RegisterAdminUsers(mux, &fakeAdmin{orgs: q.orgs})
|
|
p.RegisterAdminQuotas(mux, q, pricedYes{})
|
|
p.RegisterAdminUsage(mux, u, pricedYes{})
|
|
return mux
|
|
}
|
|
|
|
func TestABranchChosenInTheTreeShowsItsLimitsAndUseTogether(t *testing.T) {
|
|
// People, their limits and their use were three pages: a team's members
|
|
// on one, its quota on another, its spend on a third.
|
|
q := oneTeam()
|
|
rpm := 60
|
|
q.limits["org:org-1"] = store.Limits{Scope: store.ScopeOrg, ScopeID: "org-1", RPM: &rpm}
|
|
u := &fakeScopeSeries{byGroup: map[store.Grouping][]store.UsageSeries{
|
|
store.GroupByOrg: {{Grouping: store.GroupByOrg, ID: "org-1", Name: "Scania", Requests: 1234,
|
|
Buckets: []store.Bucket{{Start: time.Now().Add(-time.Hour), Requests: 1234}}}},
|
|
}}
|
|
body := strings.Join(strings.Fields(
|
|
mirrorGet(t, peoplePage(t, q, u), "/admin/users?sel=org:org-1").Body.String()), " ")
|
|
|
|
for _, want := range []string{
|
|
`class="people-detail"`, "<h2>Scania</h2>",
|
|
"<dt>Requests/min</dt><dd>60</dd>", // its own limit
|
|
"1.2k requests", // its use over the week
|
|
`name="sel" value="org:org-1"`, // changing it comes back here
|
|
`aria-current="page">Organizations & people`,
|
|
} {
|
|
if !strings.Contains(body, want) {
|
|
t.Errorf("the panel lacks %q", want)
|
|
}
|
|
}
|
|
if u.asked[len(u.asked)-1] != store.GroupByOrg {
|
|
t.Errorf("use was read by %v, not by organization", u.asked)
|
|
}
|
|
}
|
|
|
|
func TestAChangeMadeInAPanelComesBackToThePanel(t *testing.T) {
|
|
q := oneTeam()
|
|
body := mirrorPost(t, peoplePage(t, q, &fakeScopeSeries{}), "/admin/quotas", url.Values{
|
|
"csrf": {"tok"}, "scope": {"org"}, "id": {"org-1"}, "rpm": {"30"},
|
|
"tab": {"people"}, "sel": {"org:org-1"},
|
|
}).Body.String()
|
|
if len(q.put) != 1 || *q.put[0].RPM != 30 {
|
|
t.Fatalf("the limit was not saved: %+v", q.put)
|
|
}
|
|
if !strings.Contains(body, `class="people-detail"`) || !strings.Contains(body, "rpm 30") {
|
|
t.Error("saving from the panel did not come back to it with the result")
|
|
}
|
|
}
|
|
|
|
func TestEachOldAddressOpensItsTab(t *testing.T) {
|
|
mux := peoplePage(t, oneTeam(), &fakeScopeSeries{})
|
|
for path, tab := range map[string]string{
|
|
"/admin/users": "Organizations & people", "/admin/quotas": "Limits", "/admin/usage": "Usage",
|
|
} {
|
|
body := mirrorGet(t, mux, path).Body.String()
|
|
if !strings.Contains(body, `aria-current="page">`+tab) {
|
|
t.Errorf("%s does not open the %s tab", path, tab)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSomethingNotInTheTreeOpensNoPanel(t *testing.T) {
|
|
body := mirrorGet(t, peoplePage(t, oneTeam(), &fakeScopeSeries{}), "/admin/users?sel=org:gone").Body.String()
|
|
if strings.Contains(body, `class="people-detail"`) {
|
|
t.Error("a panel opened for an organization that does not exist")
|
|
}
|
|
}
|
|
|
|
func TestHowRequestsFaredIsShownBesideWhatTheyCost(t *testing.T) {
|
|
stats := map[string]store.GroupStats{"org-1": {ID: "org-1", Requests: 200, Errors: 30, Limited: 25,
|
|
TTFT: store.Quantiles{P50: 180, P95: 900, P99: 2400, N: 150},
|
|
Latency: store.Quantiles{P50: 3000, P95: 8000, P99: 120000, N: 200, Over: true}}}
|
|
u := &fakeScopeSeries{stats: stats, byGroup: map[store.Grouping][]store.UsageSeries{
|
|
store.GroupByOrg: {{Grouping: store.GroupByOrg, ID: "org-1", Name: "Scania", Requests: 200,
|
|
Buckets: []store.Bucket{{Start: time.Now().Add(-time.Hour), Requests: 200}}}},
|
|
}}
|
|
mux := peoplePage(t, oneTeam(), u)
|
|
|
|
table := strings.Join(strings.Fields(mirrorGet(t, mux, "/admin/usage?by=org").Body.String()), " ")
|
|
for _, want := range []string{"15.0%", ">25<", "180 ms / 900 ms", "8.0 s"} {
|
|
if !strings.Contains(table, want) {
|
|
t.Errorf("the usage table lacks %q", want)
|
|
}
|
|
}
|
|
panel := strings.Join(strings.Fields(mirrorGet(t, mux, "/admin/users?sel=org:org-1").Body.String()), " ")
|
|
for _, want := range []string{"<dt>Refused by a limit</dt>", "p95 900 ms", "p99 > 120 s"} {
|
|
if !strings.Contains(panel, want) {
|
|
t.Errorf("the panel lacks %q", want)
|
|
}
|
|
}
|
|
}
|