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
65 lines
2.4 KiB
Go
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)
|
|
}
|
|
}
|