package conditions import ( "context" "encoding/json" "errors" "sort" "sync" "time" ) // InMemory is a store and a history held in this process: for tests, and for nothing else — a // condition kept here is forgotten by a restart, which is the fault the store exists to remove. type InMemory struct { mu sync.Mutex values map[string]Entry revision uint64 events []Event // Fail, when set, is what every read and write answers: a store that is away. Fail error } // NewInMemory is an empty store. func NewInMemory() *InMemory { return &InMemory{values: map[string]Entry{}} } func (m *InMemory) Get(_ context.Context, key string) (Entry, bool, error) { m.mu.Lock() defer m.mu.Unlock() if m.Fail != nil { return Entry{}, false, m.Fail } e, ok := m.values[key] return e, ok, nil } func (m *InMemory) Create(_ context.Context, key string, value []byte) error { m.mu.Lock() defer m.mu.Unlock() if m.Fail != nil { return m.Fail } if _, ok := m.values[key]; ok { return ErrMoved } m.revision++ m.values[key] = Entry{Value: value, Revision: m.revision} return nil } func (m *InMemory) Update(_ context.Context, key string, value []byte, revision uint64) error { m.mu.Lock() defer m.mu.Unlock() if m.Fail != nil { return m.Fail } if e, ok := m.values[key]; !ok || e.Revision != revision { return ErrMoved } m.revision++ m.values[key] = Entry{Value: value, Revision: m.revision} return nil } func (m *InMemory) Delete(_ context.Context, key string, revision uint64) error { m.mu.Lock() defer m.mu.Unlock() if m.Fail != nil { return m.Fail } if e, ok := m.values[key]; !ok || e.Revision != revision { return ErrMoved } delete(m.values, key) return nil } func (m *InMemory) All(context.Context) (map[string]Entry, error) { m.mu.Lock() defer m.mu.Unlock() if m.Fail != nil { return nil, m.Fail } out := make(map[string]Entry, len(m.values)) for k, v := range m.values { out[k] = v } return out, nil } func (m *InMemory) Append(_ context.Context, e Event) error { m.mu.Lock() defer m.mu.Unlock() if m.Fail != nil { return m.Fail } m.events = append(m.events, e) return nil } func (m *InMemory) Since(_ context.Context, since time.Time) ([]Event, error) { m.mu.Lock() defer m.mu.Unlock() if m.Fail != nil { return nil, m.Fail } var out []Event for _, e := range m.events { if !e.At.Before(since) { out = append(out, e) } } sort.SliceStable(out, func(i, j int) bool { return out[i].At.Before(out[j].At) }) return out, nil } // Told is a teller that remembers what it was told, for tests. type Told struct { mu sync.Mutex Events []Event Names []string Fail error } func (t *Told) PublishSeatEvent(_ context.Context, seat, event string, body []byte) error { t.mu.Lock() defer t.mu.Unlock() if t.Fail != nil { return t.Fail } if seat != Seat { return errors.New("told under the wrong seat: " + seat) } var e Event if err := json.Unmarshal(body, &e); err != nil { return err } t.Events = append(t.Events, e) t.Names = append(t.Names, event) return nil } // Said is a copy of what was told so far. func (t *Told) Said() []Event { t.mu.Lock() defer t.mu.Unlock() return append([]Event(nil), t.Events...) }