Judge the grants step on the declaration it sends, so nothing composed between the check and the send slips through (review of #230)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheGrantsStepSendsTheUserListAndNothingElse (2.55s)

The guard composed its own declaration ahead of the send; now sendToEach
judges the one it composed and stamped, and refuses it unsent.
This commit is contained in:
2026-10-11 19:12:33 +02:00
parent 538c4d5115
commit 78205d7405
4 changed files with 96 additions and 80 deletions
+48 -37
View File
@@ -1,6 +1,7 @@
package main
import (
"context"
"encoding/json"
"strings"
"testing"
@@ -65,31 +66,50 @@ func TestTheGrantsStepSendsTheUserListAndNothingElse(t *testing.T) {
}
}
// What anchor was last sent: everything at the builds of before the merge, kept as a send keeps it.
sendAnchor := func() {
wasEpoch := epochForActs
epochForActs = func(context.Context) (uint64, error) { return 7, nil }
t.Cleanup(func() { epochForActs = wasEpoch })
// A send to anchor composed as sendToEach composes it: numbered, planned, declared, stamped.
compose := func(under context.Context) (catalogue.Resolution, sendable) {
t.Helper()
plan, settings, err := planFor(keepingRecorded(ctx), open, "anchor")
numbered, err := allot(under, inv, "anchor")
if err != nil {
t.Fatal(err)
}
gens, err := generators(ctx, open)
plan, settings, err := planFor(under, open, "anchor")
if err != nil {
t.Fatal(err)
}
declared, err := declarationWith(keepingRecorded(ctx), open, "anchor", plan, settings, gens, Allocating)
gens, err := generators(under, open)
if err != nil {
t.Fatal(err)
}
declared, err := declarationWith(under, open, "anchor", plan, settings, gens, Allocating)
if err != nil {
t.Fatal(err)
}
numbered.stamp(&declared)
return plan, declared
}
record := func(declared sendable) {
t.Helper()
body, err := declared.Body()
if err != nil {
t.Fatal(err)
}
if _, err := recordSent(ctx, inv, "anchor", body, declared.Builds, 0); err != nil {
if _, err := recordSent(ctx, inv, "anchor", body, declared.Builds, declared.Epoch,
sentRecordOf(ctx, declared)); err != nil {
t.Fatal(err)
}
}
sendAnchor()
if only, err := grantsOnly(ctx, open, "anchor"); err != nil || only != "" {
kept := keepingEveryBuild(keepingRecorded(ctx))
// What anchor was last sent: everything at the builds of before the merge, as an ordinary send sends it.
_, before := compose(keepingRecorded(ctx))
record(before)
plan, declared := compose(kept)
if only, err := grantsOnlyIn(ctx, open, "anchor", plan, declared); err != nil || only != "" {
t.Fatalf("with nothing changed, the step reads %q, %v", only, err)
}
@@ -100,62 +120,53 @@ func TestTheGrantsStepSendsTheUserListAndNothingElse(t *testing.T) {
}
// The step's send to anchor: restic as anchor runs it, c1, and the user list granting c2's tool.
kept := keepingEveryBuild(keepingRecorded(ctx))
plan, settings, err := planFor(kept, open, "anchor")
generation, err := inv.AssignmentGeneration(ctx)
if err != nil {
t.Fatal(err)
}
plan, declared = compose(kept)
for _, m := range plan.Modules {
if m.Module == "restic" && len(m.Tools) > 0 {
t.Fatalf("the grants step composed restic's new build on anchor: tools %v", m.Tools)
}
}
list, _, err := busUserList(ctx, inv, plan.Modules)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(list, "mesh.mod.restic.tool.backup_health.laptop") {
t.Errorf("the user list the step sends does not grant laptop's node-engine restic's new tool:\n%s", list)
}
gens, err := generators(kept, open)
if err != nil {
t.Fatal(err)
}
declared, err := declarationWith(kept, open, "anchor", plan, settings, gens, Allocating)
if err != nil {
t.Fatal(err)
}
if declared.Builds["restic"] != "c1" {
t.Fatalf("the step's send records it carries restic %q; want c1, which anchor runs", declared.Builds["restic"])
}
if !strings.Contains(declared.BusUsers, "mesh.mod.restic.tool.backup_health.laptop") {
t.Error("the step's declaration does not carry the new grant")
t.Errorf("the step's declaration does not grant laptop's node-engine restic's new tool:\n%s", declared.BusUsers)
}
// Recorded as a send records it: anchor still runs restic c1, and its gate is still to come there.
if err := inv.RecordSent(ctx, nodeID(t, open, "anchor"), "d-grants", declared.Builds); err != nil {
t.Fatal(err)
// The generation it carries is the current one (novox/hq issue 234), and the step does not raise it.
if declared.composedFrom != generation {
t.Errorf("the step's send was composed from generation %d; the mesh is at %d", declared.composedFrom, generation)
}
if sent, _, err := inv.SentBuilds(ctx, "anchor"); err != nil || sent["restic"] != "c1" {
t.Fatalf("after the step anchor reads as sent restic %q (%v); want c1", sent["restic"], err)
}
// The guard: only the user list differs from what anchor was last sent.
if only, err := grantsOnly(ctx, open, "anchor"); err != nil || only != "" {
// The guard, on the very declaration the send sends: only the user list differs from the last send.
if only, err := grantsOnlyIn(ctx, open, "anchor", plan, declared); err != nil || only != "" {
t.Fatalf("the step reads as changing more than the user list: %q, %v", only, err)
}
// And the gate's refusal still reads the step's send: it moves nothing there.
if names, err := ungatedIn(kept, open, []string{"anchor"}, ""); err != nil || strings.Join(names, ",") != "anchor" {
t.Fatalf("the step's send is refused as a move: %v, %v", names, err)
}
// Recorded as the send records it: anchor still runs restic c1, its gate still to come there.
record(declared)
if sent, _, err := inv.SentBuilds(ctx, "anchor"); err != nil || sent["restic"] != "c1" {
t.Fatalf("after the step anchor reads as sent restic %q (%v); want c1", sent["restic"], err)
}
if after, err := inv.AssignmentGeneration(ctx); err != nil || after != generation {
t.Fatalf("the step moved the assignment generation from %d to %d (%v)", generation, after, err)
}
// A module newly assigned to anchor: the step would install it, unjudged, so it is not sent.
// A module newly assigned to anchor: the step would install it, unjudged, so it is refused unsent.
if _, err := inv.Assign(ctx, "anchor", "wallpaper"); err != nil {
t.Fatal(err)
}
only, err := grantsOnly(ctx, open, "anchor")
plan, declared = compose(kept)
only, err := grantsOnlyIn(ctx, open, "anchor", plan, declared)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(only, "wallpaper newly assigned") || !strings.Contains(only, "wallpaper") {
if !strings.Contains(only, "wallpaper newly assigned") || !strings.Contains(only, "+wallpaper") {
t.Fatalf("a new assignment on the bus's machine reads %q; want the step refused, naming it", only)
}
}