Files
llmgw/internal/router/pages_auth_test.go
T
Ilker ManapandClaude Opus 5.5 fd3c7bda32
CI / test (push) Failing after 6m12s
The client API described in OpenAPI, and both APIs in Swagger UI
/v1/openapi.json is the API clients use — OpenAI's endpoints, Anthropic's,
and what llmgw adds — in OpenAPI 3.1, generated from internal/clientapi's
route table by the generator the admin API's document comes from, now
told each document's title, server, token and error. A test reads the
router's mux.HandleFunc calls and refuses a route described and not
mounted, or mounted and not described. Asked for before there is a key,
so it needs none.

/docs/api shows it in Swagger UI 5.33.1 (Apache 2.0, served from the
gateway as the page's policy requires): Authorize takes a key, Try it out
sends to this gateway. Staff are offered the admin API's document too,
from /docs/api/admin.json, which nobody else is given.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 20:22:48 +02:00

67 lines
2.7 KiB
Go

package router
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// Every page a browser opens with a session must get past the API-key check
// and reach its own sign-in guard. One missing from the exempt list answers a
// signed-in person with {"error": "no api key was provided"} — which is what
// /org did, and which a test that mounts the page without the middleware
// cannot see.
func TestSessionPagesAreNotAskedForAnAPIKey(t *testing.T) {
rt, _ := chatRouter(t, "http://127.0.0.1:1")
h := rt.Handler()
// The /admin pages are mounted only with a cluster view, which this
// router has none of; these are the session pages it does mount.
for _, path := range []string{"/profile", "/chat", "/images", "/3d", "/org", "/org/org-x"} {
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
if strings.Contains(w.Body.String(), "api key") {
t.Errorf("%s asked a browser for an API key: %d %s", path, w.Code, w.Body.String())
}
if w.Code != http.StatusSeeOther || w.Header().Get("Location") != "/login" {
t.Errorf("%s without a session: %d to %q, want a redirect to /login",
path, w.Code, w.Header().Get("Location"))
}
}
}
func TestTheEditorSetupScriptNeedsNoKey(t *testing.T) {
// curl | sh from a machine that has no key in a header yet: the script
// holds none, and reads the key from the environment when it runs.
rt, _ := chatRouter(t, "http://127.0.0.1:1")
w := httptest.NewRecorder()
rt.Handler().ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/setup/codex?model=qwen", nil))
if w.Code != http.StatusOK || !strings.HasPrefix(w.Body.String(), "#!/bin/sh") {
t.Errorf("%d %s", w.Code, w.Body.String())
}
}
func TestTheAPIIsDescribedToWhoeverAsksAndTheAdminAPIToStaffOnly(t *testing.T) {
rt, _ := chatRouter(t, "http://127.0.0.1:1")
h := rt.Handler()
get := func(path string) *httptest.ResponseRecorder {
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
return w
}
// Asked for before there is a key, by SDK generators among others.
if w := get("/v1/openapi.json"); w.Code != http.StatusOK || !strings.Contains(w.Body.String(), `"title": "llmgw client API"`) {
t.Errorf("/v1/openapi.json: %d %.80s", w.Code, w.Body.String())
}
w := get("/docs/api")
if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), "/v1/openapi.json") || !strings.Contains(w.Body.String(), "swagger-ui-bundle.js") {
t.Fatalf("/docs/api: %d", w.Code)
}
if strings.Contains(w.Body.String(), "/docs/api/admin.json") {
t.Error("the admin API's document is offered to somebody not signed in")
}
if w := get("/docs/api/admin.json"); w.Code == http.StatusOK {
t.Errorf("the admin API's document without a session: %d", w.Code)
}
}