mesh/delivery merged: source.main moved to novox/mesh-delivery@e559798972b4
mesh/check being carried to 2dd5239726ca on e559798972b4 | from d409bdf20f48
Review of 09791f5: with the indexer's modules bucket readable but empty, nothing was read, so
nothing was found and both phases passed; the operator could have handed the lease away while an
unkept read still stood. Judge now refuses when no manifest was read, or fewer of the indexer's
modules than it holds (a provided manifest does not count for a missing one), saying why; a
missing or refused manifest is named as before, now under test.
The controller's grants leave the keep list: ADR 0571 §9 has no manifest read them from L7's
delivery, changing ADR 0567 §10 and ADR 0547 §5's shadow compare, so the assigner's and the grants
part's reads are refused, and a refusal names the record that ended a declaration where one does.
Quick fix: the counts in the grants part's Judge, as here. Thorough fix: the same coverage rule
in the shared mesh-sdk derivation once the keep list moves there; the quick one stands until then.
157 lines
9.2 KiB
Go
157 lines
9.2 KiB
Go
package manifest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
|
|
"mesh-grants/internal/grants"
|
|
)
|
|
|
|
// Issue 774, design 47 L3 (ADR 0547 §11, ADR 0561 §3, ADR 0571 §13): grants_lease_check answers from bus state who holds
|
|
// the controller's lease, every manifest's dependency on the controller with what keeps it, the controller's streams in
|
|
// use, and the lease write's check run dry, at the write (the three kept to it alone) and before its delivery.
|
|
func TestTheLeaseCheckJudgesEveryDependencyOnTheController(t *testing.T) {
|
|
const mManifest = `{"module":"mod-m","version":"1","reads":["mesh-controller.declared-packages"]}`
|
|
controllerHolds := map[string]any{"instance": "controller@novox-be pid 1 since 2026-10-30T14:52:10Z", "epoch": 60,
|
|
"taken": "2026-10-30T14:52:10Z", "renewed": "2026-10-30T22:00:00Z"}
|
|
handedAway := map[string]any{"handed-to": "the operator's hand-over", "handed-by": "operator"}
|
|
type want struct {
|
|
deps []string // "<module> <kind> <what> <kept?>" with kept as "kept" or "unkept"
|
|
writePasses bool
|
|
beforePasses bool
|
|
reason string // a reason the check at the write must name, or ""
|
|
bothPhases bool // the check before the delivery must name the reason too
|
|
handedAway bool
|
|
durable []string // durable consumers of KV_mesh-controller_assignments
|
|
}
|
|
for _, row := range []struct {
|
|
name string
|
|
manifests map[string]string
|
|
lease map[string]any
|
|
want want
|
|
setup func(b *fakeBus)
|
|
}{
|
|
{"a read of the lease alone is no dependency, and the controller's own manifest is not judged",
|
|
map[string]string{"mod-l": `{"module":"mod-l","version":"1","reads":["mesh-controller.lease"]}`,
|
|
"mesh-controller": `{"module":"mesh-controller","version":"1","invokes":["seat:mesh-controller.status"]}`},
|
|
controllerHolds, want{writePasses: true, beforePasses: true}, nil},
|
|
{"the builder's consume of artifacts-gone is kept to the write itself",
|
|
map[string]string{"mesh-delivery-builder": `{"module":"mesh-delivery-builder","version":"1","consumes":["mesh-controller.artifacts-gone"]}`},
|
|
controllerHolds, want{deps: []string{"mesh-delivery-builder consume mesh-controller.artifacts-gone kept"}, writePasses: true, beforePasses: true}, nil},
|
|
{"the assigner's read of composed is kept until the write's delivery, which drops it",
|
|
map[string]string{"mesh-delivery-assigner": `{"module":"mesh-delivery-assigner","version":"1","reads":["mesh-controller.composed","mesh-controller.lease"]}`},
|
|
controllerHolds, want{deps: []string{"mesh-delivery-assigner read mesh-controller.composed kept"}, beforePasses: true,
|
|
reason: "mesh-delivery-assigner declares read mesh-controller.composed, kept by ADR 0571 §8 only until the lease write's own delivery"}, nil},
|
|
{"a read no record keeps fails both phases, named",
|
|
map[string]string{"pacman": `{"module":"pacman","version":"1","reads":["mesh-controller.declared-packages"]}`},
|
|
controllerHolds, want{deps: []string{"pacman read mesh-controller.declared-packages unkept"},
|
|
reason: "pacman declares read mesh-controller.declared-packages, which no record keeps until the lease write"}, nil},
|
|
{"a seat verb and a module tool of the controller's, a consume and a wait are each one; every tool (*) is none",
|
|
map[string]string{"mod-i": `{"module":"mod-i","version":"1","invokes":["seat:mesh-controller.root-free","mesh-controller.status","*","nats.nats_users"],` +
|
|
`"consumes":["mesh-controller.checked","mod-x.thing"],"uses":["mesh-controller"]}`,
|
|
"node-tools": `{"module":"node-tools","version":"1","waits":["mesh-controller.checked"]}`},
|
|
controllerHolds, want{deps: []string{"mod-i consume mesh-controller.checked unkept", "mod-i invoke mesh-controller.status unkept",
|
|
"mod-i invoke seat:mesh-controller.root-free unkept", "mod-i uses mesh-controller unkept", "node-tools waits mesh-controller.checked unkept"},
|
|
reason: "mod-i declares invoke seat:mesh-controller.root-free, which no record keeps"}, nil},
|
|
{"a process's own invoke of the controller is its module's dependency, named by the process",
|
|
map[string]string{"mod-p": `{"module":"mod-p","version":"1","resources":[{"id":"account","type":"user","name":"mod-p"},{"id":"worker","type":"process","run":["./w"],"user":"mod-p",` +
|
|
`"bus":{"invokes":["seat:mesh-controller.held"]}}]}`},
|
|
controllerHolds, want{deps: []string{"mod-p invoke seat:mesh-controller.held unkept"},
|
|
reason: "mod-p (process worker) declares invoke seat:mesh-controller.held"}, nil},
|
|
{"no manifest read from the indexer fails both phases, though nothing read depends on the controller",
|
|
map[string]string{},
|
|
controllerHolds, want{reason: "no manifest was read from the indexer (it holds 0 modules)", bothPhases: true}, nil},
|
|
{"a module whose manifest digest is missing fails both phases, named",
|
|
map[string]string{"mod-l": `{"module":"mod-l","version":"1"}`, "mod-m": mManifest},
|
|
controllerHolds, want{reason: "manifest of mod-m could not be read", bothPhases: true}, func(b *fakeBus) {
|
|
delete(b.states[grants.ReadManifests], delivery.ManifestDigest([]byte(mManifest)))
|
|
}},
|
|
{"a manifest mesh-sdk refuses fails both phases, named",
|
|
map[string]string{"mod-l": `{"module":"mod-l","version":"1"}`, "mod-r": `{"module":"mod-r","version":"1","waits":["mesh-controller.checked"]}`},
|
|
controllerHolds, want{reason: "manifest of mod-r could not be read", bothPhases: true}, nil},
|
|
{"a lease already handed away leaves nothing to write",
|
|
map[string]string{},
|
|
handedAway, want{handedAway: true, reason: "there is nothing to write: the lease is handed to the operator's hand-over"}, nil},
|
|
} {
|
|
t.Run(row.name, func(t *testing.T) {
|
|
b := newFakeBus()
|
|
for m, raw := range row.manifests {
|
|
b.manifestOf(m, raw)
|
|
}
|
|
b.put(grants.ReadLease, grants.LeaseKey, row.lease)
|
|
if row.setup != nil {
|
|
row.setup(b)
|
|
}
|
|
b.streams = []map[string]any{
|
|
{"name": "KV_mesh-controller_assignments", "consumers": 3, "messages": 2991, "subjects_held": 40, "last": "2026-10-30T12:55:13Z"},
|
|
{"name": "KV_mesh-controller_rollouts", "consumers": 0, "messages": 6652},
|
|
{"name": "KV_mesh-grants_memberships", "consumers": 243, "messages": 1573},
|
|
}
|
|
b.consumers = map[string][]map[string]any{"KV_mesh-controller_assignments": {{"name": "0AYfjxta"}, {"name": "1g9XJFUX"}, {"name": "grants-assignments"}}}
|
|
raw, err := partOn(b).LeaseCheck()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var got grants.LeaseAnswer
|
|
data, _ := json.Marshal(raw)
|
|
if err := json.Unmarshal(data, &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var deps []string
|
|
for _, d := range got.Dependencies {
|
|
kept := "unkept"
|
|
if d.Kept != "" {
|
|
kept = "kept"
|
|
}
|
|
deps = append(deps, d.Module+" "+d.Kind+" "+d.What+" "+kept)
|
|
}
|
|
if !slices.Equal(deps, row.want.deps) {
|
|
t.Errorf("dependencies %q, want %q", deps, row.want.deps)
|
|
}
|
|
if got.Check.Phase != grants.PhaseWrite || got.Check.Passes != row.want.writePasses {
|
|
t.Errorf("check at the write passes=%v (%v), want %v", got.Check.Passes, got.Check.Reasons, row.want.writePasses)
|
|
}
|
|
if got.Before.Phase != grants.PhaseBefore || got.Before.Passes != row.want.beforePasses {
|
|
t.Errorf("check before the delivery passes=%v (%v), want %v", got.Before.Passes, got.Before.Reasons, row.want.beforePasses)
|
|
}
|
|
if row.want.reason != "" && !slices.ContainsFunc(got.Check.Reasons, func(r string) bool { return strings.Contains(r, row.want.reason) }) {
|
|
t.Errorf("reasons %q name nothing like %q", got.Check.Reasons, row.want.reason)
|
|
}
|
|
if row.want.bothPhases && !slices.ContainsFunc(got.Before.Reasons, func(r string) bool { return strings.Contains(r, row.want.reason) }) {
|
|
t.Errorf("reasons before the delivery %q name nothing like %q", got.Before.Reasons, row.want.reason)
|
|
}
|
|
if got.Lease.HandedAway != row.want.handedAway {
|
|
t.Errorf("lease handed away %v (%s), want %v", got.Lease.HandedAway, got.Lease.Says, row.want.handedAway)
|
|
}
|
|
if !row.want.handedAway && (got.Lease.Since == nil || got.Lease.Since.Format("15:04") != "14:52" ||
|
|
!strings.Contains(got.Lease.Says, "the controller holds the lease") || !slices.Equal(got.Lease.Does, grants.DoesWhileController)) {
|
|
t.Errorf("lease %+v: the controller's instance since 14:52, and what it does while it holds it", got.Lease)
|
|
}
|
|
if len(got.Streams) != 1 || got.Streams[0].Stream != "KV_mesh-controller_assignments" || got.Streams[0].Consumers != 3 ||
|
|
!slices.Equal(got.Streams[0].Durable, []string{"grants-assignments"}) || !slices.Equal(got.Idle, []string{"KV_mesh-controller_rollouts"}) {
|
|
t.Errorf("streams %+v idle %v: the controller's assignments bucket with its one durable consumer, the rollouts idle, no other module's", got.Streams, got.Idle)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Every entry of the keep list names the record that keeps it, and exactly three are kept to the write itself (ADR 0571 §13).
|
|
func TestTheKeepListNamesItsRecordsAndThreeToTheWrite(t *testing.T) {
|
|
toTheWrite := 0
|
|
for _, k := range grants.KeepList {
|
|
if !strings.HasPrefix(k.Record, "ADR ") || k.Module == "" || k.What == "" {
|
|
t.Errorf("entry %+v names no record, module or declaration", k)
|
|
}
|
|
if k.ToTheWrite {
|
|
toTheWrite++
|
|
}
|
|
}
|
|
if toTheWrite != 3 {
|
|
t.Errorf("%d entries kept to the write itself, ADR 0571 §13 names three", toTheWrite)
|
|
}
|
|
}
|