Files
mesh-catalog/modules/nats/cmd/nats-tools/keptools_test.go
T
jschoubben b6884796e7 NATS4: the bus module takes and restores a restore point of a module's kept data, beside the live data
Verbs snapshot-kept and restore-kept (with aside-only and discard-aside) on the seat mesh-broker, handed to the
keeper; taken over from the join's 0480e16f and c4e88eaa, without the verbs aside and discard-aside and the tool
nats_kept, which no design line gives.

Builds: design 47 G §296, §297, §302, §337, §405

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
2026-11-11 12:08:05 +01:00

83 lines
3.5 KiB
Go

package main
import (
"encoding/json"
"strings"
"testing"
"time"
stdio "git.novox.be/novox/mesh-sdk/go"
"nats-tools/internal/keeper"
)
// The seat mesh-broker's verbs for a module's kept data hand their request to the keeper as the proved caller, wait for
// its end on the watch of the requests, and say what a restore set aside (design 47, "A module's kept data on the bus").
func TestTheKeptDataVerbsAreHandedToTheKeeperAndReadBack(t *testing.T) {
held := map[string]keeper.Request{}
rs := requestStore{
Put: func(key string, r keeper.Request) error { held[key] = r; return nil },
Get: func(key string) (*keeper.Request, error) {
r, ok := held[key]
if !ok {
return nil, nil
}
return &r, nil
},
Now: func() time.Time { return time.Date(2026, 11, 9, 20, 0, 0, 0, time.UTC) },
// The keeper's end, as its watch hands it.
Ended: func(id string, within time.Duration) *keeper.Request {
r := held[id]
r.Done, r.Outcome, r.Dir = time.Unix(1, 0), "done", "/keeper/points/idx/modules/f0-abc"
held[id] = r
return &r
},
}
tools := map[string]stdio.Tool{}
for _, tl := range keptTools(rs) {
tools[tl.Name] = tl
}
for _, verb := range KeptVerbs {
if _, ok := tools["mesh-broker."+verb]; !ok {
t.Errorf("the verb mesh-broker.%s is not served", verb)
}
}
out, err := tools["mesh-broker.snapshot-kept"].Run(map[string]any{"module": "idx", "local-name": "modules", "form": float64(1),
"fingerprint": "abc", "catch-up": "indexer_take_in", "why": "before form 1", "caller": "person.mallory"})
if err != nil {
t.Fatal(err)
}
if r := out.(*keeper.Request); r.Verb != keeper.VerbSnapshotKept || r.Form != 1 || r.Fingerprint != "abc" || r.CatchUp != "indexer_take_in" ||
r.Caller != stdio.Unproven || r.Dir == "" {
t.Errorf("answered %+v: the request for form 1 before abc, for the caller the runtime proved, with the directory the keeper answered", r)
}
for name, args := range map[string]map[string]any{
"mesh-broker.snapshot-kept": {"module": "idx", "local-name": "modules", "fingerprint": "abc"},
"mesh-broker.restore-kept": {"module": "idx", "local-name": "modules", "fingerprint": "abc", "why": "revert"},
"mesh-broker.restore-kept#aside-only": {"aside-only": true, "why": "x"},
} {
name, _, _ = strings.Cut(name, "#")
if _, err := tools[name].Run(args); err == nil {
t.Errorf("%s with %v was handed to the keeper", name, args)
}
}
if out, err := tools["mesh-broker.restore-kept"].Run(map[string]any{"module": "idx", "local-name": "modules", "fingerprint": "abc",
"from": "/restored/f0-abc", "why": "revert"}); err != nil || out.(*keeper.Request).From != "/restored/f0-abc" {
t.Errorf("restore answered %+v (%v)", out, err)
}
// `aside-only` sets the live data aside and restores nothing; `discard-aside` removes the data set aside (§405).
if out, err := tools["mesh-broker.restore-kept"].Run(map[string]any{"module": "idx", "local-name": "modules", "aside-only": true, "why": "revert"}); err != nil ||
!out.(*keeper.Request).AsideOnly || out.(*keeper.Request).Verb != keeper.VerbRestoreKept {
t.Errorf("aside-only answered %+v (%v)", out, err)
}
if out, err := tools["mesh-broker.restore-kept"].Run(map[string]any{"discard-aside": "idx.modules.1", "why": "operator's call"}); err != nil ||
out.(*keeper.Request).Verb != keeper.VerbDiscardAside || out.(*keeper.Request).From != "idx.modules.1" {
t.Errorf("discard-aside answered %+v (%v)", out, err)
}
}
func toJSON(v any) string {
b, _ := json.Marshal(v)
return string(b)
}