A warrant named the ask by id and the option by id, and the asker acted on whatever its record held for them. Now the ask carries, for every option above acknowledge, the digest of exactly what it performs (Option.Binds, ActDigest), the router puts the digest of the ask it took in the warrant (Warrant.AskDigest), For refuses a warrant whose digest is not that of the ask the asker keeps, and Option.Performs refuses an act the option did not bind. A warrant then authorises one act, as the person was shown it, once.
142 lines
4.3 KiB
Go
142 lines
4.3 KiB
Go
package asker
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
var now = time.Date(2026, 10, 8, 14, 0, 0, 0, time.UTC)
|
|
|
|
func client() (*Client, map[string][]string) {
|
|
sent := map[string][]string{}
|
|
c := &Client{Module: "mesh-delivery", 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 held() asks.Ask {
|
|
binds, _ := asks.ActDigest(releaseHQ)
|
|
return asks.Ask{ID: "d-1", Headline: "Delivery of hq held", Explanation: "Needs you: release it, or stop it.",
|
|
Who: asks.Operator, Expires: now.Add(time.Hour), OnExpiry: "the delivery stays held",
|
|
Options: []asks.Option{{ID: "release", Label: "Release", Does: "it goes on", Level: asks.Approve, Binds: binds}}}
|
|
}
|
|
|
|
func warrant() asks.Warrant {
|
|
return asks.Warrant{Ask: "d-1", Asker: "mesh-delivery", Outcome: asks.OutcomeChosen, Option: "release",
|
|
Label: "Release", Level: asks.Approve, At: now.Add(time.Minute), AskDigest: held().Digest(),
|
|
By: &asks.Person{Who: asks.Operator, Kind: "telegram", Identity: "42", Verified: "user id verified"}}
|
|
}
|
|
|
|
func TestAnAskIsKeptThenPublishedUnderTheModulesOwnName(t *testing.T) {
|
|
c, sent := client()
|
|
if err := c.Ask(held()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := sent[asks.AskSubject("mesh-delivery")]; len(got) != 1 || got[0] != "d-1" {
|
|
t.Fatalf("%v", sent)
|
|
}
|
|
if err := c.Ask(held()); err == nil || !strings.Contains(err.Error(), "already made") {
|
|
t.Errorf("the same id was asked twice: %v", err)
|
|
}
|
|
bad := held()
|
|
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(held())
|
|
acted := 0
|
|
act := func(o asks.Option, _ asks.Warrant) error {
|
|
if err := o.Performs(releaseHQ); err != nil {
|
|
t.Errorf("the bound act: %v", err)
|
|
}
|
|
acted++
|
|
return nil
|
|
}
|
|
if h, err := c.Take(warrant(), act); h != Acted || err != nil || acted != 1 {
|
|
t.Fatalf("%s %v %d", h, err, acted)
|
|
}
|
|
if h, _ := c.Take(warrant(), act); h != Done || acted != 1 {
|
|
t.Errorf("a redelivery was acted on: %s", h)
|
|
}
|
|
for why, change := range map[string]func(*asks.Warrant){
|
|
"another asker": func(w *asks.Warrant) { w.Asker = "mesh-controller" },
|
|
"an ask not made": func(w *asks.Warrant) { w.Ask = "d-9" },
|
|
"another level": func(w *asks.Warrant) { w.Level = asks.Acknowledge },
|
|
"an option not offered": func(w *asks.Warrant) { w.Option = "delete" },
|
|
"after its expiry": func(w *asks.Warrant) { w.At = now.Add(2 * time.Hour) },
|
|
"another ask's digest": func(w *asks.Warrant) { w.AskDigest = "sha256:00" },
|
|
} {
|
|
c2, _ := client()
|
|
_ = c2.Ask(held())
|
|
w := warrant()
|
|
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(held())
|
|
w := warrant()
|
|
w.Outcome, w.Option, w.By = asks.OutcomeExpired, "", nil
|
|
if h, err := c.Take(w, func(asks.Option, asks.Warrant) error { t.Fatal("acted"); return nil }); h != Recorded || err != nil {
|
|
t.Fatalf("%s %v", h, err)
|
|
}
|
|
if h, _ := c.Take(warrant(), func(asks.Option, asks.Warrant) error { t.Fatal("acted after it ended"); return nil }); h != Done {
|
|
t.Errorf("%s", h)
|
|
}
|
|
}
|