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 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 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) } } }