Stamp a ledger operation with the caller the runner proved for the call in flight it names, refusing another module's call or one not in flight; serve the ledger calls on the pipe (issue 1966 T5)
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buscalls"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/membership"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"github.com/novox/mesh-tools/node-tools/internal/bus"
|
||||
mt "github.com/novox/mesh-tools/node-tools/internal/meshtest"
|
||||
)
|
||||
|
||||
@@ -72,3 +77,101 @@ func TestTheLedgerRepairsAreToolsOfEveryLedgerOwner(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// aLedger raises one ledger of module m on the bus as the bus module would create it, binds it in m's membership, and
|
||||
// answers the runtime's buses for m and for other, its types given as a test gives them.
|
||||
func aLedger(t *testing.T) (m, other *moduleBus, mesh *mt.Mesh) {
|
||||
t.Helper()
|
||||
mesh = mt.New(t)
|
||||
nc, err := nats.Connect(mt.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(nc.Close)
|
||||
js, _ := jetstream.New(nc)
|
||||
ctx := context.Background()
|
||||
for _, cfg := range []jetstream.StreamConfig{
|
||||
{Name: "LEDGER_m_records", Subjects: []string{"mesh.ledger.m.records.>"}},
|
||||
{Name: "SNAPSHOT_m_records", Subjects: []string{"mesh.ledger-snapshot.m.records.>"}, MaxMsgsPerSubject: 1},
|
||||
} {
|
||||
if _, err := js.CreateStream(ctx, cfg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: "m_records"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mem := mt.MembershipOf("m", "anchor", false, nil)
|
||||
mem.Bindings = []membership.BusBinding{{Local: "records", Storage: catalogue.StorageLedger, Template: catalogue.TemplateLedger,
|
||||
History: "LEDGER_m_records", Subject: "mesh.ledger.m.records", State: "m_records", Snapshot: "mesh.ledger-snapshot.m.records"}}
|
||||
mesh.Issue(t, mem)
|
||||
mesh.Issue(t, mt.MembershipOf("other", "anchor", false, nil))
|
||||
conn := connect(t, "node-tools", "anchor")
|
||||
conn.Follow("m")
|
||||
conn.Follow("other")
|
||||
mt.Until(t, func() error {
|
||||
if conn.Membership("m") == nil || conn.Membership("other") == nil {
|
||||
return errors.New("not issued yet")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
all := &consumers{conn: conn, logf: t.Logf, of: map[string]*moduleEvents{}, ledgers: ledgerSource{
|
||||
types: func(string, string) ([]catalogue.LedgerType, error) {
|
||||
return []catalogue.LedgerType{{Name: "decision", Schema: map[string]catalogue.LedgerField{"title": {Type: "string"}},
|
||||
Operations: []catalogue.LedgerOperation{{Name: "title", Effect: catalogue.EffectSetFields, Fields: []string{"title"}, Author: true}}}}, nil
|
||||
},
|
||||
writer: func(string) (*bus.Conn, error) { return conn, nil },
|
||||
}}
|
||||
return all.forModule("m").(*moduleBus), all.forModule("other").(*moduleBus), mesh
|
||||
}
|
||||
|
||||
// 1966/AC17, 1966/AC18: the author an apply is stamped with is the caller the runtime proved for the call in flight it
|
||||
// names, whatever author the bundle wrote; another module's call, or one not in flight, is refused naming it (ADR 0597 §7).
|
||||
func TestAnApplyIsStampedWithTheCallerTheRuntimeProved(t *testing.T) {
|
||||
m, other, _ := aLedger(t)
|
||||
call, end := m.Begin("op@shanks")
|
||||
defer end()
|
||||
theirs, endTheirs := other.Begin("op@shanks")
|
||||
defer endTheirs()
|
||||
forged := `"body":{"title":"T","author":{"account":"x","caller":"forged"}}`
|
||||
for _, c := range []struct{ name, params, caller, refusal string }{
|
||||
{"1966/AC17 a call in flight stamps its proven caller", `{"ledger":"records","instance":"decision/1","operation":"title",` + forged + `,"call":"` + call + `","author":"forged"}`, "op@shanks", ""},
|
||||
{"naming no call stamps the account alone", `{"ledger":"records","instance":"decision/2","operation":"title",` + forged + `}`, "", ""},
|
||||
{"1966/AC18 another module's call is refused, naming it", `{"ledger":"records","instance":"decision/3","operation":"title",` + forged + `,"call":"` + theirs + `"}`, "", "the call " + theirs + ", which is another module's"},
|
||||
{"a call not in flight is refused, naming it", `{"ledger":"records","instance":"decision/3","operation":"title",` + forged + `,"call":"c0ffee"}`, "", "the call c0ffee, which is not a tool call in flight"},
|
||||
} {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var req struct {
|
||||
Instance string `json:"instance"`
|
||||
}
|
||||
_ = json.Unmarshal([]byte(c.params), &req)
|
||||
_, err := m.BusState(buscalls.LedgerApply, json.RawMessage(c.params))
|
||||
if c.refusal != "" {
|
||||
if err == nil || !strings.Contains(err.Error(), c.refusal) {
|
||||
t.Fatalf("applied with %v, want refused with %q", err, c.refusal)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, err := m.BusState(buscalls.LedgerHistory, json.RawMessage(`{"ledger":"records","instance":"`+req.Instance+`"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var page buscalls.HistoryPage
|
||||
_ = json.Unmarshal(raw, &page)
|
||||
if len(page.Operations) != 1 || page.Operations[0].Author != (buscalls.Author{Account: "m", Caller: c.caller}) {
|
||||
t.Fatalf("the history holds %+v, want the author {m %s}", page.Operations, c.caller)
|
||||
}
|
||||
state, _ := m.BusState(buscalls.LedgerGet, json.RawMessage(`{"ledger":"records","instance":"`+req.Instance+`"}`))
|
||||
if !strings.Contains(string(state), `"author":{"account":"m"`) || strings.Contains(string(state), "forged") {
|
||||
t.Fatalf("the state took the bundle's author: %s", state)
|
||||
}
|
||||
})
|
||||
}
|
||||
end()
|
||||
if _, err := m.BusState(buscalls.LedgerApply, json.RawMessage(`{"ledger":"records","instance":"decision/4","operation":"title","call":"`+call+`"}`)); err == nil {
|
||||
t.Fatal("a call that ended was still taken as in flight")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user