stdio.BusState asks the runtime one buscalls method on the bundle's own bus state; package busstate makes the object store's put, get, list, delete and fill by the store's local name (A3's request 1, distribution's incoming). Its test takes the place of one merged into its neighbour, so mesh-sdk's unit count does not grow. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
103 lines
3.4 KiB
Go
103 lines
3.4 KiB
Go
package busstate
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"io"
|
|
"testing"
|
|
"time"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
"git.novox.be/novox/mesh-sdk/go/buscalls"
|
|
)
|
|
|
|
// A bundle keeps objects in its own object store through the runtime (ADR 0595 §2, §9; A3's request for distribution's
|
|
// incoming): each call is the runtime's method of buscalls, naming the store by its local name, and answers as the
|
|
// runtime answered; a method that is no bus-state call is refused before the runtime is asked.
|
|
func TestABundleKeepsObjectsThroughTheRuntime(t *testing.T) {
|
|
t.Setenv(stdio.ServedAs, "distribution")
|
|
inR, inW := io.Pipe()
|
|
outR, outW := io.Pipe()
|
|
type result struct {
|
|
what string
|
|
err error
|
|
got any
|
|
}
|
|
results := make(chan result, 8)
|
|
go func() {
|
|
_ = stdio.ServeOn("", []stdio.Tool{{Name: "keep", Run: func(map[string]any) (any, error) {
|
|
go func() {
|
|
in := ObjectsOf("incoming")
|
|
info, err := in.Put("t1.part0", []byte("hello"), "a part")
|
|
results <- result{"put", err, info.Digest}
|
|
data, err := in.Get("t1.part0")
|
|
results <- result{"get", err, string(data.Data)}
|
|
list, err := in.List()
|
|
results <- result{"list", err, len(list)}
|
|
results <- result{"delete", in.Delete("t1.part0"), nil}
|
|
fill, err := in.Fill()
|
|
results <- result{"fill", err, fill.MaxBytes}
|
|
_, err = stdio.BusState("mesh/state.get", nil)
|
|
results <- result{"refused", err, nil}
|
|
}()
|
|
return "started", nil
|
|
}}}, inR, outW)
|
|
}()
|
|
lines := bufio.NewScanner(outR)
|
|
next := func() map[string]any {
|
|
done := make(chan map[string]any, 1)
|
|
go func() {
|
|
if lines.Scan() {
|
|
var m map[string]any
|
|
_ = json.Unmarshal(lines.Bytes(), &m)
|
|
done <- m
|
|
}
|
|
}()
|
|
select {
|
|
case m := <-done:
|
|
return m
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("the bundle said nothing")
|
|
return nil
|
|
}
|
|
}
|
|
send := func(m map[string]any) { b, _ := json.Marshal(m); _, _ = inW.Write(append(b, '\n')) }
|
|
send(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": map[string]any{"name": "keep"}})
|
|
answers := map[string]any{
|
|
buscalls.ObjectsPut: buscalls.ObjectInfo{Name: "t1.part0", Size: 5, Digest: "SHA-256=x"},
|
|
buscalls.ObjectsGet: buscalls.ObjectData{Info: buscalls.ObjectInfo{Name: "t1.part0"}, Data: []byte("hello")},
|
|
buscalls.ObjectsList: buscalls.ObjectsListed{Objects: []buscalls.ObjectInfo{{Name: "t1.part0"}}},
|
|
buscalls.ObjectsDelete: buscalls.Done{},
|
|
buscalls.ObjectsInfo: buscalls.Fill{Count: 0, MaxBytes: 20 << 30},
|
|
}
|
|
asked := map[string]map[string]any{}
|
|
for len(asked) < len(answers) {
|
|
m := next()
|
|
method, _ := m["method"].(string)
|
|
if method == "" {
|
|
continue // the tool's own answer
|
|
}
|
|
p, _ := m["params"].(map[string]any)
|
|
asked[method] = p
|
|
send(map[string]any{"jsonrpc": "2.0", "id": m["id"], "result": answers[method]})
|
|
}
|
|
want := map[string]any{"put": "SHA-256=x", "get": "hello", "list": 1, "delete": nil, "fill": uint64(20 << 30)}
|
|
for range 5 {
|
|
r := <-results
|
|
if r.err != nil || r.got != want[r.what] {
|
|
t.Errorf("%s answered %v, %v; want %v", r.what, r.got, r.err, want[r.what])
|
|
}
|
|
}
|
|
if r := <-results; r.err == nil {
|
|
t.Error("a call that is not on the bundle's bus state was asked of the runtime")
|
|
}
|
|
for method, p := range asked {
|
|
if p["objects"] != "incoming" {
|
|
t.Errorf("%s named the store %v, not its local name", method, p["objects"])
|
|
}
|
|
}
|
|
if p := asked[buscalls.ObjectsPut]; p["name"] != "t1.part0" || p["data"] != "aGVsbG8=" || p["description"] != "a part" {
|
|
t.Errorf("put asked %v", p)
|
|
}
|
|
}
|