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