Files
mesh-delivery/modules/mesh-delivery-view/internal/view/holds_test.go
T
jschoubben 30ca8a22bc List holds and let the operator release or keep one again from mesh-delivery-cli (issue 1906)
A hold answered keep on the phone could not be changed: its ask ended with the
answer, nothing listed it, and a kept hold is never lifted by itself. The view
lists every hold with its node, cause and answer (mesh-delivery-cli holds) and
answers the standing hold ask with hold-release or hold-keep through its one
write; the teller takes a later view answer that changes a hold's option into
the answer of that asking, never an older one.
2026-10-25 15:09:40 +01:00

92 lines
3.6 KiB
Go

package view
import (
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// heldE is the assigner's hold of module at fp, as it writes it in its wants.
func heldE(t *testing.T, module, fp, node, words string) Entry {
t.Helper()
key, err := delivery.HeldKey(module, fp)
if err != nil {
t.Fatal(err)
}
v, err := delivery.EncodeHeld(delivery.Held{Node: node, Words: words})
if err != nil {
t.Fatal(err)
}
return Entry{Bucket: delivery.AssignerWantsBucket, Key: key, Value: v, At: t0}
}
func answered(t *testing.T, key, id, option string) Entry {
t.Helper()
v, err := delivery.EncodeAnswer(delivery.Answer{Option: option, Warrant: id + "@2026-10-25T04:00:00Z", Words: "the operator, via telegram, chose " + option})
if err != nil {
t.Fatal(err)
}
return Entry{Bucket: delivery.AnswersBucket, Key: key, Value: v, At: t0}
}
// 1906/N23-AC1: given holds in the assigner's wants, the view lists each with its node, its cause in words, its hold ask and
// the answer that stands for that asking; a hold without an ask says so, and a held key is never taken for a want.
func TestTheViewListsEveryHoldWithItsNodeCauseAndAnswer(t *testing.T) {
m := New()
e, a := holdAsk(t)
fill(m, e, heldE(t, "plex", fpNew, "ace", "health failed, phase running: connection refused"), answered(t, e.Key, a.ID, "keep"),
heldE(t, "web", fpNew, "g14", "health failed"))
got := m.Holds()
if len(got) != 2 {
t.Fatalf("holds = %+v", got)
}
p := got[0]
if p.Module != "plex" || p.FP != fpNew || p.Node != "ace" || !strings.Contains(p.Words, "connection refused") || p.AskKey != e.Key || p.Asking != a.ID || p.Answer != "keep" {
t.Errorf("plex = %+v", p)
}
if w := got[1]; w.Module != "web" || w.Node != "g14" || w.AskKey != "" || w.Answer != "" {
t.Errorf("web = %+v", w)
}
if len(m.Snapshot().Wants) != 0 {
t.Errorf("a hold was taken for a want: %+v", m.Snapshot().Wants)
}
}
// 1906/N23-AC2: given a hold ask answered "keep" (the ask ended with it), when the operator releases the hold, then the view
// writes its answer "release" to that asking under the ask's view key, though the ask was answered; the fingerprint may be the
// start of its hex. The same answer again, an unknown hold and another option are refused, and write nothing.
func TestAKeptHoldCanBeReleased(t *testing.T) {
m, w := New(), &memWriter{}
m.SetWriter(w)
e, a := holdAsk(t)
fill(m, e, heldE(t, "plex", fpNew, "ace", "health failed"), answered(t, e.Key, a.ID, "keep"))
short := strings.TrimPrefix(fpNew, delivery.SourcePrefix2)[:8]
if _, err := m.AnswerHold("plex", short, HoldRelease, "it was a flake"); err != nil {
t.Fatal(err)
}
va, err := delivery.DecodeViewAnswer(w.puts[e.Key+".view"])
if err != nil || va.Asking != a.ID || va.Option != HoldRelease || va.Words != "it was a flake" {
t.Fatalf("the view's answer = %+v, %v", va, err)
}
w.puts = nil
fill(m, answered(t, e.Key, a.ID, HoldRelease))
for name, try := range map[string]func() error{
"again": func() error { _, err := m.AnswerHold("plex", fpNew, HoldRelease, ""); return err },
"unknown": func() error { _, err := m.AnswerHold("plex", "ffff", HoldKeep, ""); return err },
"option": func() error { _, err := m.AnswerHold("plex", fpNew, "restore", ""); return err },
"module": func() error { _, err := m.AnswerHold("web", fpNew, HoldKeep, ""); return err },
} {
if err := try(); err == nil {
t.Errorf("%s: not refused", name)
}
}
if len(w.puts) != 0 {
t.Errorf("a refused answer wrote %v", w.puts)
}
// And kept again: the operator may change their mind both ways.
if _, err := m.AnswerHold("plex", fpNew, HoldKeep, ""); err != nil {
t.Errorf("keep after release: %v", err)
}
}