CAT-K3: each operation of the records' ledger types has its effect on a head, tested against the manifest's own types
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buscalls"
|
||||
)
|
||||
|
||||
// Each operation of the ledger's types has the effect on a head the design says (ADR 0597 §2, to-be 55 §2.1): a
|
||||
// write-section puts the section's title and digest, never its text, among the pending sections; an operation that
|
||||
// makes a version sets the head and empties them; the others set the head and leave them.
|
||||
func TestEachOperationHasItsEffectOnAHead(t *testing.T) {
|
||||
text := "the section's text"
|
||||
for _, c := range []struct {
|
||||
op string
|
||||
body map[string]any
|
||||
pendingKept bool // the pending sections stand after it
|
||||
clears bool // the pending sections are emptied by it
|
||||
}{
|
||||
{OpReserve, map[string]any{"entry": map[string]any{"v": 1}}, false, false},
|
||||
{OpPropose, map[string]any{"entry": map[string]any{"v": 1}, "pending-sections": map[string]any{}}, false, true},
|
||||
{OpSubmit, map[string]any{"entry": map[string]any{"v": 1}, "pending-sections": map[string]any{}}, false, true},
|
||||
{OpDrop, map[string]any{"entry": map[string]any{"v": 1}, "pending-sections": map[string]any{}}, false, true},
|
||||
{OpImport, map[string]any{"entry": map[string]any{"v": 1}, "pending-sections": map[string]any{}}, false, true},
|
||||
{OpForm, map[string]any{"entry": map[string]any{"v": 1}, "pending-sections": map[string]any{}}, false, true},
|
||||
{OpTakeOver, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
{OpReview, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
{OpAsk, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
{OpWarrant, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
{OpAccept, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
{OpWithdraw, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
{OpRetract, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
{OpDraft, map[string]any{"entry": map[string]any{"v": 1}}, true, false},
|
||||
} {
|
||||
f := newFakeLedger(t)
|
||||
apply := func(op string, body map[string]any) {
|
||||
t.Helper()
|
||||
raw, _ := json.Marshal(body)
|
||||
var got buscalls.Applied
|
||||
if err := f.Call(buscalls.LedgerApply, buscalls.ApplyRequest{Ledger: LedgerName, Instance: "decision/0001", Operation: op, Body: raw}, &got); err != nil {
|
||||
t.Fatalf("%s: %v", op, err)
|
||||
}
|
||||
}
|
||||
apply(OpWriteSection, map[string]any{"title": "Context", "text": text, "digest": SectionDigest(text)})
|
||||
body := map[string]any{"kind": "decision", "number": 1}
|
||||
for k, v := range c.body {
|
||||
body[k] = v
|
||||
}
|
||||
apply(c.op, body)
|
||||
var st map[string]any
|
||||
_ = json.Unmarshal(mustJSON(t, f.state["decision/0001"]), &st)
|
||||
pending, _ := st["pending-sections"].(map[string]any)
|
||||
switch {
|
||||
case c.clears && len(pending) != 0:
|
||||
t.Errorf("%s leaves pending sections %v", c.op, pending)
|
||||
case c.pendingKept && len(pending) != 1:
|
||||
t.Errorf("%s does not keep the pending sections: %v", c.op, pending)
|
||||
}
|
||||
if c.op == OpReserve && len(pending) != 1 {
|
||||
t.Errorf("reserve touched the pending sections: %v", pending)
|
||||
}
|
||||
if st["entry"] == nil || st["sequence"] != float64(2) {
|
||||
t.Errorf("%s: head %v", c.op, st)
|
||||
}
|
||||
if a, _ := st["author"].(map[string]any); a["account"] != "mesh-knowledge" {
|
||||
t.Errorf("%s: author %v", c.op, st["author"])
|
||||
}
|
||||
if raw, _ := json.Marshal(st); json.Valid(raw) && strings.Contains(string(raw), text) {
|
||||
t.Errorf("%s: the head holds the section's text", c.op)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A write-section whose digest is not its text's is refused by apply, and an operation the type does not allow is too.
|
||||
func TestTheLedgerRefusesADigestThatIsNotTheTextsAndAnOperationTheTypeLacks(t *testing.T) {
|
||||
f := newFakeLedger(t)
|
||||
var got buscalls.Applied
|
||||
bad, _ := json.Marshal(map[string]any{"title": "Context", "text": "a", "digest": SectionDigest("b")})
|
||||
if err := f.Call(buscalls.LedgerApply, buscalls.ApplyRequest{Ledger: LedgerName, Instance: "decision/0001", Operation: OpWriteSection, Body: bad}, &got); err == nil {
|
||||
t.Error("a digest of another text was taken")
|
||||
}
|
||||
if err := f.Call(buscalls.LedgerApply, buscalls.ApplyRequest{Ledger: LedgerName, Instance: "decision/0001", Operation: "delete"}, &got); err == nil {
|
||||
t.Error("an operation the type lacks was taken")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user