views: an ask through a tool reads the tool runner's {result, error} envelope
A5's request: the bus module's keeper asks the indexer's first view through the tool runner, which answers in that envelope; its error is the ask's. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
+12
-1
@@ -352,7 +352,8 @@ func (f *Follower) Gap(ctx context.Context, why string) error {
|
||||
return f.askUntil(ctx, "a gap: "+why, false)
|
||||
}
|
||||
|
||||
// AskVia is a Follower's Ask through a process's call of the owner's tool (process.Conn.Call), decoding its answer.
|
||||
// AskVia is a Follower's Ask through a call of the owner's tool (process.Conn.Call, or stdio.Ask through the tool
|
||||
// runner), decoding its answer: bare, or in the tool runner's envelope {result, error}, whose error is the ask's.
|
||||
func AskVia(call func(key string, body any) (json.RawMessage, error), tool string) func(context.Context, string) (Answer, error) {
|
||||
return func(_ context.Context, after string) (Answer, error) {
|
||||
body := map[string]any{}
|
||||
@@ -363,6 +364,16 @@ func AskVia(call func(key string, body any) (json.RawMessage, error), tool strin
|
||||
if err != nil {
|
||||
return Answer{}, err
|
||||
}
|
||||
var wrapped struct {
|
||||
Result json.RawMessage `json:"result"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
if json.Unmarshal(raw, &wrapped) == nil && (len(wrapped.Result) > 0 || wrapped.Error != "") {
|
||||
if wrapped.Error != "" {
|
||||
return Answer{}, fmt.Errorf("%s answered: %s", tool, wrapped.Error)
|
||||
}
|
||||
raw = wrapped.Result
|
||||
}
|
||||
return DecodeAnswer(raw)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,24 @@ func TestViews(t *testing.T) {
|
||||
t.Errorf("asked again by hand: %v, %v", again, err)
|
||||
}
|
||||
})
|
||||
t.Run("an ask through a tool reads the answer bare or in the tool runner's envelope, and its error", func(t *testing.T) {
|
||||
bare := `{"v":1,"owner":"o","state":"s","revision":3,"values":{"k":"1"}}`
|
||||
for name, c := range map[string]struct {
|
||||
answer string
|
||||
rev uint64
|
||||
err string
|
||||
}{
|
||||
"bare": {bare, 3, ""},
|
||||
"in an envelope": {`{"result":` + bare + `}`, 3, ""},
|
||||
"an error": {`{"error":"not yet"}`, 0, "not yet"},
|
||||
} {
|
||||
ask := AskVia(func(string, any) (json.RawMessage, error) { return json.RawMessage(c.answer), nil }, "o.first_view")
|
||||
a, err := ask(context.Background(), "")
|
||||
if (c.err == "") != (err == nil) || (err != nil && !strings.Contains(err.Error(), c.err)) || a.Revision != c.rev {
|
||||
t.Errorf("%s: %+v, %v", name, a, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
t.Run("an event names its state on its subject, and decodes as it was encoded", func(t *testing.T) {
|
||||
if s := StateOfEvent("mesh.stream.mesh-delivery-indexer.event.views.config-candidates"); s != "config-candidates" {
|
||||
t.Errorf("state of subject = %q", s)
|
||||
|
||||
Reference in New Issue
Block a user