The marks of the haves (a refusal, the words a module keeps) read and write each module's have in turn, and each write walks every have: on a slow bus the pass waited minutes on them (shanks, 2026-11-05 22:22). They now run off the pass, newest word per kind, each bounded; an ask of compose during a pass is answered at once and queues one more pass. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
76 lines
2.4 KiB
Go
76 lines
2.4 KiB
Go
package selfcompose
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
"github.com/novox/mesh-host/internal/agent"
|
|
)
|
|
|
|
// Issue 1965 (shanks 2026-11-05 22:27: the verb compose gave no answer in 30 s while a pass waited on the bus): an ask
|
|
// that comes while a pass runs is answered at once, says one more pass is queued and what the running one waits on, and
|
|
// the queued pass is made when the running one ends.
|
|
func TestComposeAnsweredWhileAPassRuns(t *testing.T) {
|
|
b := newFakeBus()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
env := b.env()
|
|
hold := make(chan struct{})
|
|
var passes atomic.Int64
|
|
reached := make(chan struct{}, 8)
|
|
env.Running = func() []delivery.Composed {
|
|
passes.Add(1)
|
|
reached <- struct{}{}
|
|
if passes.Load() == 1 {
|
|
<-hold // the first pass waits on the bus
|
|
}
|
|
return nil
|
|
}
|
|
c := New(env)
|
|
out, err := c.Watch(ctx, "n1.>")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
have, _ := delivery.EncodeHaveNode(delivery.HaveNode{Record: &delivery.NodeRecord{Node: "n1", TunnelKey: "k", SealingKey: "sk", Capabilities: []string{"systemd"}}})
|
|
place, _ := delivery.EncodePlacement(delivery.Placement{Node: "n1", Decided: delivery.NodeDecided{Hub: true, Address: "10.77.0.1"}})
|
|
nodeWant, _ := delivery.EncodeNodeWant(delivery.NodeWant{Self: true})
|
|
b.put(t, delivery.PlacementBucket, "placement.n1", place)
|
|
b.put(t, delivery.HaveBucket, "n1.node", have)
|
|
b.put(t, delivery.AssignerWantsBucket, "want.n1.node", nodeWant)
|
|
for _, bucket := range []string{delivery.AssignerWantsBucket, delivery.PlacementBucket, delivery.HaveBucket, delivery.ModulesBucket, ResolvedBucket, delivery.BuilderBuildsBucket} {
|
|
b.send(bucket, agent.Update{Caught: true})
|
|
}
|
|
go func() {
|
|
for range out {
|
|
}
|
|
}()
|
|
select {
|
|
case <-reached:
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("no pass began")
|
|
}
|
|
answered := make(chan Pass, 1)
|
|
go func() {
|
|
p, _ := c.ComposeNow(ctx)
|
|
answered <- p
|
|
}()
|
|
select {
|
|
case p := <-answered:
|
|
if !strings.Contains(p.Queued, "a pass is running") {
|
|
t.Errorf("answered %+v; want it to say a pass is running and one is queued", p)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("the verb compose gave no answer while a pass ran")
|
|
}
|
|
close(hold)
|
|
select {
|
|
case <-reached: // the queued pass
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("the queued pass was not made when the running one ended")
|
|
}
|
|
}
|