mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/a-verb-survives-the-handover delivered: every member is delivered
A controller being replaced refuses a call it cannot serve, marked retry: handing-over; the one after it answers the same call.
74 lines
2.5 KiB
Go
74 lines
2.5 KiB
Go
package bus_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"github.com/novox/mesh-tools/node-tools/internal/bus"
|
|
"github.com/novox/mesh-tools/node-tools/internal/meshtest"
|
|
)
|
|
|
|
// **A call the controller refused because it is handing over is asked once more** (novox/hq issue 289):
|
|
// the controller being replaced did nothing and marked its refusal, and the one after it answers. A
|
|
// refusal not so marked is final, and a second handover refusal is said, not asked a third time.
|
|
func TestAHandoverRefusalIsAskedOnceMore(t *testing.T) {
|
|
was := bus.HandoverPause
|
|
bus.HandoverPause = 10 * time.Millisecond
|
|
t.Cleanup(func() { bus.HandoverPause = was })
|
|
|
|
url := meshtest.URL(t)
|
|
seat, err := nats.Connect(url)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer seat.Close()
|
|
c, err := bus.Connect(bus.Credential{URL: url, Module: "console", Node: "anchor"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
serve := func(t *testing.T, verb string, answers ...map[string]any) *atomic.Int32 {
|
|
t.Helper()
|
|
var asked atomic.Int32
|
|
sub, err := seat.Subscribe(bus.SeatToolSubject("mesh-controller", verb, "", ""), func(m *nats.Msg) {
|
|
n := int(asked.Add(1)) - 1
|
|
if n >= len(answers) {
|
|
n = len(answers) - 1
|
|
}
|
|
body, _ := json.Marshal(answers[n])
|
|
_ = m.Respond(body)
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = sub.Unsubscribe() })
|
|
_ = seat.Flush()
|
|
return &asked
|
|
}
|
|
handingOver := map[string]any{"error": "the controller is handing over to the next one; nothing was done, ask again",
|
|
"retry": bus.RetryHandingOver}
|
|
|
|
asked := serve(t, "rotate", handingOver, map[string]any{"result": "rotated"})
|
|
got, err := c.Ask("seat:mesh-controller.rotate", map[string]any{}, "")
|
|
if err != nil || string(got.Result) != `"rotated"` || asked.Load() != 2 {
|
|
t.Fatalf("after a handover refusal: %s %v, asked %d times", got.Result, err, asked.Load())
|
|
}
|
|
|
|
asked = serve(t, "push", map[string]any{"error": "refused for its own reason"})
|
|
if _, err := c.Ask("seat:mesh-controller.push", map[string]any{}, ""); err == nil || asked.Load() != 1 {
|
|
t.Fatalf("an unmarked refusal was asked again: %v, asked %d times", err, asked.Load())
|
|
}
|
|
|
|
asked = serve(t, "status", handingOver)
|
|
_, err = c.Ask("seat:mesh-controller.status", map[string]any{}, "")
|
|
if err == nil || asked.Load() != 2 || !strings.Contains(err.Error(), "both times refused as a handover") {
|
|
t.Fatalf("two handover refusals: %v, asked %d times", err, asked.Load())
|
|
}
|
|
}
|