Files
mesh-catalog/modules/pacman/cmd/pacman-tools/bus_fake_test.go
T

154 lines
3.2 KiB
Go

package main
import (
"encoding/json"
"fmt"
"sort"
"strings"
"sync"
stdio "git.novox.be/novox/mesh-sdk/go"
)
// memBucket is a state in memory, with the bus's revisions and its refusal of a lost race.
type memBucket struct {
mu sync.Mutex
values map[string][]byte
revs map[string]uint64
rev uint64
writes []string // "op key", in order
fail map[string]error
}
func newBucket() *memBucket {
return &memBucket{values: map[string][]byte{}, revs: map[string]uint64{}, fail: map[string]error{}}
}
func (b *memBucket) Get(key string) (*stdio.StateEntry, error) {
b.mu.Lock()
defer b.mu.Unlock()
if err := b.fail["get "+key]; err != nil {
return nil, err
}
v, ok := b.values[key]
if !ok {
return nil, nil
}
return &stdio.StateEntry{Key: key, Value: json.RawMessage(v), Revision: b.revs[key]}, nil
}
func (b *memBucket) write(op, key string, value any) (uint64, error) {
if err := b.fail[op+" "+key]; err != nil {
return 0, err
}
data, err := json.Marshal(value)
if err != nil {
return 0, err
}
b.rev++
b.values[key], b.revs[key] = data, b.rev
b.writes = append(b.writes, op+" "+key)
return b.rev, nil
}
func (b *memBucket) Create(key string, value any) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()
if _, ok := b.values[key]; ok {
return 0, fmt.Errorf("%s: %w", key, stdio.ErrChanged)
}
return b.write("create", key, value)
}
func (b *memBucket) Update(key string, value any, revision uint64) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()
if b.revs[key] != revision {
return 0, fmt.Errorf("%s: %w", key, stdio.ErrChanged)
}
return b.write("update", key, value)
}
func (b *memBucket) Put(key string, value any) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()
return b.write("put", key, value)
}
func (b *memBucket) Delete(key string) error {
b.mu.Lock()
defer b.mu.Unlock()
if err := b.fail["delete "+key]; err != nil {
return err
}
delete(b.values, key)
delete(b.revs, key)
b.writes = append(b.writes, "delete "+key)
return nil
}
func (b *memBucket) Keys() ([]string, error) {
b.mu.Lock()
defer b.mu.Unlock()
keys := []string{}
for k := range b.values {
keys = append(keys, k)
}
sort.Strings(keys)
return keys, nil
}
// value reads a key into its shape; a missing key is the zero value and false.
func (b *memBucket) value(key string, v any) bool {
b.mu.Lock()
defer b.mu.Unlock()
raw, ok := b.values[key]
if ok {
if err := json.Unmarshal(raw, v); err != nil {
panic(err)
}
}
return ok
}
// writesTo are the writes to keys starting with prefix.
func (b *memBucket) writesTo(prefix string) []string {
b.mu.Lock()
defer b.mu.Unlock()
var out []string
for _, w := range b.writes {
if _, key, _ := strings.Cut(w, " "); strings.HasPrefix(key, prefix) {
out = append(out, w)
}
}
return out
}
// sentEvent is one event a fake sink was handed.
type sentEvent struct {
Name string
Body any
}
type eventLog struct {
mu sync.Mutex
sent []sentEvent
}
func (e *eventLog) Emit(name string, body any, _ ...stdio.EmitOptions) error {
e.mu.Lock()
defer e.mu.Unlock()
e.sent = append(e.sent, sentEvent{name, body})
return nil
}
func (e *eventLog) names() []string {
e.mu.Lock()
defer e.mu.Unlock()
var out []string
for _, s := range e.sent {
out = append(out, s.Name)
}
return out
}