J2: the tool runner tells a subscribed bundle how its module's consumer stands, as mesh/heartbeat (stdio.OnHeartbeat), at each idle beat and once Missed (ADR 0615, ADR 0621, issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -60,3 +60,72 @@ func TestASlowHandlerAndABurstAreEachHandedOverOnce(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The runtime tells a bundle how its module's consumer stands between events (stdio.OnHeartbeat): a word at each
|
||||
// idle heartbeat while the bus answers, once Missed when the bus ended the loop, and a word again when it is bound again.
|
||||
func TestAConsumerSaysItIsBoundWhileIdleAndOnceWhenTheBusEndedIt(t *testing.T) {
|
||||
mesh := mt.New(t)
|
||||
restore := bus.IdleHeartbeat
|
||||
bus.IdleHeartbeat = 200 * time.Millisecond
|
||||
t.Cleanup(func() { bus.IdleHeartbeat = restore })
|
||||
mesh.Consumer(t, "anchor", "quiet", []string{"mesh.mod.alpha.event.>"}, time.Second)
|
||||
c, err := bus.Connect(bus.Credential{URL: mt.URL(t), Module: "node-tools", Node: "anchor"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.Logf = func(string, ...any) {}
|
||||
t.Cleanup(c.Close)
|
||||
|
||||
var mu sync.Mutex
|
||||
var beats []bus.Beat
|
||||
stop, err := c.ConsumeAsBeating("quiet", func(bus.Envelope) error { return nil }, func(b bus.Beat) {
|
||||
mu.Lock()
|
||||
beats = append(beats, b)
|
||||
mu.Unlock()
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(stop)
|
||||
last := func() (bus.Beat, int) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if len(beats) == 0 {
|
||||
return bus.Beat{}, 0
|
||||
}
|
||||
return beats[len(beats)-1], len(beats)
|
||||
}
|
||||
mt.Until(t, func() error {
|
||||
if b, n := last(); n == 0 || b.Missed || b.At.IsZero() {
|
||||
return fmt.Errorf("no word that the consumer is bound yet: %d beats", n)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
mesh.RemoveConsumer(t, bus.EventsStream, "anchor", "quiet")
|
||||
mt.Until(t, func() error {
|
||||
if b, _ := last(); !b.Missed || b.Why == "" {
|
||||
return fmt.Errorf("the consumer is gone from the bus and no Missed word came: %+v", b)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
mu.Lock()
|
||||
missed := 0
|
||||
for _, b := range beats {
|
||||
if b.Missed {
|
||||
missed++
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
time.Sleep(600 * time.Millisecond)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
after := 0
|
||||
for _, b := range beats {
|
||||
if b.Missed {
|
||||
after++
|
||||
}
|
||||
}
|
||||
if after != missed || missed != 1 {
|
||||
t.Errorf("Missed is said once, not at every beat: %d then %d", missed, after)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user