Files
llmgw/internal/control/adminapi/routes_test.go
T
Ilker ManapandClaude Opus 5.5 a95c519bc5 The admin API described in OpenAPI 3.1: generated from a route table by docgen, served at /api/v1/openapi.json
adminapi.Routes lists every endpoint with its query parameters and Go values
of the bodies it reads and answers with; internal/docgen/openapi turns that
into docs/admin-api.openapi.json by reflection, the way encoding/json encodes
the types, with the fields' comments as descriptions. A test fails when an
endpoint is registered and not in the table, another when the committed
document is stale. The binary embeds it and serves it with the token.

Handlers that wrote map literals and decoded anonymous structs now use named
types, so the document can see them. GET /api/v1/models/{name} answers with a
model's whole definition as it is in force, which a tool managing it needs.
docs/admin-api.md: reaching the API, the token, what a request must carry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 23:47:59 +02:00

120 lines
3.5 KiB
Go

package adminapi
import (
"net/http"
"slices"
"strings"
"testing"
"gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog"
)
// patterns records what Register mounts.
type patterns []string
func (p *patterns) HandleFunc(pattern string, _ func(http.ResponseWriter, *http.Request)) {
*p = append(*p, pattern)
}
func TestEveryRegisteredEndpointIsInTheRouteTable(t *testing.T) {
// The OpenAPI document is generated from Routes, so an endpoint missing
// from it is one no client generated from the document can call, and one
// in it that is not served is a promise the server breaks.
a, _ := setup(t, testModels())
var got patterns
a.Register(&got)
table := map[string]bool{}
for _, r := range Routes {
key := r.Method + " " + r.Path
if table[key] {
t.Errorf("%s is in Routes twice", key)
}
table[key] = true
}
served := map[string]bool{}
for _, p := range got {
served[p] = true
if !table[p] {
t.Errorf("%s is registered and not in Routes: describe it there", p)
}
}
for key := range table {
if !served[key] {
t.Errorf("%s is in Routes and not registered", key)
}
}
}
func TestEveryRouteSaysWhatItAnswers(t *testing.T) {
for _, r := range Routes {
if r.Summary == "" || r.Response == nil {
t.Errorf("%s %s needs a summary and a response type", r.Method, r.Path)
}
if r.Request != nil && (r.Method == "GET" || r.Method == "HEAD") {
t.Errorf("%s %s reads a body on a GET", r.Method, r.Path)
}
for _, seg := range strings.Split(strings.Trim(r.Path, "/"), "/") {
if !strings.HasPrefix(seg, "{") {
continue
}
found := false
for k := range PathParams {
if strings.HasSuffix(k, "/"+seg) && strings.Contains(r.Path, k) {
found = true
}
}
if !found {
t.Errorf("%s %s: %s is not described in PathParams", r.Method, r.Path, seg)
}
}
}
}
func TestTheAPIServesItsOwnDescription(t *testing.T) {
a, _ := setup(t, testModels())
var doc struct {
OpenAPI string `json:"openapi"`
Paths map[string]map[string]any `json:"paths"`
}
if code := call(t, a, "GET", "/api/v1/openapi.json", "", &doc); code != 200 {
t.Fatalf("status %d", code)
}
if doc.OpenAPI != "3.1.0" {
t.Errorf("openapi %q", doc.OpenAPI)
}
// What is built in is the document generated when it was last committed;
// internal/docgen's test is what fails when that is stale. This checks the
// two ends meet: every route the table has is in what is served.
for _, r := range Routes {
if _, ok := doc.Paths[r.Path][strings.ToLower(r.Method)]; !ok {
t.Errorf("the served document has no %s %s", r.Method, r.Path)
}
}
}
func TestOneModelsDefinitionIsReadByItsNameAlone(t *testing.T) {
a, _ := setup(t, testModels())
var m catalog.Model
if code := call(t, a, "GET", "/api/v1/models/llama", "", &m); code != 200 {
t.Fatalf("status %d", code)
}
if m.Name != "llama" || m.Source != "/m" || len(m.Profiles) != 1 || m.Profiles[0].TP != 8 {
t.Errorf("got %+v", m)
}
if m.Scaling.Min != 2 || m.Scaling.Max != 4 {
t.Errorf("scaling %+v", m.Scaling)
}
if !slices.Equal(m.Aliases, []string{"l"}) {
t.Errorf("aliases %v", m.Aliases)
}
// An alias answering would let a tool that manages "l" believe it exists
// when what it found is llama.
if code := call(t, a, "GET", "/api/v1/models/l", "", nil); code != http.StatusNotFound {
t.Errorf("an alias answered %d", code)
}
if code := call(t, a, "GET", "/api/v1/models/nothing", "", nil); code != http.StatusNotFound {
t.Errorf("a missing model answered %d", code)
}
}