assign and unassign say only what they changed on their node; push <node> lists that node's, push to many counts each and points at status. The once-per-change log is the serving controller's alone: a one-shot command starts with no memory, so it logged every node on every call.
109 lines
4.1 KiB
Go
109 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// An act says what it changed about the node it acted on, and nothing about the rest of the mesh
|
|
// (novox/hq ADR 0207): after the seat dependencies shipped, every `push <node>` and `assign` printed
|
|
// every node's unmet dependencies, a hundred lines around the one about the module just assigned.
|
|
|
|
func aContainer(name string) catalogue.Manifest {
|
|
return catalogue.Manifest{Module: name, Version: "1",
|
|
Resources: []map[string]any{{"id": name, "type": "container", "image": name}}}
|
|
}
|
|
|
|
func TestAnAssignmentSaysOnlyWhatItChangedOnItsOwnNode(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
register(t, open, aContainer("web"))
|
|
register(t, open, aContainer("db"))
|
|
// anchor already lacks a runtime for db: true, and not this act's to say.
|
|
if _, err := open.inventory.Assign(ctx, "anchor", "db"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := open.inventory.Assign(ctx, "laptop", "db"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
said, err := assign(ctx, open, "laptop", "web")
|
|
if err != nil {
|
|
t.Fatalf("%v\n%s", err, said)
|
|
}
|
|
if !strings.Contains(said, "web on laptop depends on "+catalogue.ContainerRuntimeSeat) {
|
|
t.Errorf("the assignment does not say what the module it assigned depends on:\n%s", said)
|
|
}
|
|
for _, not := range []string{"db on laptop", "db on anchor", "anchor:"} {
|
|
if strings.Contains(said, not) {
|
|
t.Errorf("the assignment says %q, which it did not change:\n%s", not, said)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAnAssignmentThatMeetsADependencySaysSo(t *testing.T) {
|
|
shelf := map[string]catalogue.Manifest{
|
|
"web": aContainer("web"),
|
|
"docker": {Module: "docker", Claims: []catalogue.Claim{{Name: catalogue.ContainerRuntimeSeat}},
|
|
Resources: []map[string]any{{"id": "d", "type": "container", "image": "dind"}}},
|
|
}
|
|
lines := unheldChange(shelf, "laptop", []string{"web"}, []string{"web", "docker"})
|
|
if len(lines) != 1 || !strings.Contains(lines[0], "web on laptop now has "+catalogue.ContainerRuntimeSeat+" held") {
|
|
t.Errorf("meeting a dependency said %v", lines)
|
|
}
|
|
// Taking the dependent off says nothing: the dependency went with its module.
|
|
if lines := unheldChange(shelf, "laptop", []string{"web"}, nil); len(lines) != 0 {
|
|
t.Errorf("unassigning the dependent said %v", lines)
|
|
}
|
|
}
|
|
|
|
func TestAPushSaysANamedNodesDependenciesAndCountsTheRest(t *testing.T) {
|
|
unheld := map[string][]catalogue.Unheld{
|
|
"anchor": {{Node: "anchor", Module: "db", Seat: catalogue.ContainerRuntimeSeat}},
|
|
"laptop": {{Node: "laptop", Module: "web", Seat: catalogue.ContainerRuntimeSeat},
|
|
{Node: "laptop", Module: "sshd", Seat: catalogue.ServiceManagerSeat}},
|
|
}
|
|
var named bytes.Buffer
|
|
reportUnheldPushed(&named, true, []string{"laptop"}, unheld)
|
|
got := named.String()
|
|
if !strings.Contains(got, "laptop has 2 unmet") || !strings.Contains(got, "web on laptop") ||
|
|
!strings.Contains(got, "sshd on laptop") || strings.Contains(got, "anchor") {
|
|
t.Errorf("a named push said:\n%s", got)
|
|
}
|
|
var all bytes.Buffer
|
|
reportUnheldPushed(&all, false, []string{"anchor", "laptop", "quiet"}, unheld)
|
|
want := "anchor: 1 unmet seat dependenc(ies) — see `status`\nlaptop: 2 unmet seat dependenc(ies) — see `status`\n"
|
|
if all.String() != want {
|
|
t.Errorf("a push to every node said:\n%s\nwant\n%s", all.String(), want)
|
|
}
|
|
}
|
|
|
|
func TestOnlyTheServingControllerLogsEachChange(t *testing.T) {
|
|
read := func(f func()) string {
|
|
old := os.Stderr
|
|
r, w, _ := os.Pipe()
|
|
os.Stderr = w
|
|
f()
|
|
_ = w.Close()
|
|
os.Stderr = old
|
|
var b bytes.Buffer
|
|
_, _ = b.ReadFrom(r)
|
|
return b.String()
|
|
}
|
|
u := []catalogue.Unheld{{Node: "n1", Module: "web", Seat: catalogue.ContainerRuntimeSeat}}
|
|
if got := read(func() { logUnheld("n1", u) }); got != "" {
|
|
t.Errorf("a command logged:\n%s", got)
|
|
}
|
|
logUnheldChanges = true
|
|
defer func() { logUnheldChanges = false }()
|
|
if got := read(func() { logUnheld("n1", u) }); !strings.Contains(got, "web on n1") {
|
|
t.Errorf("the serving controller did not log a change:\n%s", got)
|
|
}
|
|
if got := read(func() { logUnheld("n1", u) }); got != "" {
|
|
t.Errorf("an unchanged report was logged again:\n%s", got)
|
|
}
|
|
}
|