Files
llmgw/internal/cli/enginecheck_test.go
Ilker ManapandClaude Opus 5 5b425c32e5 Ship binaries with releases, and ask engines whether our flags still exist
A release was a git tag. Installing llmgw on a node meant having a Go
toolchain, and the onboarding script copied a binary from wherever it was run —
which works and quietly makes whoever runs it the build server, with the fleet
ending up on whatever was in their working tree. Tags now build static
linux/amd64, linux/arm64 and darwin binaries and attach them. CGO_ENABLED=0 is
the point: it has to run on a node whose libc is older than the builder's, and
llmgw has no C dependency because modernc.org/sqlite is pure Go precisely so
this stays true. Verified: "not a dynamic executable", runs on a node, reports
its version.

`ctl engine-check` closes the other half of the phase 4 gap. llmgw's launch
arguments are read from each engine's documentation, and engines rename flags
between versions; when that happens the failure arrives minutes into a model
load, in the engine's own error message, on a node nobody was watching. It
builds the widest command llmgw would ever produce — multi-node, since a flag
that only appears there is the one nobody notices has gone — and checks each
flag against the engine's own --help. No GPU: a CLI prints its help anywhere.
Verified against llama.cpp b10991, all seven flags accepted.

Flags are matched on a word boundary, so --port is not satisfied by
--port-range. Without that, a renamed flag would sail through the check that
exists to catch renamed flags.

It also warns when the help text looks truncated, which is how the first run
went: llama.cpp's help cut to 400 of its 725 lines reported five flags as
missing that the running cluster uses.

TestSidecarRunsBeforeEngine was flaky under a full run, and worse than flaky —
its wait loop fell through on timeout, so a slow machine turned the ordering
assertion into a no-op and the failure surfaced later as something else. It now
asserts, with a deadline that suits a machine running every other package at
the same time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:44:29 +02:00

72 lines
2.3 KiB
Go

package cli
import (
"testing"
)
func TestAFlagIsNotSatisfiedByALongerOne(t *testing.T) {
// The failure this guards against is silent: --port would be reported as
// present because --port-range is documented, and a renamed flag would sail
// through the check that exists to catch renamed flags.
help := " --port-range N a range\n --model-name S a name\n"
if mentions(help, "--port") {
t.Error("--port was satisfied by --port-range")
}
if mentions(help, "--model") {
t.Error("--model was satisfied by --model-name")
}
if !mentions(help, "--port-range") {
t.Error("--port-range was not found")
}
}
func TestFlagsAreFoundInTheShapesHelpTextUses(t *testing.T) {
help := `
-m, --model FNAME model path
--host HOST ip address
--parallel, -np N slots
--metrics enable prometheus
--alias=NAME served name
`
for _, flag := range []string{"--model", "--host", "--parallel", "--metrics", "--alias"} {
if !mentions(help, flag) {
t.Errorf("%s was not found in a help text that documents it", flag)
}
}
for _, flag := range []string{"--nonsense", "--mode", "--para"} {
if mentions(help, flag) {
t.Errorf("%s was found in a help text that does not document it", flag)
}
}
}
func TestTheRepresentativeShardIsTheWidestOne(t *testing.T) {
// A flag that only appears in the multi-node case is exactly the one nobody
// notices has been renamed, so the command built for checking has to be the
// widest llmgw would ever produce.
spec := representativeShard("vllm", 2, 8, 4)
if spec.NNodes != 2 || spec.Profile.Nodes != 2 {
t.Errorf("not a multi-node shard: %+v", spec)
}
if len(spec.GPUIndices) != 4 {
t.Errorf("%d GPUs, want 4", len(spec.GPUIndices))
}
if spec.LeaderAddr == "" || spec.LeaderPort == 0 {
t.Error("no rendezvous point, so the flags that carry it will not appear")
}
}
func TestATruncatedHelpIsSuspected(t *testing.T) {
// Found by truncating llama.cpp's 725-line help to 400 lines and being told,
// confidently, that five flags the running cluster uses do not exist.
full := " --help show this\n --model F path\n"
if !mentions(full, "--help") {
t.Fatal("the guard's own signal is missing from a complete help")
}
truncated := " --model F path\n"
if mentions(truncated, "--help") {
t.Error("a truncated help was taken for a complete one")
}
}