Files
oikos/internal/httpapi/pct_create_test.go
dtoro 7a7ce2b89b
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
fix: gateway pre-flight check could never actually fail
Verified live that after deploying the "fixed" bridge-bound pre-flight, it
still let a known-bad vmbr0+192.168.8.2 config straight through to a full
pct_create with no error. Root cause: the check used
`strings.Contains(pingOut, "REACHABLE")` against markers "REACHABLE" /
"UNREACHABLE" — but "UNREACHABLE" contains "REACHABLE" as a substring, so the
containment check was true for BOTH outcomes. The pre-flight was structurally
incapable of ever failing, regardless of the actual ping result.

Fixed with distinct, non-overlapping markers (PREFLIGHT_OK/PREFLIGHT_FAIL)
and exact-match comparison, pulled into a small gatewayPreflightPassed()
helper with a unit test asserting the exact historical bug case
("UNREACHABLE" must be false) so this bug class can't silently recur.

Re-verified live end-to-end: manually re-tested the exact ping command
(confirmed UNREACHABLE via vmbr0), and this was caught only by actually
running the check against production, not by reading the code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:02:42 +02:00

158 lines
5.4 KiB
Go

package httpapi
import (
"encoding/json"
"strings"
"testing"
)
// TestFlexBoolUnmarshal covers the exact production failure: the LLM emitted
// `"privileged":0` / `"nesting":1` (numbers) and the strict bool field made the
// already-approved pct_create execution fail to parse, so the LXC was never
// created.
func TestFlexBoolUnmarshal(t *testing.T) {
type cfg struct {
Privileged flexBool `json:"privileged"`
Nesting flexBool `json:"nesting"`
}
cases := []struct {
in string
privileged bool
nesting bool
wantErr bool
}{
{`{"privileged":0,"nesting":1}`, false, true, false}, // the prod payload
{`{"privileged":false,"nesting":true}`, false, true, false}, // canonical
{`{"privileged":"1","nesting":"0"}`, true, false, false}, // stringified
{`{"privileged":"true","nesting":"no"}`, true, false, false},
{`{}`, false, false, false}, // absent → zero
{`{"privileged":"maybe"}`, false, false, true},
}
for _, c := range cases {
var out cfg
err := json.Unmarshal([]byte(c.in), &out)
if (err != nil) != c.wantErr {
t.Fatalf("%s: err=%v wantErr=%v", c.in, err, c.wantErr)
}
if c.wantErr {
continue
}
if bool(out.Privileged) != c.privileged || bool(out.Nesting) != c.nesting {
t.Errorf("%s: got priv=%v nest=%v want priv=%v nest=%v",
c.in, bool(out.Privileged), bool(out.Nesting), c.privileged, c.nesting)
}
}
}
func TestResolveTemplate(t *testing.T) {
avail := []string{
"debian-12-standard_12.7-1_amd64.tar.zst",
"debian-13-standard_13.0-1_amd64.tar.zst",
"ubuntu-24.04-standard_24.04-2_amd64.tar.zst",
}
cases := []struct {
requested string
want string
}{
{"debian-13-standard_13.0-1_amd64.tar.zst", "debian-13-standard_13.0-1_amd64.tar.zst"}, // exact
{"debian-13", "debian-13-standard_13.0-1_amd64.tar.zst"}, // prefix
{"", "debian-13-standard_13.0-1_amd64.tar.zst"}, // auto newest debian
{"debian-99", "debian-13-standard_13.0-1_amd64.tar.zst"}, // miss prefix → auto debian
}
for _, c := range cases {
if got := resolveTemplate(c.requested, avail); got != c.want {
t.Errorf("resolveTemplate(%q): got %q want %q", c.requested, got, c.want)
}
}
if got := resolveTemplate("debian-13", nil); got != "" {
t.Errorf("empty cache should yield empty, got %q", got)
}
}
// TestJSONErrValidForNastyOutput guards the bug where command output with
// quotes/backslashes/newlines produced invalid JSON, failing the ::jsonb cast
// and silently dropping the execution's final status update.
func TestJSONErrValidForNastyOutput(t *testing.T) {
nasty := "CT 132 already exists on node \"hubris\"\n\tpath C:\\x\r\n\x00 100%"
for _, payload := range [][]byte{
jsonErr("%s", nasty),
jsonErr("list templates on %s: %s", "host:strong", nasty),
} {
var m map[string]any
if err := json.Unmarshal(payload, &m); err != nil {
t.Fatalf("jsonErr produced invalid JSON: %v\npayload=%s", err, payload)
}
if _, ok := m["error"]; !ok {
t.Errorf("missing error key: %s", payload)
}
}
}
// TestGatewayPreflightPassed guards the exact bug found live: "UNREACHABLE"
// contains "REACHABLE" as a substring, so a strings.Contains(out,"REACHABLE")
// check is true for BOTH outcomes and can never fail. Exact-match only.
func TestGatewayPreflightPassed(t *testing.T) {
cases := []struct {
out string
want bool
}{
{"PREFLIGHT_OK", true},
{"PREFLIGHT_OK\n", true},
{" PREFLIGHT_OK ", true},
{"PREFLIGHT_FAIL", false},
{"PREFLIGHT_FAIL\n", false},
{"", false},
{"some garbage output", false},
// the specific historical bug: a naive substring check on the old
// REACHABLE/UNREACHABLE markers would have called this true.
{"UNREACHABLE", false},
}
for _, c := range cases {
if got := gatewayPreflightPassed(c.out); got != c.want {
t.Errorf("gatewayPreflightPassed(%q) = %v, want %v", c.out, got, c.want)
}
}
}
func TestProvisionScript(t *testing.T) {
s := provisionScript([]string{"docker.io", "git"}, "echo hi > /root/x")
// Network/DNS gate must come before apt.
gate := strings.Index(s, "getent hosts")
apt := strings.Index(s, "apt-get update")
post := strings.Index(s, "echo hi > /root/x")
if gate < 0 || apt < 0 || post < 0 {
t.Fatalf("missing sections: gate=%d apt=%d post=%d\n%s", gate, apt, post, s)
}
if !(gate < apt && apt < post) {
t.Errorf("wrong ordering: gate=%d apt=%d post=%d", gate, apt, post)
}
if !strings.Contains(s, "nameserver 1.1.1.1") {
t.Error("missing DNS self-heal fallback")
}
if !strings.Contains(s, "docker.io git") {
t.Error("packages not joined into install line")
}
// No packages: no apt lines, but post_install and gate still present.
s2 := provisionScript(nil, "systemctl status foo")
if strings.Contains(s2, "apt-get install") {
t.Error("apt install should be absent when no packages requested")
}
if !strings.Contains(s2, "systemctl status foo") || !strings.Contains(s2, "getent hosts") {
t.Error("post_install or gate missing in no-package case")
}
}
func TestSanitizePkgs(t *testing.T) {
in := []string{"docker.io", "git", "rm -rf /", "curl;wget", "python3-pip", ""}
got := sanitizePkgs(in)
want := map[string]bool{"docker.io": true, "git": true, "python3-pip": true}
if len(got) != len(want) {
t.Fatalf("got %v want keys %v", got, want)
}
for _, g := range got {
if !want[g] {
t.Errorf("unexpected package survived sanitize: %q", g)
}
}
}