Files
mesh-host/internal/link/handover_test.go
T
jochen e74ee82a76
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
Renew the engine placed by hand to the proved build, and take a hand-over from the controller's terminal (hq issue 356)
The file on PATH was the engine a person copied at installation, and every delivered engine ran from the versions directory: the operator typed hand-over and a build from before it answered "unexpected argument". A proved engine now copies itself over that file when the bytes differ (never a link). And the hand-over of a directory used as found (issue 339) gets its channel: the controller's terminal asks this node's engine on mesh.node.<node>.ask.hand-over; the engine judges every value — the ask's size and shape, who asked, the path as it states it, a directory and no link, through no link — and records it or refuses and records nothing. Every text the engine addresses to the operator names the nox line (ADR 0272); the engine's own subcommands are install-time and rescue, and its usage says so.
2026-10-09 18:34:10 +02:00

90 lines
3.4 KiB
Go

package link
import (
"encoding/json"
"errors"
"sort"
"strings"
"testing"
)
// The hand-over's ask and answer hold these field names; the controller's side holds the same list
// (mesh-controller internal/link, TestTheHandOverAskAndAnswerKeepTheirFieldNames).
func TestTheHandOverAskAndAnswerKeepTheirFieldNames(t *testing.T) {
body, _ := json.Marshal(HandOverAsk{Path: "/srv/notes", By: "jo through mesh-cli on anchor"})
if got := keysOf(t, body); got != "by path" {
t.Fatalf("the ask's fields are %q", got)
}
body, _ = json.Marshal(HandOverAnswer{Said: "s", Refused: "r"})
if got := keysOf(t, body); got != "refused said" {
t.Fatalf("the answer's fields are %q", got)
}
if AskHandOverSubject("laptop") != "mesh.node.laptop.ask.hand-over" {
t.Fatalf("the subject is %q", AskHandOverSubject("laptop"))
}
}
func keysOf(t *testing.T, body []byte) string {
t.Helper()
var m map[string]any
if err := json.Unmarshal(body, &m); err != nil {
t.Fatal(err)
}
var keys []string
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return strings.Join(keys, " ")
}
// Every value of an ask is judged before the handler sees it, and a refusal hands nothing to the handler:
// an ask too large, not a hand-over, naming nobody, naming somebody over more than a line, or naming no
// directory. What passes reaches the handler with its path as given and who asked trimmed.
func TestAHandOverAskIsJudgedBeforeAnythingIsRecorded(t *testing.T) {
var given []string
handle := func(path, by string) (string, error) {
given = append(given, path+" by "+by)
if path == "/refused" {
return "", errors.New("not a directory this machine uses as found")
}
return path + " is handed to the mesh", nil
}
refusals := map[string][]byte{
"too large": []byte(`{"path":"/` + strings.Repeat("a", MaxHandOverAsk) + `","by":"jo"}`),
"not a hand-over": []byte(`not json`),
"nobody": []byte(`{"path":"/srv/notes","by":" "}`),
"two lines": []byte(`{"path":"/srv/notes","by":"jo\nroot"}`),
"a name too long": []byte(`{"path":"/srv/notes","by":"` + strings.Repeat("x", MaxHandOverBy+1) + `"}`),
"no directory": []byte(`{"path":"","by":"jo"}`),
"a NUL in the name": []byte(`{"path":"/srv/notes","by":"jo\u0000"}`),
}
for name, body := range refusals {
a := AnswerHandOver(handle, body)
if a.Refused == "" || a.Said != "" {
t.Fatalf("%s was answered %+v; want a refusal", name, a)
}
if !strings.Contains(a.Refused, "nothing was handed over") {
t.Fatalf("%s's refusal does not say nothing was handed over: %q", name, a.Refused)
}
}
if len(given) != 0 {
t.Fatalf("the handler was given %v although every ask was refused", given)
}
if a := AnswerHandOver(nil, []byte(`{"path":"/srv/notes","by":"jo"}`)); a.Refused == "" {
t.Fatal("an engine with no handler took a hand-over")
}
a := AnswerHandOver(handle, []byte(`{"path":"/refused","by":"jo"}`))
if a.Refused != "not a directory this machine uses as found" || a.Said != "" {
t.Fatalf("the handler's refusal was answered as %+v", a)
}
a = AnswerHandOver(handle, []byte(`{"path":"/srv/notes","by":" jo through mesh-cli on anchor "}`))
if a.Said != "/srv/notes is handed to the mesh" || a.Refused != "" {
t.Fatalf("answered %+v", a)
}
if strings.Join(given, "; ") != "/refused by jo; /srv/notes by jo through mesh-cli on anchor" {
t.Fatalf("the handler was given %v", given)
}
}