The approval unit table's in-memory state held `<node>.act.sha256:<hex>`, a key the live bus refuses, so the table passed while every marked call failed on the bus. meshtest.State now refuses a key outside letters, digits and -/_= in dot-separated names, as the bus does, checked with mesh-sdk's keys.CheckName per name; a row of the refusals table holds it. Builds: design 46 7-approval-mark §13, §22; lead answer A-209 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
114 lines
2.5 KiB
Go
114 lines
2.5 KiB
Go
package meshtest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
|
)
|
|
|
|
// State is a module's state as a fixture, the shape a gate's Store takes: Fail makes every read and write fail, as a bus
|
|
// refusing them; Vals are its values by key. A key the bus refuses (a name outside letters, digits and -/_=, in
|
|
// dot-separated names) is refused here too, so no test passes a key the live bus refuses (lead answer A-209).
|
|
type State struct {
|
|
mu sync.Mutex
|
|
Vals map[string]json.RawMessage
|
|
revs map[string]uint64
|
|
n uint64
|
|
Fail error
|
|
}
|
|
|
|
// checkKey refuses a key the bus would.
|
|
func checkKey(k string) error {
|
|
for _, part := range strings.Split(k, ".") {
|
|
if keys.CheckName("key", part) != nil {
|
|
return fmt.Errorf("%q is not a key the bus can hold: letters, digits and -/_=, in dot-separated names", k)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewState is an empty State.
|
|
func NewState() *State { return &State{Vals: map[string]json.RawMessage{}, revs: map[string]uint64{}} }
|
|
|
|
func (m *State) Get(k string) (*stdio.StateEntry, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if err := checkKey(k); err != nil {
|
|
return nil, err
|
|
}
|
|
if m.Fail != nil {
|
|
return nil, m.Fail
|
|
}
|
|
v, ok := m.Vals[k]
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
return &stdio.StateEntry{Key: k, Value: v, Revision: m.revs[k]}, nil
|
|
}
|
|
func (m *State) Create(k string, v any) (uint64, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if err := checkKey(k); err != nil {
|
|
return 0, err
|
|
}
|
|
if m.Fail != nil {
|
|
return 0, m.Fail
|
|
}
|
|
if _, ok := m.Vals[k]; ok {
|
|
return 0, stdio.ErrChanged
|
|
}
|
|
return m.put(k, v)
|
|
}
|
|
func (m *State) Update(k string, v any, rev uint64) (uint64, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if err := checkKey(k); err != nil {
|
|
return 0, err
|
|
}
|
|
if m.Fail != nil {
|
|
return 0, m.Fail
|
|
}
|
|
if rev == 0 || m.revs[k] != rev {
|
|
return 0, stdio.ErrChanged
|
|
}
|
|
return m.put(k, v)
|
|
}
|
|
func (m *State) Put(k string, v any) (uint64, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if err := checkKey(k); err != nil {
|
|
return 0, err
|
|
}
|
|
if m.Fail != nil {
|
|
return 0, m.Fail
|
|
}
|
|
return m.put(k, v)
|
|
}
|
|
func (m *State) Keys() ([]string, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.Fail != nil {
|
|
return nil, m.Fail
|
|
}
|
|
var out []string
|
|
for k := range m.Vals {
|
|
out = append(out, k)
|
|
}
|
|
sort.Strings(out)
|
|
return out, nil
|
|
}
|
|
func (m *State) put(k string, v any) (uint64, error) {
|
|
raw, ok := v.(json.RawMessage)
|
|
if !ok {
|
|
raw, _ = json.Marshal(v)
|
|
}
|
|
m.n++
|
|
m.Vals[k], m.revs[k] = raw, m.n
|
|
return m.n, nil
|
|
}
|