From 3b01711e1fa10d7107d29855f5adbb66228f96a2 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 17:05:55 +0100 Subject: [PATCH] mesh-config: the whole test waits on the module's views subscriptions, never a second's sleep Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- .../cmd/mesh-config/whole_integration_test.go | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/modules/mesh-config/cmd/mesh-config/whole_integration_test.go b/modules/mesh-config/cmd/mesh-config/whole_integration_test.go index c89b4d140..7c29a7088 100644 --- a/modules/mesh-config/cmd/mesh-config/whole_integration_test.go +++ b/modules/mesh-config/cmd/mesh-config/whole_integration_test.go @@ -38,6 +38,7 @@ import ( "git.novox.be/novox/mesh-sdk/go/health" "git.novox.be/novox/mesh-sdk/go/process" "git.novox.be/novox/mesh-sdk/go/testbus" + "git.novox.be/novox/mesh-sdk/go/views" ) type wire struct { @@ -506,6 +507,22 @@ func (p *player) get(state, key string) ([]byte, bool) { return e.Value(), true } +// hearsTheViews waits until the module subscribed to every owner's views family it follows: a change said before that +// would reach it only as a gap. Waited on, never slept for. +func (p *player) hearsTheViews() { + p.t.Helper() + eventually(p.t, "the module subscribed to every views family it follows", func() bool { + p.mu.Lock() + defer p.mu.Unlock() + for _, f := range followed { + if !slices.Contains(p.subs, f.Owner+"."+views.EventName(f.State)) { + return false + } + } + return true + }) +} + // viewsReady waits until every first view of the module is answered (config_first_views). func (p *player) viewsReady() { p.t.Helper() @@ -664,7 +681,7 @@ func TestConfigWholeRenamesAndRetiresAgainstTheBus(t *testing.T) { if _, err := p.call(ResyncTool, nil); err != nil { t.Fatal(err) } - time.Sleep(time.Second) // the watches start after the first call + p.hearsTheViews() set := func(module, key string, value any) { t.Helper() if _, err := p.call(Seat+"."+Verb, map[string]any{"action": "set", "module": module, "node": "g14", "key": key, @@ -710,17 +727,15 @@ func TestConfigWholeRenamesAndRetiresAgainstTheBus(t *testing.T) { // The module restarted inside the window keeps it, and says renamed no more. said := p.publishedCount(EventRenamed) - cancelOld := p.ctx - _ = cancelOld p2 := start(t, bin, js, router, haves, nil) p2.viewsReady() if _, err := p2.call(ResyncTool, nil); err != nil { t.Fatal(err) } - time.Sleep(time.Second) - if v, ok := resolvedValue(p2, "g14", "player", "volume"); !ok || v != "8" { - t.Errorf("after the restart the old name answers %q (%v), want 8", v, ok) - } + eventually(t, "after the restart the old name answers 8", func() bool { + v, ok := resolvedValue(p2, "g14", "player", "volume") + return ok && v == "8" + }) if n := p2.publishedCount(EventRenamed); n != 0 || said != 1 { t.Errorf("renamed was said %d times before the restart and %d after", said, n) }