Builds: design 47 G §60, §84, §258 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
111 lines
3.9 KiB
Go
111 lines
3.9 KiB
Go
package stateobjects
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/kvlimit"
|
|
"git.novox.be/novox/mesh-sdk/go/views"
|
|
)
|
|
|
|
type acts struct {
|
|
done []views.HandAct
|
|
err error
|
|
}
|
|
|
|
func (a *acts) RecordHandAct(h views.HandAct) error {
|
|
if a.err != nil {
|
|
return a.err
|
|
}
|
|
a.done = append(a.done, h)
|
|
return nil
|
|
}
|
|
|
|
// ADR 0666 §25: the four tools read what each store holds and drop only what is no longer live, each drop only with who
|
|
// asks and why, recorded as a hand act first.
|
|
func TestTheObjectToolsReadEverythingAndDropOnlyWhatIsNotLive(t *testing.T) {
|
|
m := newMemory()
|
|
m.modified = map[string]time.Time{}
|
|
v := m.values()
|
|
big := big(2 * kvlimit.ModuleStateValueBytes)
|
|
for _, k := range []string{"live", "gone"} {
|
|
if err := v.Put(k, big); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
// the two keys hold one object each (different keys): delete one key
|
|
if err := v.Delete("gone"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Date(2026, 11, 10, 12, 0, 0, 0, time.UTC)
|
|
rec := &acts{}
|
|
sentStore := newMemory()
|
|
sentStore.modified = map[string]time.Time{"reply.list.aa": now.Add(-48 * time.Hour), "event.done.bb": now.Add(-time.Hour)}
|
|
sentStore.objects = map[string][]byte{"reply.list.aa": []byte("1"), "event.done.bb": []byte("22")}
|
|
tools := Tools("m", []State{{Name: "views", Values: v}}, &Sent{Objects: memObjects{sentStore}}, rec, func() time.Time { return now })
|
|
by := map[string]func(map[string]any) (any, error){}
|
|
for _, tl := range tools {
|
|
by[tl.Name] = tl.Run
|
|
}
|
|
for _, name := range []string{"m_state_objects", "m_state_objects_drop", "m_sent_objects", "m_sent_objects_drop"} {
|
|
if by[name] == nil {
|
|
t.Fatalf("no tool %s", name)
|
|
}
|
|
}
|
|
read, err := by["m_state_objects"](map[string]any{})
|
|
if err != nil || read.(map[string]any)["key-gone"] != 1 {
|
|
t.Fatalf("the state's objects: %v, %v", read, err)
|
|
}
|
|
listed, err := by["m_sent_objects"](map[string]any{})
|
|
if objs := listed.(map[string]any)["objects"].([]SentObject); err != nil || len(objs) != 2 {
|
|
t.Fatalf("the sent objects: %v, %v", listed, err)
|
|
}
|
|
// each drop refuses without by and why, and records nothing
|
|
for _, tc := range []struct {
|
|
tool string
|
|
args map[string]any
|
|
}{
|
|
{"m_state_objects_drop", map[string]any{"why": "x"}},
|
|
{"m_state_objects_drop", map[string]any{"by": "a@b"}},
|
|
{"m_sent_objects_drop", map[string]any{"why": "x", "older-than": "24h"}},
|
|
{"m_sent_objects_drop", map[string]any{"by": "a@b", "why": "x", "older-than": "soon"}},
|
|
} {
|
|
if _, err := by[tc.tool](tc.args); err == nil {
|
|
t.Errorf("%s %v was accepted", tc.tool, tc.args)
|
|
}
|
|
}
|
|
if len(rec.done) != 0 {
|
|
t.Fatalf("a refused drop recorded %v", rec.done)
|
|
}
|
|
// the hand act is recorded first: one that cannot be recorded drops nothing
|
|
rec.err = errors.New("hand acts are down")
|
|
if _, err := by["m_state_objects_drop"](map[string]any{"by": "a@b", "why": "tidy"}); err == nil || !strings.Contains(err.Error(), "nothing was dropped") {
|
|
t.Fatalf("an unrecorded drop: %v", err)
|
|
}
|
|
if kept, _ := v.Kept(); len(kept) != 2 {
|
|
t.Fatalf("an unrecorded drop removed objects: %d left", len(kept))
|
|
}
|
|
rec.err = nil
|
|
// the state drop takes only the key-gone object
|
|
out, err := by["m_state_objects_drop"](map[string]any{"by": "a@b", "why": "tidy"})
|
|
if err != nil || len(out.(map[string]any)["dropped"].([]string)) != 1 {
|
|
t.Fatalf("the state drop: %v, %v", out, err)
|
|
}
|
|
if kept, _ := v.Kept(); len(kept) != 1 || kept[0].KeyGone {
|
|
t.Fatalf("the live object did not survive: %+v", kept)
|
|
}
|
|
// the sent drop takes only objects past the age
|
|
out, err = by["m_sent_objects_drop"](map[string]any{"by": "a@b", "why": "tidy", "older-than": "24h"})
|
|
if err != nil || len(out.(map[string]any)["dropped"].([]string)) != 1 {
|
|
t.Fatalf("the sent drop: %v, %v", out, err)
|
|
}
|
|
if _, ok := sentStore.objects["event.done.bb"]; !ok || len(sentStore.objects) != 1 {
|
|
t.Fatalf("the young sent object did not survive: %v", sentStore.objects)
|
|
}
|
|
if len(rec.done) != 2 {
|
|
t.Fatalf("%d hand acts, want 2", len(rec.done))
|
|
}
|
|
}
|