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: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
callerOn reads a caller only under the call kind, so a tool subscription ending in a wildcard never yields one. A caller-named call that reached nobody is said once per subject before it is asked on the old subject, so a runtime or grant that drifted behind is seen rather than a quiet unproven. The caller tests wait for their bundle's exit, which reads the launcher's restart pauses another test sets.
100 lines
3.6 KiB
Go
100 lines
3.6 KiB
Go
package runtime
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"github.com/novox/mesh-tools/node-tools/internal/bus"
|
|
mt "github.com/novox/mesh-tools/node-tools/internal/meshtest"
|
|
)
|
|
|
|
type saidCaller struct {
|
|
Caller string `json:"caller"`
|
|
Arguments map[string]any `json:"arguments"`
|
|
}
|
|
|
|
// **A module is told who called it, from the subject the call arrived on** (novox/hq issue 365): a call on the
|
|
// caller-named subject reaches the bundle with that caller in the call's `_meta`, for a module's tool, on its
|
|
// machine's address, and for a seat's verb; a caller named in the arguments is only an argument.
|
|
func TestACallOnTheCallerNamedSubjectReachesTheModuleWithItsCaller(t *testing.T) {
|
|
mesh := mt.New(t)
|
|
mesh.Issue(t, mt.MembershipOf("ledger", "anchor", true, map[string][]string{"node-desk": {"who"}}))
|
|
nodeTools := connect(t, "node-tools", "anchor")
|
|
stop, err := Run(nodeTools, []Served{{"ledger", []string{mt.Fixture("caller-echo.py")}}}, nil, func(string, ...any) {})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer settled(stop)
|
|
asker, err := bus.Connect(bus.Credential{URL: mt.URL(t), Module: "node-tools", Node: "shanks",
|
|
User: "shanks.node-tools", Password: "unused"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
asker.Logf = func(string, ...any) {}
|
|
defer asker.Close()
|
|
|
|
for _, key := range []string{"ledger.who", "ledger.who@anchor", "seat:node-desk.who@anchor"} {
|
|
var got saidCaller
|
|
mt.Until(t, func() error {
|
|
answered, err := asker.Ask(key, map[string]any{"caller": "novox.node-tools",
|
|
"_meta": map[string]any{"mesh/caller": "novox.controller"}}, "")
|
|
if err == nil {
|
|
err = json.Unmarshal(answered.Result, &got)
|
|
}
|
|
return err
|
|
})
|
|
if got.Caller != "shanks.node-tools" {
|
|
t.Errorf("%s reached the module with caller %q, want the asker's bus user", key, got.Caller)
|
|
}
|
|
if got.Arguments["caller"] != "novox.node-tools" {
|
|
t.Errorf("%s: the arguments did not reach the module as sent: %v", key, got.Arguments)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The subject that names no caller is still served, for one release (retired by hq issue 464), and says so:
|
|
// the module is named no caller, whatever the body claims.
|
|
func TestTheSubjectThatNamesNoCallerStillWorksAndNamesNone(t *testing.T) {
|
|
mesh := mt.New(t)
|
|
mesh.Issue(t, mt.MembershipOf("ledger", "anchor", true, map[string][]string{"node-desk": {"who"}}))
|
|
nodeTools := connect(t, "node-tools", "anchor")
|
|
stop, err := Run(nodeTools, []Served{{"ledger", []string{mt.Fixture("caller-echo.py")}}}, nil, func(string, ...any) {})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer settled(stop)
|
|
old, err := nats.Connect(mt.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer old.Close()
|
|
body, _ := json.Marshal(map[string]any{"_meta": map[string]any{"mesh/caller": "novox.controller"}})
|
|
for _, subject := range []string{"mesh.mod.ledger.tool.who", "mesh.mod.ledger.tool.who.anchor", "mesh.seat.node-desk.tool.who.anchor"} {
|
|
var reply struct {
|
|
Result saidCaller `json:"result"`
|
|
Error string `json:"error"`
|
|
}
|
|
mt.Until(t, func() error {
|
|
msg, err := old.Request(subject, body, 5*time.Second)
|
|
if err == nil {
|
|
err = json.Unmarshal(msg.Data, &reply)
|
|
}
|
|
return err
|
|
})
|
|
if reply.Error != "" || reply.Result.Caller != "" {
|
|
t.Errorf("%s: the module was named caller %q (error %q), want none", subject, reply.Result.Caller, reply.Error)
|
|
}
|
|
}
|
|
}
|
|
|
|
// settled stops a runtime and waits for its bundle's exit to be handled: the launcher reads its restart pauses
|
|
// (launch.RestartFirst) as a bundle exits, and a later test in this package sets them, so a test that leaves its
|
|
// bundle exiting behind it races the next one.
|
|
func settled(stop func()) {
|
|
stop()
|
|
time.Sleep(500 * time.Millisecond)
|
|
}
|