An unplaced node owes no pass, so no timer is armed again; every wait a test sets is put back when it ends; pin mesh-sdk 8520a4c, whose masking splits only a file's secrets (issue 1126; review of seed-same-3, round 1)
mesh/delivery merged: source.main moved to novox/mesh-host@df878ba1f780
mesh/delivery-group group fix/1126-seed-same-3 merged: every member is merged
mesh/check pass 1e01868d92c3 on c3a2ffde6755 | carried from mesh-catalog 774fcdfa93ad | unit 2188 run, 1064 kept, 36 skipped | integration 2197 run, 1…

This commit is contained in:
2026-10-28 12:25:32 +01:00
parent 9d04ae7edd
commit 1e01868d92
4 changed files with 25 additions and 8 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ module github.com/novox/mesh-host
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028110239-70de9429e56d
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028112237-8520a4c09960
github.com/nats-io/nats-server/v2 v2.11.17
github.com/nats-io/nats.go v1.54.0
golang.org/x/crypto v0.57.0
+2 -2
View File
@@ -1,5 +1,5 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028110239-70de9429e56d h1:6XOd1cxwctmFHS3HU44LhaenXkIzcLABKoY6Er1a4qM=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028110239-70de9429e56d/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028112237-8520a4c09960 h1:+XJKxMzi1l0U9zAlv2wj8i3hTmHvc3XqBcIhCM2luV4=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028112237-8520a4c09960/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
+1
View File
@@ -482,6 +482,7 @@ func (c *Composer) compose(ctx context.Context) []agent.Update {
// The catalogue no longer places it: the agent returns to the controller's compositions (endWhenUnplaced), and
// this is no refusal of what it runs (issue 1126, the seed's revert).
c.sayOnce("the catalogue no longer places this node: it composes nothing of its own, and keeps running what it runs")
c.owed.Store(false) // nothing of its own to compose, so no pass is owed and no timer armed again
return nil
}
// Composed from the build its want names (frombus.Scope.KeptAt, issue 1126).
+21 -5
View File
@@ -244,7 +244,7 @@ func TestTheNodeComposesItsWantsFromTheBus(t *testing.T) {
// Issue 1126, the switch made safe: a bus not answering for a moment (a reload of its accounts) is tried again and
// refuses nothing; a pass whose run ended refuses nothing either.
composeAgain = []time.Duration{time.Millisecond, time.Millisecond, time.Millisecond}
setFor(t, &composeAgain, []time.Duration{time.Millisecond, time.Millisecond, time.Millisecond})
fresh := func(get func(context.Context, string, string) ([]byte, error)) (*Composer, *[]string) {
var refusals []string
e := b.env()
@@ -491,6 +491,14 @@ func TestTheNodeComposesItsWantsFromTheBus(t *testing.T) {
if composed != "" || !again.owed.Load() {
t.Errorf("%s: composed %v and owes a pass again %v; want nothing handed and a pass owed", row.name, composed != "", again.owed.Load())
}
// Then the catalogue no longer places the node: it composes nothing of its own, and owes no pass either, so
// no timer is armed again (rev-seed-same-3, round 1).
again.mu.Lock()
clear(again.placements)
again.mu.Unlock()
if again.compose(ctx); again.owed.Load() {
t.Errorf("%s: an unplaced node still owes a pass", row.name)
}
continue
}
if composed == "" {
@@ -837,7 +845,7 @@ func TestTheNodeComposesItsWantsFromTheBus(t *testing.T) {
t.Errorf("what runs moved, and %d answer(s) were composed again for %d group(s)", got-settled, len(answers))
}
// A read the bus did not answer is no answer to keep: the group is answered again after a bound, though nothing moved.
laterAgain = []time.Duration{20 * time.Millisecond}
setFor(t, &laterAgain, []time.Duration{20 * time.Millisecond})
ownFails.Store(true)
candidate("adds", 9, "lemurs", "keeper")
waitFor("the answer met the vault not answering", func(a map[string]delivery.CandidateAnswer) bool {
@@ -903,7 +911,7 @@ func TestTheNodeComposesItsWantsFromTheBus(t *testing.T) {
// Issue 1126, the shadow shows its work: one input not read after the wait is said, naming it, in the journal and in
// a refused answer to each group known; once read, the group is answered for real.
notReadyAfter = 30 * time.Millisecond
setFor(t, &notReadyAfter, 30*time.Millisecond)
nb := newPatternBus()
nenv := readOnly.env()
nenv.Watch = nb.watch
@@ -948,8 +956,7 @@ func TestTheNodeComposesItsWantsFromTheBus(t *testing.T) {
t.Errorf("a shadow not ready said %q", nsaid)
}
nmu.Unlock()
slowAfter = time.Nanosecond // every answer is slow: said, and noted in words that do not move with the time
defer func() { slowAfter = time.Minute }()
setFor(t, &slowAfter, time.Nanosecond) // every answer is slow: said, and noted in words that do not move with the time
nb.send(delivery.AssignerWantsBucket, agent.Update{Caught: true})
select {
case a := <-nanswered:
@@ -1310,3 +1317,12 @@ func TestAHaveSaysWhetherItsModuleRunsInsideTheNodesOwn(t *testing.T) {
})
}
}
// setFor sets a package global (a wait the composer or the shadow reads) for one test, and puts back what it held when
// the test ends, so no later test runs on another test's waits.
func setFor[T any](t *testing.T, p *T, v T) {
t.Helper()
old := *p
*p = v
t.Cleanup(func() { *p = old })
}