114 lines
3.0 KiB
Go
114 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// fakeOperator is the gate's bucket asks and the teller behind it: it keeps the asks written, and answers as the test says
|
|
// (the teller's answer under the ask's key). In auto mode it approves every ask the moment it is read (the tests of other
|
|
// behaviour, which are not about the Approve). An ask is known here by its key (mesh-sdk go/delivery MergeAskKey), as the
|
|
// delivery keeps it.
|
|
type fakeOperator struct {
|
|
mu sync.Mutex
|
|
now func() time.Time
|
|
auto bool
|
|
asked []asks.Ask
|
|
byKey map[string]sdk.Ask
|
|
order []string
|
|
seen map[string]bool
|
|
cancelled []string
|
|
state map[string]string
|
|
answers map[string]*sdk.Answer
|
|
refuse error
|
|
}
|
|
|
|
func newFakeOperator(now func() time.Time) *fakeOperator {
|
|
return &fakeOperator{now: now, byKey: map[string]sdk.Ask{}, seen: map[string]bool{}, state: map[string]string{}, answers: map[string]*sdk.Answer{}}
|
|
}
|
|
|
|
func (o *fakeOperator) Ask(key string, a sdk.Ask) error {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
if o.refuse != nil {
|
|
return o.refuse
|
|
}
|
|
if err := a.Ask.Check(o.now()); err != nil {
|
|
return err
|
|
}
|
|
if _, err := sdk.EncodeAsk(a); err != nil {
|
|
return err
|
|
}
|
|
if o.state[key] == "open" && sdk.SameAsk(o.byKey[key], a) {
|
|
return nil // the same ask is there, as kvOperator leaves it
|
|
}
|
|
o.asked = append(o.asked, a.Ask)
|
|
o.byKey[key] = a
|
|
if _, known := o.seen[key]; !known {
|
|
o.order = append(o.order, key)
|
|
o.seen[key] = true
|
|
}
|
|
o.state[key] = "open"
|
|
delete(o.answers, key)
|
|
return nil
|
|
}
|
|
|
|
func (o *fakeOperator) Cancel(key string) error {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
o.cancelled = append(o.cancelled, key)
|
|
o.state[key] = "cancelled"
|
|
return nil
|
|
}
|
|
|
|
func (o *fakeOperator) Where(key string) (*sdk.Answer, error) {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
if o.auto && o.state[key] == "open" {
|
|
o.answerLocked(key, OptionApprove)
|
|
}
|
|
return o.answers[key], nil
|
|
}
|
|
|
|
// ask is the router's ask at a key, as the gate composed it.
|
|
func (o *fakeOperator) ask(key string) asks.Ask {
|
|
if a, ok := o.byKey[key]; ok {
|
|
return a.Ask
|
|
}
|
|
panic("no ask " + key)
|
|
}
|
|
|
|
func (o *fakeOperator) answerLocked(key, option string) {
|
|
a := o.ask(key)
|
|
opt, _ := a.Option(option)
|
|
o.state[key] = "answered"
|
|
o.answers[key] = &sdk.Answer{Option: option, Warrant: fmt.Sprintf("%s@%s", a.ID, o.now().UTC().Format(time.RFC3339)),
|
|
Words: "the operator, via telegram, chose " + opt.Label}
|
|
}
|
|
|
|
// answer is the operator's choice, proven to the teller by the router and written by it.
|
|
func (o *fakeOperator) answer(key, option string) {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
o.answerLocked(key, option)
|
|
}
|
|
|
|
// open is the keys of the asks waiting for an answer, in the order asked.
|
|
func (o *fakeOperator) open() []string {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
var out []string
|
|
for _, k := range o.order {
|
|
if o.state[k] == "open" {
|
|
out = append(out, k)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (o *fakeOperator) count() int { o.mu.Lock(); defer o.mu.Unlock(); return len(o.asked) }
|