package main import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "sync" "testing" "time" "git.novox.be/novox/mesh-sdk/go/asks" ) // The router of novox/hq ADR 0259: it holds every ask, judges each answer and issues a warrant; it // performs nothing. These tests hold it to every rule of the record's §4, §5 and §7. // memAsks is the router's state in memory, with the compare-and-set the bus gives. type memAsks struct { mu sync.Mutex asked map[string][]time.Time asks map[string]AskRecord revs map[string]uint64 ids map[string]Identity links map[string]PendingLink unlinked map[string]Unlinked rev uint64 } func newMemAsks() *memAsks { return &memAsks{asks: map[string]AskRecord{}, revs: map[string]uint64{}, ids: map[string]Identity{}, links: map[string]PendingLink{}, unlinked: map[string]Unlinked{}, asked: map[string][]time.Time{}} } func clone(r AskRecord) AskRecord { raw, _ := json.Marshal(r) var out AskRecord _ = json.Unmarshal(raw, &out) return out } func (m *memAsks) GetAsk(k string) (*AskRecord, uint64, error) { m.mu.Lock() defer m.mu.Unlock() r, ok := m.asks[k] if !ok { return nil, 0, nil } c := clone(r) return &c, m.revs[k], nil } func (m *memAsks) CreateAsk(k string, r AskRecord) (uint64, error) { m.mu.Lock() defer m.mu.Unlock() if _, ok := m.asks[k]; ok { return 0, ErrChanged } m.rev++ m.asks[k], m.revs[k] = clone(r), m.rev return m.rev, nil } func (m *memAsks) UpdateAsk(k string, r AskRecord, rev uint64) (uint64, error) { m.mu.Lock() defer m.mu.Unlock() if m.revs[k] != rev { return 0, ErrChanged } m.rev++ m.asks[k], m.revs[k] = clone(r), m.rev return m.rev, nil } func (m *memAsks) AskKeys() ([]string, error) { m.mu.Lock() defer m.mu.Unlock() var out []string for k := range m.asks { out = append(out, k) } return out, nil } func (m *memAsks) GetIdentity(k string) (*Identity, error) { m.mu.Lock() defer m.mu.Unlock() id, ok := m.ids[k] if !ok { return nil, nil } return &id, nil } func (m *memAsks) PutIdentity(k string, id Identity) error { m.mu.Lock() defer m.mu.Unlock() m.ids[k] = id return nil } func (m *memAsks) DeleteIdentity(k string) error { m.mu.Lock() defer m.mu.Unlock() delete(m.ids, k) return nil } func (m *memAsks) IdentityKeys() ([]string, error) { m.mu.Lock() defer m.mu.Unlock() var out []string for k := range m.ids { out = append(out, k) } return out, nil } func (m *memAsks) GetLink(kind string) (*PendingLink, error) { m.mu.Lock() defer m.mu.Unlock() l, ok := m.links[kind] if !ok { return nil, nil } return &l, nil } func (m *memAsks) PutLink(kind string, l PendingLink) error { m.mu.Lock() defer m.mu.Unlock() m.links[kind] = l return nil } func (m *memAsks) DeleteLink(kind string) error { m.mu.Lock() defer m.mu.Unlock() delete(m.links, kind) return nil } func (m *memAsks) GetUnlinked(kind string) (*Unlinked, error) { m.mu.Lock() defer m.mu.Unlock() u, ok := m.unlinked[kind] if !ok { return nil, nil } return &u, nil } func (m *memAsks) PutUnlinked(kind string, u Unlinked) error { m.mu.Lock() defer m.mu.Unlock() m.unlinked[kind] = u return nil } func (m *memAsks) GetLinksAsked(key string) ([]time.Time, error) { m.mu.Lock() defer m.mu.Unlock() return append([]time.Time(nil), m.asked[key]...), nil } func (m *memAsks) PutLinksAsked(key string, times []time.Time) error { m.mu.Lock() defer m.mu.Unlock() m.asked[key] = append([]time.Time(nil), times...) return nil } // rootFreeOf is each test router's machines said not root-free, by why. var rootFreeOf = map[*Holder]map[string]string{} // told is what the router published to askers, by subject. type told struct{ by map[string][]asks.Warrant } func (w *told) of(asker string) []asks.Warrant { return w.by[asks.DecidedSubject(asker)] } // newRouter is a router with two kinds: telegram, which reaches the operator away and verifies its sender, // and the desk, which does neither — as the controller would issue them from the claims. func newRouter(t *testing.T) (*Holder, *fakeChannel, *fakeChannel, *clock, *memAsks, *told) { t.Helper() h, tg, dt, c, _ := newHolder(t) store := newMemAsks() w := &told{by: map[string][]asks.Warrant{}} bench := NewBench(c.now) bench.SetKinds([]KindInfo{ {Kind: "telegram", Module: "telegram", Node: "anchor", Capabilities: []string{"deliver", "reaches-away", "choice", "edit", "verified-sender", "exact-render"}}, {Kind: "desktop", Module: "desk-channel", Node: "anchor", Capabilities: []string{"deliver", "choice", "edit", "exact-render", "private"}}, }) bench.To = func(kind string) string { if ids := h.identitiesOf(kind); len(ids) > 0 { return ids[0].Identity } return "" } bench.Stand("telegram", Standing{Ready: true, At: c.now()}) bench.Stand("desktop", Standing{Ready: true, At: c.now()}) h.Bench, h.Asks = bench, store h.Node = "anchor" // The controller's word on root, as its verb root-free answers it: free unless a test says otherwise. notFree := map[string]string{} rootFreeOf[h] = notFree h.RootFree = func(machines []string) (map[string]RootVerdict, error) { out := map[string]RootVerdict{} for _, m := range machines { why, no := notFree[m] out[m] = RootVerdict{Machine: m, Free: !no, Why: why} } return out, nil } h.Zone = func() *time.Location { return time.UTC } h.Publish = func(subject string, body any, id string) error { if wt, ok := body.(asks.Warrant); ok { w.by[subject] = append(w.by[subject], wt) } return nil } return h, tg, dt, c, store, w } // stand keeps the channels' word fresh as the clock moves. func stand(h *Holder, c *clock) { // The controller's word on the kinds, read again as the router does every half minute. h.Bench.SetKinds(h.Bench.Kinds()) h.Bench.Stand("telegram", Standing{Ready: true, At: c.now()}) h.Bench.Stand("desktop", Standing{Ready: true, At: c.now()}) } // bound is what an option of the held delivery's ask binds: the asker's act, digested. func bound(verb string) string { d, err := asks.ActDigest(asks.Act{"verb": "mesh-delivery." + verb, "id": "novox/hq@0555"}) if err != nil { panic(err) } return d } func heldAsk(c *clock, id string) asks.Ask { return asks.Ask{ID: id, Headline: "Delivery of hq held for 36 hours", Explanation: "Needs you: release it, or stop it. A delivery of hq has been held for 36 hours.", Who: asks.Operator, Expires: c.now().Add(2 * time.Hour), OnExpiry: "the delivery stays held", Options: []asks.Option{ {ID: "release", Label: "Release", Does: "the delivery goes on", Level: asks.Approve, Binds: bound("release")}, {ID: "stop", Label: "Stop", Does: "the delivery ends", Level: asks.Approve, Binds: bound("stop")}, {ID: "silence", Label: "Silence for a week", Does: "nothing more is said of it for a week", Level: asks.Acknowledge}, }} } func submit(t *testing.T, h *Holder, asker string, a asks.Ask) { t.Helper() raw, _ := json.Marshal(a) if err := h.TakeAsk("ask", asker, raw); err != nil { t.Fatal(err) } } func link(h *Holder, store *memAsks, c *clock, identity string, at time.Time) { store.ids[identityKey("telegram", identity)] = Identity{Kind: "telegram", Identity: identity, Display: "Jo", Linked: at, ApproveFrom: at.Add(LinkedApproveAfter)} } func choiceOf(ticket, identity string, at time.Time) asks.Choice { return asks.Choice{ID: "c-" + ticket + "-" + identity, Ticket: ticket, At: at, Sender: asks.Sender{Identity: identity, Display: "Jo", Authenticated: true}} } func ticketFor(m Message, label string) string { for _, a := range m.Actions { if a.Label == label { return a.Token } } return "" } func labelsOf(m Message) string { var out []string for _, a := range m.Actions { out = append(out, a.Label) } return strings.Join(out, ", ") } // ackAsk is an ask of acknowledgements alone: silencing the held delivery. func ackAsk(c *clock, id string) asks.Ask { a := heldAsk(c, id) a.Options = a.Options[2:] a.Explanation = "Needs you: silence it, or leave it. A delivery of hq has been held for 36 hours." return a } // An ask that offers an approval is answered only where it could be approved, whichever option is chosen (the // review of 2026-10-09): the desk, which proves nothing of who clicked, is offered none of it. An ask of // acknowledgements alone is offered on every kind that carries a choice. func TestAnAskIsOfferedOnEachKindAsFarAsItCanCarryIt(t *testing.T) { h, tg, dt, c, store, _ := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) a := heldAsk(c, "d-1") a.Urgent = true submit(t, h, "mesh-delivery", a) settle(h, c) if len(tg.sends) != 1 || len(dt.sends) != 0 { t.Fatalf("telegram %d, desk %d", len(tg.sends), len(dt.sends)) } if got := labelsOf(tg.sends[0]); got != "Release, Stop, Silence for a week" { t.Errorf("telegram offers %q", got) } if rec := store.asks["mesh-delivery.d-1"]; rec.State != StateOpen || len(rec.Tickets) != 3 { t.Errorf("recorded %+v", rec) } b := ackAsk(c, "d-2") b.Urgent = true b.About = "other" submit(t, h, "mesh-delivery", b) settle(h, c) if len(dt.sends) != 1 || labelsOf(dt.sends[0]) != "Silence for a week" { t.Fatalf("the desk was not offered an acknowledgement: %+v", dt.sends) } } func TestWithoutALinkedAccountOnlyTheMeshMCPServerApproves(t *testing.T) { h, tg, dt, c, _, w := newRouter(t) submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) settle(h, c) // Telegram says only to the account linked as the operator: none linked, it is not used at all. if len(tg.sends) != 0 { t.Errorf("an unlinked Telegram was sent %d messages", len(tg.sends)) } if err := h.Bench.Ready("telegram"); err == nil || !strings.Contains(err.Error(), "send /start to the bot") { t.Errorf("an unlinked Telegram is ready: %v", err) } // The ask offers an approval no channel can carry: refused to its asker, in words. if got := w.of("mesh-delivery"); len(got) != 1 || got[0].Outcome != asks.OutcomeRefused || !strings.Contains(got[0].Words, "no channel can carry") { t.Fatalf("not refused: %+v", got) } if len(dt.sends) != 0 { t.Errorf("the desk was offered an ask with an approval: %+v", dt.sends) } } func TestAnApproveFromTheLinkedAccountIsAWarrantForTheAskerAlone(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) asked := heldAsk(c, "d-1") submit(t, h, "mesh-delivery", asked) settle(h, c) release := ticketFor(tg.sends[0], "Release") out, err := h.Chosen("telegram", choiceOf(release, "42", c.now())) if err != nil || out != "chosen" { t.Fatalf("%s %v", out, err) } got := w.of("mesh-delivery") if len(got) != 1 || got[0].Outcome != asks.OutcomeChosen || got[0].Option != "release" || got[0].Channel != "telegram" || got[0].Says() != "the operator, via telegram (user id verified), chose Release" || strings.Join(got[0].Proofs, ",") != "P1" { t.Fatalf("the warrant: %+v", got) } if _, err := got[0].For("mesh-delivery", asked); err != nil { t.Errorf("the asker would not act on it: %v", err) } // It answers the ask as it was taken, and no other ask under that id (ADR 0259 §6). if got[0].AskDigest != asked.Digest() { t.Errorf("the warrant names the digest %s, not the ask's %s", got[0].AskDigest, asked.Digest()) } if _, err := got[0].For("mesh-delivery", heldAsk(c, "d-1")); err == nil { t.Error("a warrant was taken for an ask under the same id with another expiry") } if rec := store.asks["mesh-delivery.d-1"]; rec.State != string(asks.OutcomeChosen) || !rec.Told || !rec.Tickets[release].Used { t.Errorf("the record: %+v", rec) } // Every copy says how it ended, its buttons gone. for _, ch := range []*fakeChannel{tg} { var edited Message for _, m := range ch.edits { edited = m } // The question's title is how it ended, read at a glance; then what the router knows of the asker (issue 372). if len(edited.Actions) != 0 || edited.Title != "You chose Release at 12:00 on Telegram · Delivery of hq held for 36 hours" || edited.Body != "" || edited.Note != "Told to mesh-delivery, which acts on it itself." { t.Errorf("%s still offers %q, or does not say how it ended in one line (issue 639): %q / %q / %q", ch.name, labelsOf(edited), edited.Title, edited.Body, edited.Note) } } // The tap is confirmed where it was made, in reply to it, once the warrant stood — and before the question // was edited, so the confirmation is what the operator sees first. var confirmed []Message for _, m := range tg.sends { if m.Reply == choiceOf(release, "42", c.now()).ID { confirmed = append(confirmed, m) } } if len(confirmed) != 1 || confirmed[0].Title != "You chose Release: recorded, and mesh-delivery was told." || confirmed[0].Body != "" || !confirmed[0].Quiet { t.Errorf("the tap was confirmed with %+v", confirmed) } // One warrant: the same answer again, and another answer after it, are refused and said. if out, _ := h.Chosen("telegram", choiceOf(release, "42", c.now())); out != "chosen" { t.Errorf("a redelivery of the same answer was acted on again: %s", out) } // The same answer from the same account, as a new tap, is what was recorded: said so, nothing more done // (issue 372). From another account it is refused. again := choiceOf(release, "42", c.now()) again.ID = "another" if out, err := h.Chosen("telegram", again); err != nil || out != "chosen" || !strings.HasPrefix(tg.sends[len(tg.sends)-1].Title, "You already chose Release at ") { t.Errorf("the same answer again was not said as recorded: %s %v %q", out, err, tg.sends[len(tg.sends)-1].Title) } stranger := choiceOf(release, "666", c.now()) if out, err := h.Chosen("telegram", stranger); err == nil || out != "refused" { t.Errorf("a used ticket was taken from another account: %s", out) } stop := ticketFor(tg.sends[0], "Stop") if out, err := h.Chosen("telegram", choiceOf(stop, "42", c.now())); err == nil || !strings.Contains(err.Error(), "already answered") { t.Errorf("a second answer stood: %s %v", out, err) } if len(w.of("mesh-delivery")) != 1 { t.Errorf("more than one warrant: %+v", w.of("mesh-delivery")) } if len(w.of("mesh-controller")) != 0 { t.Error("another asker heard the warrant") } } func TestEveryAnswerThatIsNotTheOperatorsIsRefusedAndSaid(t *testing.T) { for name, tc := range map[string]struct { kind, identity string authenticated bool linkedAgo time.Duration label string pass time.Duration want string }{ "an account not linked": {"telegram", "666", true, 2 * time.Hour, "Release", 0, "not linked as the operator"}, "a sender the service did not say": {"telegram", "42", false, 2 * time.Hour, "Release", 0, "did not authenticate"}, "a link younger than an hour": {"telegram", "42", true, 59 * time.Minute, "Release", 0, "can answer this question from"}, "an ask that expired": {"telegram", "42", true, 2 * time.Hour, "Release", 3 * time.Hour, "expired"}, "a ticket shown on another kind": {"desktop", "g14", true, 2 * time.Hour, "Release", 0, "not offered here"}, } { t.Run(name, func(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now().Add(-tc.linkedAgo)) submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) settle(h, c) ticket := ticketFor(tg.sends[0], tc.label) if ticket == "" { t.Fatalf("no %s offered: %q", tc.label, labelsOf(tg.sends[0])) } c.pass(tc.pass) ch := choiceOf(ticket, tc.identity, c.now()) ch.Sender.Authenticated = tc.authenticated before := len(tg.sends) out, err := h.Chosen(tc.kind, ch) if err == nil || out != "refused" || !strings.Contains(err.Error(), tc.want) { t.Fatalf("%s %v, want refused for %q", out, err, tc.want) } if len(w.of("mesh-delivery")) != 0 { t.Error("a warrant was issued") } title := "That answer was not taken" if tc.identity == "666" { // Another account's answer is said to the operator, naming that account (the lab's proof found // the plain refusal reached the operator and read as their own answer refused). title = "Another account tried to answer for you" } if tc.kind == "telegram" && (len(tg.sends) != before+1 || tg.sends[len(tg.sends)-1].Title != title) { t.Errorf("the refusal was not said on the channel: %+v", tg.sends[before:]) } if tc.identity == "666" && !strings.Contains(tg.sends[len(tg.sends)-1].Body, "number 666") { t.Errorf("the notice does not name the other account: %q", tg.sends[len(tg.sends)-1].Body) } }) } } func TestALinkApprovesAfterAnHourAndAcknowledgesAtOnce(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now()) submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) settle(h, c) release, silence := ticketFor(tg.sends[0], "Release"), ticketFor(tg.sends[0], "Silence for a week") c.pass(59*time.Minute - BurstWindow) if out, err := h.Chosen("telegram", choiceOf(release, "42", c.now())); err == nil || out != "refused" { t.Fatalf("approved at 59 minutes: %s", out) } c.pass(time.Minute) stand(h, c) late := choiceOf(release, "42", c.now().Add(time.Second)) late.ID = "at-sixty" if out, err := h.Chosen("telegram", late); err != nil || out != "chosen" { t.Fatalf("refused at 60 minutes: %s %v", out, err) } // An acknowledgement is taken at once from a fresh link, on an ask of acknowledgements alone. h2, tg2, _, c2, store2, w2 := newRouter(t) link(h2, store2, c2, "42", c2.now()) submit(t, h2, "mesh-delivery", ackAsk(c2, "d-2")) settle(h2, c2) silence = ticketFor(tg2.sends[0], "Silence for a week") if out, err := h2.Chosen("telegram", choiceOf(silence, "42", c2.now())); err != nil || out != "chosen" { t.Fatalf("an acknowledgement from a fresh link was refused: %s %v", out, err) } if got := w2.of("mesh-delivery"); len(got) != 1 || got[0].Level != asks.Acknowledge { t.Errorf("%+v", got) } _ = w } // An acknowledgement does not end an ask that waits for an approval from where an approval is not proven // (the review of 2026-10-09): a fresh link's Silence on the held delivery's ask is refused, and the ask stays // open for Release or Stop once the hour has passed. func TestAnAcknowledgementDoesNotEndAnAskThatWaitsForAnApproval(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now()) submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) settle(h, c) silence := ticketFor(tg.sends[0], "Silence for a week") if out, err := h.Chosen("telegram", choiceOf(silence, "42", c.now())); err == nil || out != "refused" { t.Fatalf("a fresh link's acknowledgement ended an ask that offers an approval: %s %v", out, err) } if len(w.of("mesh-delivery")) != 0 || store.asks["mesh-delivery.d-1"].State != StateOpen { t.Fatal("the ask ended") } // Nor while the controller does not say root is free there. c.pass(time.Hour) stand(h, c) rootFreeOf[h]["anchor"] = "not measured" again := choiceOf(silence, "42", c.now()) again.ID = "again" if out, _ := h.Chosen("telegram", again); out != "refused" || store.asks["mesh-delivery.d-1"].State != StateOpen { t.Fatalf("an acknowledgement ended the ask while root is not free: %s", out) } delete(rootFreeOf[h], "anchor") release := choiceOf(ticketFor(tg.sends[0], "Release"), "42", c.now()) if out, err := h.Chosen("telegram", release); err != nil || out != "chosen" { t.Fatalf("the approval after the hour: %s %v", out, err) } } func TestTheDeskAcknowledgesAndNeverApproves(t *testing.T) { h, _, dt, c, _, w := newRouter(t) submit(t, h, "mesh-delivery", ackAsk(c, "d-1")) settle(h, c) silence := ticketFor(dt.sends[0], "Silence for a week") ch := choiceOf(silence, "g14", c.now()) ch.Sender.Authenticated = false if out, err := h.Chosen("desktop", ch); err != nil || out != "chosen" { t.Fatalf("%s %v", out, err) } got := w.of("mesh-delivery") if len(got) != 1 || got[0].By.Verified != "a desk click: whoever was at the operator's session on g14" || len(got[0].Proofs) != 0 { t.Errorf("%+v", got) } // An approve ticket forged onto the desk is refused: the kind cannot carry it. h2, tg2, _, c2, store2, _ := newRouter(t) link(h2, store2, c2, "42", c2.now().Add(-2*time.Hour)) submit(t, h2, "mesh-delivery", heldAsk(c2, "d-1")) settle(h2, c2) rec := store2.asks["mesh-delivery.d-1"] release := ticketFor(tg2.sends[0], "Release") tk := rec.Tickets[release] tk.Kind = "desktop" rec.Tickets[release] = tk store2.asks["mesh-delivery.d-1"] = rec h2.keepAsk("mesh-delivery.d-1", rec, store2.revs["mesh-delivery.d-1"]) if out, err := h2.Chosen("desktop", choiceOf(release, "g14", c2.now())); err == nil || out != "refused" || !strings.Contains(err.Error(), "cannot answer a question that asks for an approval") { t.Errorf("the desk approved: %s %v", out, err) } // Nor does an acknowledgement forged onto the desk end that ask. silence = ticketFor(tg2.sends[0], "Silence for a week") tk = rec.Tickets[silence] tk.Kind = "desktop" rec.Tickets[silence] = tk store2.asks["mesh-delivery.d-1"] = rec h2.keepAsk("mesh-delivery.d-1", rec, store2.revs["mesh-delivery.d-1"]) if out, _ := h2.Chosen("desktop", choiceOf(silence, "g14", c2.now())); out != "refused" || store2.asks["mesh-delivery.d-1"].State != StateOpen { t.Errorf("a desk click ended an ask that waits for an approval: %s", out) } } func TestAnAskIsRefusedInWordsToItsAsker(t *testing.T) { h, _, _, c, store, w := newRouter(t) bad := heldAsk(c, "d-1") bad.Options[0].Level = asks.Destroy submit(t, h, "mesh-delivery", bad) carrying := heldAsk(c, "d-2") carrying.Explanation = "Needs you: see /etc/passwd on the machine." submit(t, h, "mesh-delivery", carrying) for i := 3; i <= 6; i++ { a := ackAsk(c, "d-"+string(rune('0'+i))) a.About = "about-" + string(rune('0'+i)) submit(t, h, "mesh-delivery", a) } got := w.of("mesh-delivery") var refused []string for _, g := range got { if g.Outcome == asks.OutcomeRefused { refused = append(refused, g.Ask+": "+g.Words) } } all := strings.Join(refused, "\n") for _, want := range []string{"d-1: the ask is refused: an ask that destroys lasts at most", "d-2: its words carry", "d-6: its asker already has 3 open asks"} { if !strings.Contains(all, want) { t.Errorf("not refused: %q in\n%s", want, all) } } if _, kept := store.asks["mesh-delivery.d-1"]; kept { t.Error("a refused ask was recorded") } // No channel can carry anything: refused, never queued in silence. h2, _, _, c2, _, w2 := newRouter(t) h2.Bench.Stand("telegram", Standing{Ready: false, Why: "no bot token", At: c2.now()}) h2.Bench.Stand("desktop", Standing{Ready: false, Why: "no session", At: c2.now()}) submit(t, h2, "mesh-delivery", heldAsk(c2, "d-1")) if got := w2.of("mesh-delivery"); len(got) != 1 || !strings.Contains(got[0].Words, "no channel can carry any of its answers now") { t.Errorf("%+v", got) } } func TestAnAskExpiresAndNeverDefaults(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) settle(h, c) c.pass(2 * time.Hour) stand(h, c) h.Tick() got := w.of("mesh-delivery") if len(got) != 1 || got[0].Outcome != asks.OutcomeExpired || got[0].Option != "" || !strings.Contains(got[0].Words, "the delivery stays held") { t.Fatalf("%+v", got) } var edited Message for _, m := range tg.edits { edited = m } if len(edited.Actions) != 0 || !strings.HasPrefix(edited.Title, "No longer answerable: nobody answered in time · Delivery of hq") || edited.Body != "" || edited.Note != "The delivery stays held." { t.Errorf("the copy still offers answers, or does not say how it ended: %+v", edited) } } func TestANewerAskAboutTheSameThingReplacesTheOlderAndACancelEndsIt(t *testing.T) { h, _, _, c, store, w := newRouter(t) a := ackAsk(c, "d-1") a.About = "delivery.hq" submit(t, h, "mesh-delivery", a) b := ackAsk(c, "d-2") b.About = "delivery.hq" submit(t, h, "mesh-delivery", b) if store.asks["mesh-delivery.d-1"].State != string(asks.OutcomeReplaced) { t.Errorf("the older ask was not replaced: %s", store.asks["mesh-delivery.d-1"].State) } if err := h.TakeAsk("cancel", "mesh-delivery", json.RawMessage(`{"id":"d-2"}`)); err != nil { t.Fatal(err) } got := w.of("mesh-delivery") if len(got) != 2 || got[0].Outcome != asks.OutcomeReplaced || got[1].Outcome != asks.OutcomeCancelled { t.Errorf("%+v", got) } // Another asker cannot cancel it: its cancel names its own ask, under its own name. submit(t, h, "mesh-delivery", ackAsk(c, "d-3")) _ = h.TakeAsk("cancel", "mesh-controller", json.RawMessage(`{"id":"d-3"}`)) if store.asks["mesh-delivery.d-3"].State != StateOpen { t.Error("another asker's cancel ended it") } } func TestAnAskAboutAConditionIsAnsweredOnItsMessage(t *testing.T) { h, tg, dt, c, store, w := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) k := cond("delivery.hq.held", Warning, "the delivery of hq is held") k.Raised = c.now() h.Condition(EventRaised, k) settle(h, c) a := ackAsk(c, "d-1") a.About = "delivery.hq.held" submit(t, h, "mesh-controller", a) settle(h, c) // Its first message was on the desk alone (a warning, the desk ready); the ask re-shows it with answers. var shown Message for _, m := range dt.edits { shown = m } if got := labelsOf(shown); got != "Silence for a week, Details" { t.Errorf("the desk's copy offers %q: %+v", got, shown) } if store.asks["mesh-controller.d-1"].Record != "delivery.hq.held" { t.Errorf("the ask is shown on %q", store.asks["mesh-controller.d-1"].Record) } silence := ticketFor(shown, "Silence for a week") if out, err := h.Chosen("desktop", choiceOf(silence, "g14", c.now())); err != nil || out != "chosen" { t.Fatalf("%s %v", out, err) } if got := w.of("mesh-controller"); len(got) != 1 || got[0].About != "delivery.hq.held" { t.Errorf("%+v", got) } _ = tg } func TestAnAskerNotToldIsToldAgain(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) settle(h, c) pub := h.Publish h.Publish = func(string, any, string) error { return errorf("the bus is away") } if out, _ := h.Chosen("telegram", choiceOf(ticketFor(tg.sends[0], "Stop"), "42", c.now())); out != "chosen" { t.Fatal(out) } if store.asks["mesh-delivery.d-1"].Told { t.Fatal("told while the bus was away") } h.Publish = pub c.pass(time.Minute) stand(h, c) h.Tick() if got := w.of("mesh-delivery"); len(got) != 1 || got[0].Option != "stop" || !store.asks["mesh-delivery.d-1"].Told { t.Errorf("not told again: %+v", got) } } func TestAnAnswerReplayedLongAfterIsNotActedOn(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) settle(h, c) old := choiceOf(ticketFor(tg.sends[0], "Release"), "42", c.now().Add(-time.Hour)) if out, _ := h.Chosen("telegram", old); out != "too old" || len(w.of("mesh-delivery")) != 0 { t.Errorf("%s", out) } } func TestLinkingShowsACodeOnTheDeskAndLinksOnlyWhoAsked(t *testing.T) { h, tg, dt, c, store, _ := newRouter(t) asked := asks.LinkAsked{ID: "l1", At: c.now(), Sender: asks.Sender{Identity: "42", Display: "Jo", Authenticated: true}} h.Link("telegram", asked) if len(dt.sends) != 1 || !strings.HasPrefix(dt.sends[0].Title, "Link a Telegram account as you") { t.Fatalf("no code on the desk: %+v", dt.sends) } code := strings.ReplaceAll(strings.TrimSuffix(strings.SplitAfter(dt.sends[0].Body, "before ")[1][7:14], "."), " ", "") if last := tg.sends[len(tg.sends)-1]; last.Title != "Send the code shown on your desk" { t.Errorf("telegram was told %q", last.Title) } if p := store.links["telegram"]; p.CodeHash == "" || strings.Contains(p.CodeHash, code) { t.Errorf("the code is kept in clear: %+v", p) } // Another account sending the code is refused; a wrong code is refused; the right one links. if a := h.Prove("telegram", asks.Code{Sender: asks.Sender{Identity: "666", Authenticated: true}, Code: code}); a.Accepted { t.Error("another account linked with the code") } if a := h.Prove("telegram", asks.Code{Sender: asks.Sender{Identity: "42", Authenticated: true}, Code: "000000x"}); a.Accepted { t.Error("a wrong code linked") } a := h.Prove("telegram", asks.Code{Sender: asks.Sender{Identity: "42", Authenticated: true}, Code: code, Purpose: "link"}) if !a.Accepted || !strings.Contains(a.Words, "approve from 13:00") { t.Fatalf("the right code: %+v (code %q)", a, code) } id := store.ids[identityKey("telegram", "42")] if id.ApproveFrom.Sub(id.Linked) != time.Hour { t.Errorf("approves from %s, linked %s", id.ApproveFrom, id.Linked) } // Said on every channel. for _, ch := range []*fakeChannel{tg, dt} { if last := ch.sends[len(ch.sends)-1]; !strings.Contains(last.Title, "is now linked as you") { t.Errorf("%s was not told: %q", ch.name, last.Title) } } // A second account cannot link while one is. h.Link("telegram", asks.LinkAsked{ID: "l2", At: c.now(), Sender: asks.Sender{Identity: "666", Authenticated: true}}) if _, pending := store.links["telegram"]; pending { t.Error("a second link was begun while one is linked") } } func TestALinkCodeExpiresAndFiveWrongTriesEndIt(t *testing.T) { h, _, _, c, store, _ := newRouter(t) jo := asks.Sender{Identity: "42", Authenticated: true} h.Link("telegram", asks.LinkAsked{ID: "l1", At: c.now(), Sender: jo}) for i := 0; i < LinkTries; i++ { h.Prove("telegram", asks.Code{Sender: jo, Code: "nope"}) } if _, pending := store.links["telegram"]; pending { t.Error("five wrong codes did not end the link") } h.Link("telegram", asks.LinkAsked{ID: "l2", At: c.now(), Sender: jo}) c.pass(LinkCodeFor) if a := h.Prove("telegram", asks.Code{Sender: jo, Code: "123456"}); a.Accepted || !strings.Contains(a.Words, "expired") { t.Errorf("%+v", a) } // The desk cannot be linked: it proves nothing about who clicked. h.Link("desktop", asks.LinkAsked{ID: "l3", At: c.now(), Sender: asks.Sender{Identity: "g14"}}) if _, pending := store.links["desktop"]; pending { t.Error("the desk began a link") } } // novox/hq ADR 0259 §7 (revised): unlinking is an approve ask put to the account that is linked; its notice // reaches that account first, by name; nobody else's answer unlinks it. func TestUnlinkingIsAskedOfTheLinkedAccountAndToldToItFirst(t *testing.T) { h, tg, dt, c, store, _ := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) got, err := h.Unlink("telegram") if err != nil || got["asked"] != true { t.Fatalf("%v %v", got, err) } if len(store.ids) != 1 { t.Fatal("unlinked before the account answered") } ask := tg.sends[len(tg.sends)-1] if ask.Title != "Urgent: Unlink this account?" || tg.to[len(tg.to)-1] != "42" || labelsOf(ask) != "Unlink" { t.Fatalf("the question: %q to %q offers %q", ask.Title, tg.to[len(tg.to)-1], labelsOf(ask)) } if last := dt.sends[len(dt.sends)-1]; !strings.Contains(last.Title, "An unlink of your Telegram account was asked") { t.Errorf("the desk was not told: %q", last.Title) } unlink := ticketFor(ask, "Unlink") // Another account cannot answer it. if out, _ := h.Chosen("telegram", choiceOf(unlink, "666", c.now())); out != "refused" || len(store.ids) != 1 { t.Fatalf("another account unlinked it: %s", out) } before := len(tg.sends) if out, err := h.Chosen("telegram", choiceOf(unlink, "42", c.now())); err != nil || out != "chosen" { t.Fatalf("%s %v", out, err) } if len(store.ids) != 0 { t.Fatal("the account's own approval did not unlink it") } // The tap is confirmed where it was made (issue 372), then the account is told, by name, before it goes. if confirmed := tg.sends[before]; confirmed.Title != "You chose Unlink: recorded." || confirmed.Reply == "" { t.Errorf("the tap was not confirmed in reply to it: %+v", confirmed) } notice := tg.sends[before+1] if notice.Title != "This account is no longer linked as you" || tg.to[before+1] != "42" { t.Errorf("the account was not told first, by name: %q to %q", notice.Title, tg.to[before+1]) } if last := dt.sends[len(dt.sends)-1]; !strings.Contains(last.Title, "No Telegram account is linked as you") { t.Errorf("the desk was not told: %q", last.Title) } } // A link within a day of an unlink waits for the replaced account's approval. func TestARelinkWithinADayWaitsForTheReplacedAccount(t *testing.T) { h, tg, dt, c, store, _ := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) _, _ = h.Unlink("telegram") _, _ = h.Chosen("telegram", choiceOf(ticketFor(tg.sends[len(tg.sends)-1], "Unlink"), "42", c.now())) h.Link("telegram", asks.LinkAsked{ID: "l9", At: c.now(), Sender: asks.Sender{Identity: "666", Display: "Eve", Authenticated: true}}) code := codeOf(t, dt.sends[len(dt.sends)-1]) a := h.Prove("telegram", asks.Code{Sender: asks.Sender{Identity: "666", Authenticated: true}, Code: code}) if !a.Accepted || !strings.Contains(a.Words, "waits for that account's approval") || len(store.ids) != 0 { t.Fatalf("linked at once within a day of an unlink: %+v %v", a, store.ids) } asked := tg.sends[len(tg.sends)-1] if asked.Title != "Urgent: Allow another account in your place?" || tg.to[len(tg.to)-1] != "42" { t.Fatalf("the replaced account was not asked: %q to %q", asked.Title, tg.to[len(tg.to)-1]) } if out, _ := h.Chosen("telegram", choiceOf(ticketFor(asked, "Allow"), "666", c.now())); out != "refused" { t.Error("the new account allowed itself") } if out, err := h.Chosen("telegram", choiceOf(ticketFor(asked, "Allow"), "42", c.now())); err != nil || out != "chosen" { t.Fatalf("%s %v", out, err) } if id, ok := store.ids[identityKey("telegram", "666")]; !ok || id.ApproveFrom.Sub(id.Linked) != time.Hour { t.Errorf("not linked on the replaced account's approval: %+v", store.ids) } } // Root at the router's machine unlinks, or allows a waiting link, by a file only root makes. It acts from the // router's minute (Tick), which holds the work: the review of 2026-10-09 found the minute took the work again // there and hung for ever the first time root used it. It is said on every channel, and the router's own open // questions end. func TestBreakGlassIsAFileRootMadeAndTheMinuteGoesOn(t *testing.T) { h, tg, dt, c, store, _ := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) h.StateDir = t.TempDir() tick := func() { t.Helper() done := make(chan struct{}) go func() { h.Tick(); close(done) }() select { case <-done: case <-time.After(5 * time.Second): t.Fatal("the router's minute hung on a break-glass file") } } // Who owns the file is what the kernel says, faked here both ways: the build seat runs this suite as root, where // every file the test makes is root's, and a person runs it as themselves (the forge's check of 2026-10-09 failed // on the first and passed on the second). was := fileOwner t.Cleanup(func() { fileOwner = was }) fileOwner = func(os.FileInfo) uint32 { return 1000 } if err := os.WriteFile(filepath.Join(h.StateDir, "unlink.telegram"), nil, 0o600); err != nil { t.Fatal(err) } tick() if len(store.ids) != 1 || !strings.Contains(dt.sends[len(dt.sends)-1].Title, "was refused") { t.Fatalf("a file not made by root unlinked: %v", store.ids) } // An open question of the router's own: root's act ends it. if _, err := h.Unlink("telegram"); err != nil { t.Fatal(err) } fileOwner = func(os.FileInfo) uint32 { return 0 } _ = os.WriteFile(filepath.Join(h.StateDir, "unlink.telegram"), nil, 0o600) tick() if len(store.ids) != 0 { t.Fatal("root's file did not unlink") } if _, err := os.Stat(filepath.Join(h.StateDir, "unlink.telegram")); err == nil { t.Error("the file was left to act again") } for _, ch := range []*fakeChannel{tg, dt} { said := false for _, m := range ch.sends { said = said || (m.Title == "Urgent: Root changed who is you" || strings.Contains(m.Title, "Root changed who is you")) && strings.Contains(m.Body, "was unlinked") && strings.Contains(m.Body, "1 open question") } if !said && ch == dt { t.Errorf("%s was not told what root did: %+v", ch.name, ch.sends[len(ch.sends)-1]) } } for k, rec := range store.asks { if rec.Asker == SelfAsker && rec.State == StateOpen { t.Errorf("the router's own question %s is still open after root decided", k) } } } // While an agent can become root where the router or the channel runs, nothing proven there approves. func TestNoApprovalWhileAnAgentCanBecomeRootThere(t *testing.T) { h, tg, _, c, store, w := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) asked := heldAsk(c, "d-1") submit(t, h, "mesh-delivery", asked) settle(h, c) release := ticketFor(tg.sends[0], "Release") rootFreeOf[h]["anchor"] = "the agent account is not judged" if out, err := h.Chosen("telegram", choiceOf(release, "42", c.now())); err == nil || out != "refused" || !strings.Contains(err.Error(), "may become root on anchor") { t.Fatalf("approved while an agent can become root there: %s %v", out, err) } if len(w.of("mesh-delivery")) != 0 { t.Error("a warrant was issued") } if got := h.carriers(asks.Approve); len(got) != 0 { t.Errorf("an approval is still offered on %v", got) } } // Another asker naming a condition's key does not take that condition's message over. func TestOnlyTheConditionsOwnerAsksOnItsMessage(t *testing.T) { h, _, _, c, store, _ := newRouter(t) k := cond("delivery.hq.held", Warning, "the delivery of hq is held") k.Raised = c.now() h.Condition(EventRaised, k) a := heldAsk(c, "d-1") a.About = "delivery.hq.held" submit(t, h, "mesh-delivery", a) if got := store.asks["mesh-delivery.d-1"].Record; got == "delivery.hq.held" { t.Error("another asker took the condition's message") } } // A channel that does not render exactly as the router does carries no answer. func TestAnAnswerNeedsAChannelThatRendersExactly(t *testing.T) { h, _, _, c, _, _ := newRouter(t) h.Bench.SetKinds([]KindInfo{{Kind: "desktop", Module: "desk-channel", Capabilities: []string{"deliver", "choice", "private"}}}) h.Bench.Stand("desktop", Standing{Ready: true, At: c.now()}) if got := h.carriers(asks.Acknowledge); len(got) != 0 { t.Errorf("a channel without exact-render carries %v", got) } } // At most three links an hour per kind, and the code is shown only where the words stay private. func TestLinksAreBoundedAndTheCodeShownOnlyInPrivate(t *testing.T) { h, tg, dt, c, store, _ := newRouter(t) jo := asks.Sender{Identity: "42", Display: "Jo", Authenticated: true} for i := 0; i < LinksAnHour; i++ { h.Link("telegram", asks.LinkAsked{ID: fmt.Sprint("l", i), At: c.now(), Sender: jo}) } shown := len(dt.sends) h.Link("telegram", asks.LinkAsked{ID: "l9", At: c.now(), Sender: jo}) if len(dt.sends) != shown || !strings.Contains(tg.sends[len(tg.sends)-1].Body, "Try again in an hour") { t.Error("a fourth link this hour showed a code") } if !strings.Contains(dt.sends[0].Body, "(number 42)") { t.Errorf("the notice does not name the account by its number: %q", dt.sends[0].Body) } h2, tg2, _, c2, store2, _ := newRouter(t) h2.Bench.SetKinds([]KindInfo{ {Kind: "telegram", Module: "telegram", Capabilities: []string{"choice", "verified-sender", "exact-render", "reaches-away"}}, {Kind: "desktop", Module: "desk-channel", Capabilities: []string{"choice", "exact-render"}}}) h2.Link("telegram", asks.LinkAsked{ID: "l1", At: c2.now(), Sender: jo}) if _, pending := store2.links["telegram"]; pending || !strings.Contains(tg2.sends[len(tg2.sends)-1].Body, "No private place") { t.Error("a code was shown where the words are not private") } _ = store } // A warning the desk could not show goes to the channel that reaches the operator away. func TestAWarningTheDeskCouldNotShowGoesAway(t *testing.T) { h, tg, dt, c, store, _ := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) k := cond("module.x.unhealthy", Warning, "x is not healthy") k.Raised = c.now() h.Condition(EventRaised, k) settle(h, c) if len(dt.sends) != 1 { t.Fatalf("the desk was sent %d", len(dt.sends)) } h.Failed("desktop", asks.Failed{Handle: "g14=1", Why: "no session answers", At: c.now()}) c.pass(BurstWindow) h.Flush() if len(tg.sends) == 0 { t.Error("a warning the desk could not show did not go to Telegram") } } // codeOf reads the six digits a link's notice shows. func codeOf(t *testing.T, m Message) string { t.Helper() i := strings.Index(m.Body, ": ") for i >= 0 { rest := m.Body[i+2:] if len(rest) >= 7 && rest[3] == ' ' { return rest[:3] + rest[4:7] } j := strings.Index(rest, ": ") if j < 0 { break } i += 2 + j } t.Fatalf("no code in %q", m.Body) return "" } // An ask's message goes alone even when it offers one answer: folded into a burst's digest, its one button was // lost (found by the lab's proof, mesh-lab asks/, with a condition's message in the same burst). func TestAnAsksMessageWithOneAnswerIsNotFoldedIntoADigest(t *testing.T) { h, tg, _, c, store, _ := newRouter(t) link(h, store, c, "42", c.now().Add(-2*time.Hour)) settle(h, c) // Something was just said on the channel, so what follows waits for the burst. submit(t, h, "mesh-delivery", heldAsk(c, "d-0")) settle(h, c) k := cond("machine.anchor.disk", Urgent, "the disk on anchor is full") k.Raised = c.now() h.Condition(EventRaised, k) before := len(tg.sends) one := heldAsk(c, "d-1") one.Options = one.Options[2:] // Silence alone: one answer submit(t, h, "mesh-delivery", one) settle(h, c) var asked *Message for i := range tg.sends[before:] { m := tg.sends[before+i] if ticketFor(m, "Silence for a week") != "" { asked = &m } } if asked == nil { t.Fatalf("the ask's one answer was not offered on Telegram: %+v", tg.sends[before:]) } if strings.Contains(asked.Body, "disk on anchor") { t.Errorf("the ask's message was folded with another: %q", asked.Body) } }