Files
mesh-contracts/go/clients/operatorchannelclient/client_test.go
T
2026-11-04 13:27:00 +01:00

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)
}
}