Files
llmgw/internal/cli/serverinit_test.go
T
Ilker ManapandClaude Opus 5.5 2a381ad669 Set up a control plane with one command
`llmgw server init` does what install.md's steps 2 and 3 asked of the
operator: the llmgw user and directories, a certificate authority, the
control plane's certificate for every address the machine has, a
configuration, the default project, an administrator with a generated
password, and the systemd unit — then starts it and waits for /health.

Every step looks before it acts, so a second run changes nothing, and
--host later reissues the certificate with the old names kept. It never
overwrites server.yaml or an authority, and leaves alone certificates
issued by an authority kept on another machine.

The configuration install.md showed could not start: an empty catalog
without control.enabled is refused as having nothing to serve. The one
init writes sets it, and a test holds that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 12:44:27 +02:00

174 lines
5.1 KiB
Go

package cli
import (
"bytes"
"context"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"gitea.mynodes.xyz/ilker/llmgw/internal/config"
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
)
func newTestInit(t *testing.T) (*serverInit, *bytes.Buffer) {
t.Helper()
root := t.TempDir()
var out bytes.Buffer
return &serverInit{
etcDir: filepath.Join(root, "etc"),
stateDir: filepath.Join(root, "state"),
pkiDir: filepath.Join(root, "etc", "pki"),
admin: "admin@example.com",
out: &out,
}, &out
}
func TestInitLeavesAControlPlaneThatStarts(t *testing.T) {
in, out := newTestInit(t)
if err := in.run(context.Background()); err != nil {
t.Fatalf("%v\n%s", err, out)
}
cfg, err := config.Load(in.configPath())
if err != nil {
t.Fatal(err)
}
// The configuration install.md used to show had an empty catalog and no
// control.enabled, which the server refuses as having nothing to serve.
if !cfg.ControlPlaneMode() {
t.Error("the written configuration does not run a control plane")
}
if !cfg.AgentTLS.Enabled() || !cfg.Auth.Enabled {
t.Error("mutual TLS and authentication should both be on")
}
for _, f := range []string{"ca.pem", "server.pem", "server-key.pem"} {
if _, err := os.Stat(filepath.Join(in.etcDir, f)); err != nil {
t.Error(err)
}
}
s, err := store.Open(context.Background(), cfg.Auth.Store)
if err != nil {
t.Fatal(err)
}
defer s.Close()
u, err := s.UserByEmail(context.Background(), "admin@example.com")
if err != nil {
t.Fatal(err)
}
if !u.IsAdmin || u.State != store.UserActive || u.ProjectID != "default" {
t.Errorf("administrator not ready to use: %+v", u)
}
if !strings.Contains(out.String(), "password: ") {
t.Error("the generated password was not shown")
}
}
func TestInitAgainChangesNothing(t *testing.T) {
in, out := newTestInit(t)
if err := in.run(context.Background()); err != nil {
t.Fatalf("%v\n%s", err, out)
}
cert, _ := os.ReadFile(filepath.Join(in.etcDir, "server.pem"))
again := *in
again.changed = false
out.Reset()
if err := again.run(context.Background()); err != nil {
t.Fatalf("%v\n%s", err, out)
}
if again.changed {
t.Errorf("a second run changed something:\n%s", out)
}
if strings.Contains(out.String(), "password: ") {
t.Error("a second run must not reset the administrator's password")
}
if now, _ := os.ReadFile(filepath.Join(in.etcDir, "server.pem")); !bytes.Equal(now, cert) {
t.Error("the certificate was reissued without reason")
}
}
func TestInitWithANewHostKeepsTheOldOnes(t *testing.T) {
in, out := newTestInit(t)
in.hosts = []string{"old.internal"}
if err := in.run(context.Background()); err != nil {
t.Fatalf("%v\n%s", err, out)
}
in.hosts = []string{"new.internal"}
if err := in.run(context.Background()); err != nil {
t.Fatalf("%v\n%s", err, out)
}
cert, err := readCert(filepath.Join(in.etcDir, "server.pem"))
if err != nil {
t.Fatal(err)
}
for _, h := range []string{"old.internal", "new.internal"} {
if !slices.Contains(cert.DNSNames, h) {
t.Errorf("certificate lost %s: %v", h, cert.DNSNames)
}
}
}
func TestInitNeverOverwritesTheConfiguration(t *testing.T) {
in, out := newTestInit(t)
if err := os.MkdirAll(in.etcDir, 0o755); err != nil {
t.Fatal(err)
}
mine := "# mine\ncontrol:\n enabled: true\nauth:\n enabled: true\n store: " +
filepath.Join(in.stateDir, "mine.db") + "\n"
if err := os.WriteFile(in.configPath(), []byte(mine), 0o640); err != nil {
t.Fatal(err)
}
if err := in.run(context.Background()); err != nil {
t.Fatalf("%v\n%s", err, out)
}
if got, _ := os.ReadFile(in.configPath()); string(got) != mine {
t.Error("an existing server.yaml was rewritten")
}
// And the store it names is the one used.
if _, err := os.Stat(filepath.Join(in.stateDir, "mine.db")); err != nil {
t.Error(err)
}
}
func TestTheInitUnitIsTheShippedUnit(t *testing.T) {
b, err := os.ReadFile("../../deploy/systemd/llmgw-server.service")
if err != nil {
t.Fatal(err)
}
if string(b) != serverUnit {
t.Error("deploy/systemd/llmgw-server.service and the unit 'server init' writes have drifted")
}
}
func TestInitKeepsCertificatesFromAnAuthorityElsewhere(t *testing.T) {
in, out := newTestInit(t)
// Issued on another machine by an authority that never comes here.
elsewhere := &serverInit{etcDir: in.etcDir, pkiDir: filepath.Join(t.TempDir(), "pki"), out: &bytes.Buffer{}}
if err := os.MkdirAll(in.etcDir, 0o755); err != nil {
t.Fatal(err)
}
if err := elsewhere.ensureCerts(context.Background()); err != nil {
t.Fatal(err)
}
before, _ := os.ReadFile(filepath.Join(in.etcDir, "server.pem"))
if err := in.run(context.Background()); err != nil {
t.Fatalf("%v\n%s", err, out)
}
if after, _ := os.ReadFile(filepath.Join(in.etcDir, "server.pem")); !bytes.Equal(before, after) {
t.Error("a certificate from another authority was replaced")
}
if _, err := os.Stat(filepath.Join(in.pkiDir, "ca-key.pem")); err == nil {
t.Error("an authority was created beside one that already exists elsewhere")
}
in.hosts = []string{"new.internal"}
if err := in.run(context.Background()); err == nil {
t.Error("--host cannot be honoured without the authority, and should say so")
}
}