An agent could make a repository of its own, or fork one the mesh builds from, commit a module.json naming sudo or mesh-host, and ask the build verb for it: the outcome was registered under that name, and the next push made whoever wrote it root on every node (novox/hq ADR 0266 §7, the review of 2026-10-09). The trunk rule checked the trunk of the repository built, which was the agent's. The take-in, which every outcome reaches whichever verb asked it, now registers a module only from its registered repository, and a new module only from a repository the catalogue already builds from (a merge adding one); anything else only when the build request was kept as asked at the controller's terminal (migration 0084). Through a verb, a build of a repository the catalogue builds nothing from is not asked at all.
187 lines
8.3 KiB
Go
187 lines
8.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// The routes to root a review of ADR 0266 found (novox/hq ADR 0266 §7): an agent makes a repository of its
|
|
// own — or forks one the mesh builds from — commits a module.json naming a module the mesh runs everywhere
|
|
// (`sudo`, granting itself a rule without a password; `mesh-host`, the node-engine), and asks the `build` verb
|
|
// for it. Its outcome was registered under that name from the agent's repository, and the next push sent it.
|
|
|
|
// onTrunk is an outcome of a commit on its repository's trunk, as the build seat says it.
|
|
func onTrunk(id, repository, seat, path string, manifest map[string]any) link.BuildResult {
|
|
raw, _ := json.Marshal(manifest)
|
|
r := link.BuildResult{ID: id, Repository: "http://forge.internal:20000/" + repository + ".git", Path: path,
|
|
Ref: "main", On: "anchor", Commit: "c0ffee0123456789", Manifest: raw,
|
|
Trunk: "main", OnTrunk: true, Branches: []string{"main"}}
|
|
if seat != "" {
|
|
r.Source = &link.SourceOnSeat{Seat: seat, Repository: repository}
|
|
}
|
|
return r
|
|
}
|
|
|
|
// keptAsked keeps a build request as the asker would: through a verb, or at the terminal.
|
|
func keptAsked(t *testing.T, inv *inventory.Inventory, id, repository string, atTerminal bool) {
|
|
t.Helper()
|
|
if err := inv.RecordBuildRequest(t.Context(), inventory.BuildRequest{ID: id, Repository: repository, Seat: "git",
|
|
For: "build", AtTerminal: atTerminal}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// theCatalogue is a mesh whose sudo is built from the catalogue repository and whose node-engine from its own.
|
|
func theCatalogue(t *testing.T) *stores {
|
|
t.Helper()
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
for _, b := range []link.BuildResult{
|
|
onTrunk("build-sudo", "novox/mesh-catalog", "git", "modules/sudo", map[string]any{"module": "sudo", "version": "1"}),
|
|
onTrunk("build-host", "novox/mesh-host", "git", "", map[string]any{"module": "mesh-host", "version": "1"}),
|
|
} {
|
|
keptAsked(t, open.inventory, b.ID, b.Source.Repository, true)
|
|
if _, _, err := takeIn(ctx, open.inventory, b); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return open
|
|
}
|
|
|
|
func TestABuildFromAnAgentsRepositoryIsNotRegisteredAsAModuleTheMeshHolds(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
for _, c := range []struct {
|
|
name, repository, path, module string
|
|
}{
|
|
{"its own repository naming sudo", "agent/sudo", "modules/sudo", "sudo"},
|
|
{"a fork of the catalogue", "agent/mesh-catalog", "modules/sudo", "sudo"},
|
|
{"a fork of the node-engine", "agent/mesh-host", "", "mesh-host"},
|
|
} {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
id := "build-" + strings.ReplaceAll(c.repository, "/", "-")
|
|
keptAsked(t, open.inventory, id, c.repository, false) // through the build verb
|
|
evil := onTrunk(id, c.repository, "git", c.path, map[string]any{"module": c.module, "version": "evil"})
|
|
_, _, err := takeIn(ctx, open.inventory, evil)
|
|
if !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("a build of %s was taken in as %s: %v", c.repository, c.module, err)
|
|
}
|
|
shelf, err := open.inventory.Catalogue(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := shelf[c.module].Version; got != "1" {
|
|
t.Fatalf("%s is now %q, from %s", c.module, got, c.repository)
|
|
}
|
|
if _, found, _ := open.inventory.BuildByID(ctx, id); !found {
|
|
t.Errorf("the refused build %s is not recorded", id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestANewModuleFromARepositoryTheCatalogueDoesNotBuildFromIsNotRegistered(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
keptAsked(t, open.inventory, "build-new", "agent/tools", false)
|
|
_, _, err := takeIn(ctx, open.inventory, onTrunk("build-new", "agent/tools", "git", "",
|
|
map[string]any{"module": "agent-tools", "version": "1"}))
|
|
if !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("a new module from an agent's repository was taken in: %v", err)
|
|
}
|
|
// And one asked of nobody here — an outcome on the bus no request was kept for — the same.
|
|
_, _, err = takeIn(ctx, open.inventory, onTrunk("build-unasked", "agent/tools", "git", "",
|
|
map[string]any{"module": "agent-tools", "version": "1"}))
|
|
if !errors.Is(err, errNotItsSource) {
|
|
t.Fatalf("an outcome nobody asked for was taken in: %v", err)
|
|
}
|
|
if shelf, _ := open.inventory.Catalogue(ctx); shelf["agent-tools"].Module != "" {
|
|
t.Fatal("the refused module is in the catalogue")
|
|
}
|
|
}
|
|
|
|
// The operator at the terminal may still move a module, or add one from a new repository.
|
|
func TestAtTheTerminalAnotherRepositoryIsRegistered(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
keptAsked(t, open.inventory, "build-moved", "novox/sudo", true)
|
|
if _, _, err := takeIn(ctx, open.inventory, onTrunk("build-moved", "novox/sudo", "git", "",
|
|
map[string]any{"module": "sudo", "version": "2"})); err != nil {
|
|
t.Fatalf("a move the operator asked for at the terminal was refused: %v", err)
|
|
}
|
|
if src, _ := open.inventory.SourceOf(ctx, "sudo"); src.Repository != "novox/sudo" {
|
|
t.Fatalf("sudo is built from %q", src.Repository)
|
|
}
|
|
keptAsked(t, open.inventory, "build-external", "someone/app", true)
|
|
if _, _, err := takeIn(ctx, open.inventory, onTrunk("build-external", "someone/app", "", "",
|
|
map[string]any{"module": "app", "version": "1"})); err != nil {
|
|
t.Fatalf("a new module the operator asked for at the terminal was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
// The delivery's flow is untouched: a merge's rebuild of a module from its own repository, and a merge adding
|
|
// a module to a repository the catalogue builds from (novox/hq issue 300), are registered with no terminal.
|
|
func TestADeliveryFromTheRegisteredRepositoryIsRegistered(t *testing.T) {
|
|
open := theCatalogue(t)
|
|
ctx := t.Context()
|
|
rebuilt := onTrunk("build-plan", "novox/mesh-catalog", "git", "modules/sudo", map[string]any{"module": "sudo", "version": "2"})
|
|
if err := open.inventory.RecordBuildRequest(ctx, inventory.BuildRequest{ID: rebuilt.ID,
|
|
Repository: "novox/mesh-catalog", Seat: "git", Path: "modules/sudo", For: "plan"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := takeIn(ctx, open.inventory, rebuilt); err != nil {
|
|
t.Fatalf("a plan's build of the module's own repository was refused: %v", err)
|
|
}
|
|
added := onTrunk("build-merge", "novox/mesh-catalog", "git", "modules/zram", map[string]any{"module": "zram", "version": "1"})
|
|
if _, _, err := takeIn(ctx, open.inventory, added); err != nil {
|
|
t.Fatalf("a module a merge added to the catalogue repository was refused: %v", err)
|
|
}
|
|
shelf, _ := open.inventory.Catalogue(ctx)
|
|
if shelf["sudo"].Version != "2" || shelf["zram"].Module == "" {
|
|
t.Fatalf("not registered: sudo %q, zram %q", shelf["sudo"].Version, shelf["zram"].Module)
|
|
}
|
|
}
|
|
|
|
// Through a verb, a build of a repository the catalogue builds nothing from is not even asked: the build node
|
|
// would run what the agent wrote.
|
|
func TestAVerbAsksNoBuildOfARepositoryTheCatalogueDoesNotBuildFrom(t *testing.T) {
|
|
theCatalogue(t)
|
|
ctx := t.Context()
|
|
t.Setenv(verbVar, "build")
|
|
t.Setenv(link.CallerVar, "node-tools.anchor, through the mesh-controller seat")
|
|
err := verbMayAsk(ctx, buildSource{Repository: "agent/sudo", Seat: "git"}, "http://forge.internal:20000/agent/sudo.git")
|
|
var policy *heldAtTheTerminal
|
|
if !errors.As(err, &policy) {
|
|
t.Fatalf("a verb's build of an agent's repository was asked: %v", err)
|
|
}
|
|
if err := verbMayAsk(ctx, buildSource{Repository: "novox/mesh-catalog", Seat: "git"},
|
|
"http://forge.internal:20000/novox/mesh-catalog.git"); err != nil {
|
|
t.Fatalf("a verb's build of the catalogue repository was refused: %v", err)
|
|
}
|
|
t.Setenv(verbVar, "")
|
|
t.Setenv(link.CallerVar, "")
|
|
if err := verbMayAsk(ctx, buildSource{Repository: "agent/sudo", Seat: "git"}, ""); err != nil {
|
|
t.Fatalf("the terminal was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
// asTheOperator keeps a build as asked at the controller's terminal, as the operator's first build of a module
|
|
// from a repository the catalogue does not yet build from is (novox/hq ADR 0266), and hands it back.
|
|
func asTheOperator(t *testing.T, inv *inventory.Inventory, b link.BuildResult) link.BuildResult {
|
|
t.Helper()
|
|
repository, seat := b.Repository, ""
|
|
if b.Source != nil {
|
|
repository, seat = b.Source.Repository, b.Source.Seat
|
|
}
|
|
if err := inv.RecordBuildRequest(t.Context(), inventory.BuildRequest{ID: b.ID, Repository: repository, Seat: seat,
|
|
Path: b.Path, For: "build", AtTerminal: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return b
|
|
}
|