/install takes a reader from three bare Ubuntu machines to a first answer: what each machine needs and the commands that check it, the release fetched and its checksum checked, the eight setup screens, the system's state and the console, and a first request with a key from the account page. Every screen is from an install of v0.14.9 on three fresh VMs, recorded with tmux and kept as terminal frames: the page shows them as text, and plays a session back when asked, fetching it compressed from /install/cast/. The administrator's password and the API key were masked before the recording was kept, and a test fails if either shape reaches the page. /about gains what llmgw now does beyond routing, and its install steps are today's: the setup screens and node add, not onboard-node.sh and a model file, with a link into the guide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
package web
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestInstallGuideShowsEveryStepAndRecording(t *testing.T) {
|
|
code, body := get(t, Install("v1.2.3"), "http://llmgw.example/install", nil)
|
|
if code != http.StatusOK {
|
|
t.Fatalf("status %d", code)
|
|
}
|
|
for _, want := range []string{
|
|
"<!doctype html>",
|
|
`id="before"`, `id="binary"`, `id="setup"`, `id="check"`, `id="use"`, `id="trouble"`,
|
|
"sha256sum -c --ignore-missing",
|
|
"sudo llmgw server init",
|
|
`href="/about"`,
|
|
"/_static/guide/admin.png?v=",
|
|
// The recording's own screens, not a description of them.
|
|
"High availability", "All three are ready.", "The control plane is up",
|
|
} {
|
|
if !strings.Contains(body, want) {
|
|
t.Errorf("the guide does not have %q", want)
|
|
}
|
|
}
|
|
if n := strings.Count(body, `class="tty" data-src="/install/cast/`); n != len(guide.Casts) {
|
|
t.Errorf("%d players on the page, %d recordings", n, len(guide.Casts))
|
|
}
|
|
}
|
|
|
|
// The recording was of a real install, with a real administrator's password
|
|
// and a real key on screen. Neither may reach the page.
|
|
func TestInstallGuideCarriesNoSecret(t *testing.T) {
|
|
_, body := get(t, Install("v1.2.3"), "http://llmgw.example/install", nil)
|
|
for _, rx := range []*regexp.Regexp{
|
|
regexp.MustCompile(`sk-llmgw-[A-Za-z0-9_-]{8,}`),
|
|
regexp.MustCompile(`password(\s|<[^>]*>)+[a-z0-9]{16,}`),
|
|
} {
|
|
if m := rx.FindString(body); m != "" {
|
|
t.Errorf("the guide shows %q", m)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEveryGuideStepHasItsScreen(t *testing.T) {
|
|
for _, name := range []string{"setup-start", "setup-choices", "setup-admin", "setup-machines",
|
|
"setup-checks", "setup-plan", "setup-install", "setup-done"} {
|
|
s, ok := guide.Stills[name]
|
|
if !ok || len(s.Lines) == 0 {
|
|
t.Errorf("no screen %s", name)
|
|
continue
|
|
}
|
|
if _, ok := guide.Casts[s.Cast]; !ok {
|
|
t.Errorf("screen %s is of a recording, %s, the guide does not have", name, s.Cast)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInstallGuideAsTheWebsite(t *testing.T) {
|
|
d := NewInstallData("v1.2.3")
|
|
d.Site = true
|
|
var b bytes.Buffer
|
|
if err := RenderInstall(&b, d); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := b.String()
|
|
for _, not := range []string{"<!doctype", "</body>", "/_static/", `href="/about"`} {
|
|
if strings.Contains(body, not) {
|
|
t.Errorf("the website's guide has %q", not)
|
|
}
|
|
}
|
|
if !strings.Contains(body, `src="guide/admin.png"`) {
|
|
t.Error("the website's guide does not take its pictures from beside it")
|
|
}
|
|
}
|
|
|
|
func TestTheRecordingsAreServedCompressed(t *testing.T) {
|
|
r := httptest.NewRequest("GET", "http://llmgw.example/install/cast/2-setup.json", nil)
|
|
r.SetPathValue("file", "2-setup.json")
|
|
r.Header.Set("Accept-Encoding", "gzip, br")
|
|
w := httptest.NewRecorder()
|
|
InstallCast(w, r)
|
|
if w.Code != http.StatusOK || w.Header().Get("Content-Encoding") != "gzip" {
|
|
t.Fatalf("%d %v", w.Code, w.Header())
|
|
}
|
|
// And plain to a client that does not take gzip.
|
|
r = httptest.NewRequest("GET", "http://llmgw.example/install/cast/2-setup.json", nil)
|
|
r.SetPathValue("file", "2-setup.json")
|
|
w = httptest.NewRecorder()
|
|
InstallCast(w, r)
|
|
if !strings.HasPrefix(w.Body.String(), `{"rows":`) {
|
|
t.Errorf("plain body starts %.20q", w.Body.String())
|
|
}
|
|
r = httptest.NewRequest("GET", "http://llmgw.example/install/cast/nothing.json", nil)
|
|
r.SetPathValue("file", "nothing.json")
|
|
w = httptest.NewRecorder()
|
|
InstallCast(w, r)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("an unknown recording answered %d", w.Code)
|
|
}
|
|
}
|