mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/asks-answered-on-any-channel checking: 3 of 4 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
131 lines
4.0 KiB
Go
131 lines
4.0 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
|
|
}
|
|
|
|
func held() asks.Ask {
|
|
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}}}
|
|
}
|
|
|
|
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),
|
|
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(asks.Option, asks.Warrant) error { 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) },
|
|
} {
|
|
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)
|
|
}
|
|
}
|