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:
2026-11-04 11:47:59 +01:00
parent e0d7644bb6
commit 80a7cee6f3
@@ -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")
}
}