Files
mesh-host/cmd/mesh-host/whatruns_test.go
T
jschoubben 1bdbf47ffa
mesh/delivery merged: source.main moved to novox/mesh-host@0dfbe1a179f2
mesh/check pass 1bdbf47ffa83 on 0dfbe1a179f2 | unit 302 run, 1044 kept, 5 skipped | integration 305 run, 1046 kept, 4 skipped | over bound: unit (slow…
Leave out the node's own copy only by kind and id, never by a shared target (issue 1945, review)
A unit or container of the node's own that shares only its name with a
module's resource is not the module's: dropped, its record became an orphan
the apply removes, stopping the module's unit (1945/AC17).
2026-10-25 14:29:06 +01:00

140 lines
6.8 KiB
Go

package main
import (
"crypto/ed25519"
"encoding/json"
"errors"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/declaration"
"git.novox.be/novox/mesh-sdk/go/delivery"
"github.com/novox/mesh-host/internal/agent"
)
func aFileOf(module, id string) json.RawMessage {
raw, _ := json.Marshal(map[string]any{"id": module + "." + id, "type": "file", "path": "/tmp/r6/" + module + "/" + id,
"content": "x", "module": module})
return raw
}
// What runs is the agent's compositions and the kept declaration's resources for each module the agent does not hold;
// the node's own parts are the kept declaration's until the agent holds the node's composition (ADR 0539 §9). One module
// under the agent must not make the rest invisible to the liveness, units, accounts, libraries, filling and the take act.
func TestWhatRunsIsTheAgentsCompositionsAndTheKeptRest(t *testing.T) {
kept := &declaration.Declaration{Identities: []declaration.Identity{{Module: "db", Name: "mesh-db"}},
Libraries: []declaration.Library{{Library: "media"}}}
everything := []delivery.Composed{
{Node: "n1", Module: "web", Resources: []json.RawMessage{aFileOf("web", "a")}},
{Node: "n1", Module: "db", Resources: []json.RawMessage{aFileOf("db", "a")}}, // still the kept declaration's
}
d, err := whatRuns(everything, false, func() (*declaration.Declaration, error) { return kept, nil })
if err != nil {
t.Fatal(err)
}
if len(d.Resources) != 2 || len(d.Identities) != 1 || len(d.Libraries) != 1 {
t.Fatalf("%d resources, %d identities, %d libraries: the kept modules and the node's kept parts are missing",
len(d.Resources), len(d.Identities), len(d.Libraries))
}
// Once the agent holds the node's composition, its parts are the node's.
node := delivery.Composed{Node: "n1", Own: delivery.NodeOwn{Identities: []declaration.Identity{{Module: "web", Name: "mesh-web"}}}}
d, err = whatRuns(append(everything, node), true, func() (*declaration.Declaration, error) { return nil, errors.New("not read") })
if err != nil {
t.Fatal(err)
}
if len(d.Identities) != 1 || d.Identities[0].Name != "mesh-web" || len(d.Libraries) != 0 {
t.Fatalf("the node's parts: %+v %+v", d.Identities, d.Libraries)
}
}
// What the take act, the judgement of directories and the filling read is what runs, not the frozen kept declaration.
func TestDeclaredNowReadsWhatRunsWhenTheAgentIsRunning(t *testing.T) {
setRunsHere(func(string, ed25519.PublicKey) (*declaration.Declaration, error) {
return &declaration.Declaration{Version: 1, Resources: nil, Provisioner: "from-agent"}, nil
})
defer setRunsHere(nil)
d, err := declaredNow("/nowhere", nil)
if err != nil || d.Provisioner != "from-agent" {
t.Fatalf("declaredNow read %+v (%v), not what runs", d, err)
}
}
// 1945/AC17 (unit): a resource the node's own composition shares with a running module's composition is declared once,
// the module's: the declaration joined of both names each file once, so the filling writes it; the node's own resources
// no module declares stay. (Live from 2026-10-25 12:43: "2 resources declare X" on every node.)
func TestTheNodesOwnCopyOfAModulesResourceIsLeftOut(t *testing.T) {
own := delivery.Composed{Node: "n1", Resources: []json.RawMessage{aFileOf("zsh", "rc"), aFileOf("mesh-wireguard", "conf")},
Filled: []string{"/tmp/r6/zsh/rc"}}
zsh := delivery.Composed{Node: "n1", Module: "zsh", FP: "src2:aa", Resources: []json.RawMessage{aFileOf("zsh", "rc")},
Filled: []string{"/tmp/r6/zsh/rc"}}
d, err := declarationOf(own, zsh)
if err != nil {
t.Fatal(err)
}
at := map[string]int{}
for _, r := range d.Resources {
at[r.Target()]++
}
if at["/tmp/r6/zsh/rc"] != 1 || at["/tmp/r6/mesh-wireguard/conf"] != 1 || len(d.Resources) != 2 {
t.Errorf("the joined declaration declares %v, want each path once", at)
}
if !d.Fills("/tmp/r6/zsh/rc") {
t.Errorf("the filled path is no longer named")
}
// The same at the node's own apply: what the others declare is theirs.
trimmed, dropped := ownWithout(own, []delivery.Composed{zsh})
if len(trimmed.Resources) != 1 || len(dropped) != 1 || dropped[0] != "zsh.rc (zsh)" {
t.Errorf("the node's own without zsh's: %d resources, dropped %v", len(trimmed.Resources), dropped)
}
if len(own.Resources) != 2 {
t.Errorf("ownWithout changed its argument")
}
// A node's own composition alone is all of it.
if d, err := declarationOf(own); err != nil || len(d.Resources) != 2 {
t.Errorf("the node's own alone: %v (%v)", d, err)
}
}
// 1945/AC18 (unit): the kept declaration's filled paths go to the module whose file is at each path, the node's own to
// the node's; and what runs from it, joined with the node's composition, names them filled.
func TestTheKeptDeclarationsFilledPathsGoToTheirModules(t *testing.T) {
raw := []byte(`{"resources":[
{"id":"claude-code.settings","type":"file","module":"claude-code","path":"/etc/cc.json"},
{"id":"screen-lock.conf","type":"file","module":"screen-lock","path":"/etc/lock.conf"},
{"id":"host.motd","type":"file","path":"/etc/motd"},
{"id":"zsh.rc","type":"file","module":"zsh","path":"/etc/zshrc"}],
"filled_files":["/etc/cc.json","/etc/lock.conf","/etc/motd"]}`)
by, filled := splitKept(raw)
if len(by["claude-code"]) != 1 || len(by[agent.NodeModule]) != 1 {
t.Fatalf("resources by module: %v", by)
}
if strings.Join(filled["claude-code"], ",") != "/etc/cc.json" || strings.Join(filled["screen-lock"], ",") != "/etc/lock.conf" ||
strings.Join(filled[agent.NodeModule], ",") != "/etc/motd" || len(filled["zsh"]) != 0 {
t.Fatalf("filled by module: %v", filled)
}
cc := delivery.Composed{Node: "n1", Module: "claude-code", Resources: by["claude-code"], Filled: filled["claude-code"]}
node := delivery.Composed{Node: "n1"}
d, err := whatRuns([]delivery.Composed{cc, node}, true, func() (*declaration.Declaration, error) { return nil, errors.New("not read") })
if err != nil || !d.Fills("/etc/cc.json") {
t.Fatalf("what runs does not name claude-code's file filled: %v (%v)", d, err)
}
if splitBy, _ := splitKept([]byte("not json")); splitBy != nil {
t.Errorf("a kept declaration that does not read split into %v", splitBy)
}
}
// 1945/AC17 (unit, review): a resource of the node's own that shares only its target with a module's (another id: a
// unit or a container of the same name) is not the module's, and is neither dropped nor left to be removed.
func TestASharedTargetWithAnotherIDIsNotDropped(t *testing.T) {
ownFile, _ := json.Marshal(map[string]any{"id": "host.zshrc", "type": "file", "path": "/tmp/r6/zsh/rc", "content": "x"})
own := delivery.Composed{Node: "n1", Resources: []json.RawMessage{ownFile}}
zsh := delivery.Composed{Node: "n1", Module: "zsh", FP: "src2:aa", Resources: []json.RawMessage{aFileOf("zsh", "rc")}}
trimmed, dropped := ownWithout(own, []delivery.Composed{zsh})
if len(dropped) != 0 || len(trimmed.Resources) != 1 {
t.Errorf("a resource sharing only its target was dropped: %v", dropped)
}
}