The test moved from mesh-sdk's process package, which no longer names the client. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
142 lines
4.5 KiB
Go
142 lines
4.5 KiB
Go
package operatorchannelclient
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
seat "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel"
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
"git.novox.be/novox/mesh-sdk/go/process"
|
|
)
|
|
|
|
type mem struct {
|
|
vals map[string]json.RawMessage
|
|
revs map[string]uint64
|
|
n uint64
|
|
}
|
|
|
|
func (m *mem) Get(k string) (*stdio.StateEntry, error) {
|
|
v, ok := m.vals[k]
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
return &stdio.StateEntry{Key: k, Value: v, Revision: m.revs[k]}, nil
|
|
}
|
|
func (m *mem) Create(k string, v any) (uint64, error) {
|
|
if _, ok := m.vals[k]; ok {
|
|
return 0, stdio.ErrChanged
|
|
}
|
|
return m.put(k, v)
|
|
}
|
|
func (m *mem) Update(k string, v any, rev uint64) (uint64, error) {
|
|
if rev == 0 || m.revs[k] != rev {
|
|
return 0, stdio.ErrChanged
|
|
}
|
|
return m.put(k, v)
|
|
}
|
|
func (m *mem) put(k string, v any) (uint64, error) {
|
|
raw, _ := json.Marshal(v)
|
|
m.n++
|
|
m.vals[k], m.revs[k] = raw, m.n
|
|
return m.n, nil
|
|
}
|
|
|
|
func client() (*Client, map[string][]string) {
|
|
sent := map[string][]string{}
|
|
c := &Client{Module: "an-asker", State: &mem{vals: map[string]json.RawMessage{}, revs: map[string]uint64{}},
|
|
Now: func() time.Time { return now },
|
|
Publish: func(subject string, body any, id string) error {
|
|
sent[subject] = append(sent[subject], id)
|
|
return nil
|
|
}}
|
|
return c, sent
|
|
}
|
|
|
|
// releaseHQ is the act the ask's one option binds.
|
|
var releaseHQ = map[string]string{"verb": "release", "delivery": "hq"}
|
|
|
|
func askedHeld() seat.Ask {
|
|
binds, _ := ActDigest(releaseHQ)
|
|
return seat.Ask{ID: "d-1", Headline: "Delivery of hq held", Explanation: "Needs you: release it, or stop it.",
|
|
Who: Operator, Expires: FormatTime(now.Add(time.Hour)), OnExpiry: "the delivery stays held",
|
|
Options: []seat.Option{{ID: "release", Label: "Release", Does: "it goes on", Level: Approve, Binds: binds}}}
|
|
}
|
|
|
|
func askedWarrant() seat.Warrant {
|
|
return seat.Warrant{Ask: "d-1", Asker: "an-asker", Outcome: OutcomeChosen, Option: "release",
|
|
Label: "Release", Level: Approve, At: FormatTime(now.Add(time.Minute)), AskDigest: Digest(askedHeld()),
|
|
By: &seat.Person{Who: Operator, Kind: "telegram", Identity: "42", Verified: "user id verified"}}
|
|
}
|
|
|
|
func TestAnAskIsKeptThenPublishedUnderTheModulesOwnName(t *testing.T) {
|
|
var _ Store = process.State{} // a process connection's state keeps the asks (issue 1188 AC2)
|
|
c, sent := client()
|
|
if err := c.Ask(askedHeld()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := sent[seat.AcceptSubjectAsk("an-asker")]; len(got) != 1 || got[0] != "d-1" {
|
|
t.Fatalf("%v", sent)
|
|
}
|
|
if err := c.Ask(askedHeld()); err == nil || !strings.Contains(err.Error(), "already made") {
|
|
t.Errorf("the same id was asked twice: %v", err)
|
|
}
|
|
bad := askedHeld()
|
|
bad.Who = "agent"
|
|
if err := c.Ask(bad); err == nil {
|
|
t.Error("an ask the contract refuses was published")
|
|
}
|
|
}
|
|
|
|
func TestAWarrantIsActedOnOnceAndOnlyForItsOwnAsk(t *testing.T) {
|
|
c, _ := client()
|
|
_ = c.Ask(askedHeld())
|
|
acted := 0
|
|
act := func(o seat.Option, _ seat.Warrant) error {
|
|
if err := Performs(o, releaseHQ); err != nil {
|
|
t.Errorf("the bound act: %v", err)
|
|
}
|
|
acted++
|
|
return nil
|
|
}
|
|
if h, err := c.Take(askedWarrant(), act); h != Acted || err != nil || acted != 1 {
|
|
t.Fatalf("%s %v %d", h, err, acted)
|
|
}
|
|
if h, _ := c.Take(askedWarrant(), act); h != Done || acted != 1 {
|
|
t.Errorf("a redelivery was acted on: %s", h)
|
|
}
|
|
for why, change := range map[string]func(*seat.Warrant){
|
|
"another asker": func(w *seat.Warrant) { w.Asker = "mesh-controller" },
|
|
"an ask not made": func(w *seat.Warrant) { w.Ask = "d-9" },
|
|
"another level": func(w *seat.Warrant) { w.Level = Acknowledge },
|
|
"an option not offered": func(w *seat.Warrant) { w.Option = "delete" },
|
|
"after its expiry": func(w *seat.Warrant) { w.At = FormatTime(now.Add(2 * time.Hour)) },
|
|
"another ask's digest": func(w *seat.Warrant) { w.AskDigest = "sha256:00" },
|
|
} {
|
|
c2, _ := client()
|
|
_ = c2.Ask(askedHeld())
|
|
w := askedWarrant()
|
|
change(&w)
|
|
if h, _ := c2.Take(w, act); h == Acted {
|
|
t.Errorf("a warrant from %s was acted on", why)
|
|
}
|
|
}
|
|
if acted != 1 {
|
|
t.Errorf("acted %d times", acted)
|
|
}
|
|
}
|
|
|
|
func TestAnAskEndedWithoutAChoiceIsRecordedAndNothingDone(t *testing.T) {
|
|
c, _ := client()
|
|
_ = c.Ask(askedHeld())
|
|
w := askedWarrant()
|
|
w.Outcome, w.Option, w.By = OutcomeExpired, "", nil
|
|
if h, err := c.Take(w, func(seat.Option, seat.Warrant) error { t.Fatal("acted"); return nil }); h != Recorded || err != nil {
|
|
t.Fatalf("%s %v", h, err)
|
|
}
|
|
if h, _ := c.Take(askedWarrant(), func(seat.Option, seat.Warrant) error { t.Fatal("acted after it ended"); return nil }); h != Done {
|
|
t.Errorf("%s", h)
|
|
}
|
|
}
|