Files
mesh-host/internal/bootstrap/choices_test.go
jschoubben 21474b0144 The installer goes as far as it can, and asks where a human must choose
Twelve steps made a mesh that RUNS and then said "what remains is somebody
else's". The seven things that turn it into a mesh that WORKS — the shared base,
a database provider, the catalogue, the private network, the packet filter — were
typed afterwards, which is how they went missing for weeks without anything
complaining.

Six more steps now: base, store, catalogue, network, filter, extras. Everything
in them is module add, build, assign and push — the same verbs a person types,
through the same commands, so the installer and an operator remain one act.

Where a human must choose, the installer asks. A choice resolves in the order a
person expects: the flag wins; a lone option answers itself ALOUD, because "it
chose for me" and "there was nothing to choose" read identically afterwards
unless one speaks; a terminal is asked; a default fills in; and a required
choice nothing answered refuses naming its flag — a guessed packet filter is a
machine somebody else configured. The filter is required, so the question is
which, not whether. A run without a terminal (the lab, --json) is never left
waiting on a prompt nobody will answer.

Placement is part of the network step, not a separate act — a lesson paid for:
the module installed, the names file was written with no names in it, and
everything reported success because nobody had said where the machine IS. The
hub endpoint derives from the broker address when unsaid: the host other
machines dial is one fact, not two that drift.

Extras fail the run rather than soft-fail: somebody asked for them by name, and
a mesh reporting success minus one thing is reporting the wrong thing.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-15 21:56:23 +02:00

65 lines
2.4 KiB
Go

package bootstrap
import (
"strings"
"testing"
)
func quietly(string) {}
// A flag answers, and answers wrongly is refused naming what would have worked.
func TestAFlagAnswersAndAWrongOneIsRefused(t *testing.T) {
filter := Choice{Name: "packet-filter", Options: []string{"nftables", "ufw"}}
got, err := decide(filter, "ufw", nil, quietly)
if err != nil || got != "ufw" {
t.Fatalf("an explicit answer was not taken: %q, %v", got, err)
}
_, err = decide(filter, "iptables", nil, quietly)
if err == nil {
t.Fatal("an answer nothing offers was accepted")
}
if !strings.Contains(err.Error(), "nftables") || !strings.Contains(err.Error(), "ufw") {
t.Fatalf("the refusal does not say what would have worked: %v", err)
}
}
// A lone option answers itself — and says so, because "it chose for me" and "there was nothing to
// choose" read identically afterwards unless one of them speaks.
func TestALoneOptionAnswersItselfAloud(t *testing.T) {
var said []string
got, err := decide(Choice{Name: "private-network", Options: []string{"wireguard"}},
"", nil, func(line string) { said = append(said, line) })
if err != nil || got != "wireguard" {
t.Fatalf("the only option was not taken: %q, %v", got, err)
}
if len(said) == 0 || !strings.Contains(said[0], "only option") {
t.Fatalf("choosing silently: %v", said)
}
}
// A terminal is asked; an empty answer takes the default when there is one.
func TestATerminalIsAskedAndEmptyTakesTheDefault(t *testing.T) {
asked := 0
prompt := func(c Choice) (string, error) { asked++; return "", nil }
got, err := decide(Choice{Name: "extras", Default: "none"}, "", prompt, quietly)
if err != nil || got != "none" || asked != 1 {
t.Fatalf("empty answer at a prompt did not take the default: %q asked=%d %v", got, asked, err)
}
}
// **Unattended and required is a refusal, not a guess.** The lab and any machine without a
// terminal must be answerable by flags — and a required choice with no flag has to stop the run
// naming the flag, because a guessed packet filter is a machine somebody else configured.
func TestUnattendedAndRequiredRefusesNamingTheFlag(t *testing.T) {
_, err := decide(Choice{Name: "packet-filter", Options: []string{"nftables", "ufw"}},
"", nil, quietly)
if err == nil {
t.Fatal("a required choice was guessed for an unattended run")
}
if !strings.Contains(err.Error(), "--packet-filter") {
t.Fatalf("the refusal does not name the flag that answers it: %v", err)
}
}