Files
mesh-host/internal/selfcompose/compose_while_passing_test.go
T

83 lines
2.8 KiB
Go

package selfcompose
import (
"context"
"strings"
"sync/atomic"
"testing"
"time"
"git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient"
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
nodeengine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"github.com/novox/mesh-host/internal/agent"
"github.com/novox/mesh-host/internal/buckets"
"github.com/novox/mesh-host/internal/buckets/bucketstest"
"github.com/novox/mesh-host/internal/compositions"
)
// 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 composeAnsweredWhileAPassRuns(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() []compositions.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)
}
place := assignedValues(t, meshmoduleassigner.Assignments{Node: "n1", Decided: meshmoduleassigner.NodeDecided{Hub: true, Address: "10.77.0.1"}})
have, _ := nodeengineclient.EncodeHaveNode(nodeengine.HaveByNodeNodeValue{Record: &nodeengine.NodeRecord{Node: "n1", TunnelKey: "k", SealingKey: "sk", Capabilities: []string{"systemd"}}})
nodeWant, _ := bucketstest.NodeWant(meshwants.WantsByWantNodeNodeValue{Self: true})
b.putAssigned(t, place)
b.put(t, buckets.Have, "n1.node", have)
b.put(t, buckets.Wants, "want.n1.node", nodeWant)
for _, bucket := range []string{buckets.Wants, buckets.Assignments, buckets.Have, buckets.Modules, ResolvedBucket, builds.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")
}
}