diff --git a/internal/link/engineseat.go b/internal/link/engineseat.go index ad7ca02..43605eb 100644 --- a/internal/link/engineseat.go +++ b/internal/link/engineseat.go @@ -13,6 +13,7 @@ import ( oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/catalogue" + "git.novox.be/novox/mesh-sdk/go/seatlist" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/micro" ) @@ -99,6 +100,22 @@ func AnswerEngineCall(handle EngineFunc, verb, node, subject string, body []byte return say() } +// ServedEngineSeat is the engine's seat as it serves it: the seat list's definition once the engine holds it, and the +// bootstrapped definition before (novox/hq ADR 0636 §2: every reader holds the bootstrapped seats from its start). The +// link serves the seat when it opens, before the node agent starts the reader of the list; read from the list alone, the +// engine served no verb, so nobody answered manifest-reads and the assigner kept an engine's old statement (issue 1966). +func ServedEngineSeat() catalogue.Seat { + if s, ok := catalogue.SeatNamed(EngineSeat); ok && len(s.Serves) > 0 { + return s + } + for _, s := range catalogue.SeatsFromDefinitions(seatlist.Bootstrapped()) { + if s.Name == EngineSeat { + return s + } + } + return catalogue.Seat{Name: EngineSeat} +} + // EngineSeats is a link that serves this node's seat. Optional, as Asked is. type EngineSeats interface { ServeEngineSeat(EngineFunc) @@ -118,7 +135,7 @@ func (l *natsLink) ServeEngineSeat(handle EngineFunc) { return } apart := make(chan struct{}, apartAtOnce) - for _, v := range catalogue.EngineVerbs(EngineSeat) { + for _, v := range ServedEngineSeat().Serves { verb := v.Name answer := EngineAnswer(handle, verb, l.node, apart) meta := EngineEndpointMetadata(v, l.node) diff --git a/internal/link/seats_for_tests_test.go b/internal/link/seats_for_tests_test.go index 7994cc2..0b8d81b 100644 --- a/internal/link/seats_for_tests_test.go +++ b/internal/link/seats_for_tests_test.go @@ -1,6 +1,9 @@ package link import ( + "slices" + "testing" + "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/seatlist/seatlisttest" ) @@ -8,3 +11,25 @@ import ( // The seats the tests judge against are given to catalogue as the node-engine gives the ones it read from the bus // (novox/hq ADR 0635 §1); catalogue compiles none. func init() { catalogue.UseSeats(catalogue.SeatsFromDefinitions(seatlisttest.Definitions())) } + +// Before the engine holds a seat list it serves its seat's verbs from the bootstrapped definition (issue 1966: served from +// the empty list, it answered no verb and the assigner kept an old statement). +func TestServedEngineSeatBeforeTheList(t *testing.T) { + held := catalogue.Seats() + defer catalogue.UseSeats(held) + for _, tc := range []struct { + name string + seats []catalogue.Seat + }{{"no list held", nil}, {"list held", held}} { + catalogue.UseSeats(tc.seats) + var names []string + for _, v := range ServedEngineSeat().Serves { + names = append(names, v.Name) + } + for _, want := range []string{ActManifestReads, ActComposition, ActHandOver} { + if !slices.Contains(names, want) { + t.Errorf("%s: the engine serves %v, without %s", tc.name, names, want) + } + } + } +}