The operator would not read the Telegram ask for ADR 0343: thirty plain lines, every clause on one, no bold, no summary. A settings proposal was the same wall of text, its values masked so the approval was blind. - messenger: an ask's message is the asker's headline and explanation alone; who asked and when, what each answer does, the expiry, where else it is answered and the asker's Details travel as the message's note (asks.Message.Note). Answered, the message is one line, "You chose Approve at 16:06 on Telegram · <headline>". An explanation that does not read at a glance (asks.Glance) is shown and logged. On a kind that proves who answers, an ask's values are shown whole (asks.Ask.Whole), held to the secret rule alone (from the earlier work on issue 383, #178). - telegram: every message in HTML, never reworded: the title bold, a key line monospace, the note apart in a collapsed blockquote; markup escaped. A tap is compared by its words, whitespace aside, and a message shown before is still compared by the digest it was kept under. - desk-channel: the note follows the body. - mesh-knowledge: a record that needs the operator carries a brief (headline, summary, at most three points), the 30th per-record rule; its ask shows the brief and never the clauses. An ask made before is composed again as it was asked (form), so its warrant still holds. - claude-code: an instructions proposal's explanation is one summary line (the proposer's, or the module's), its text in the Details; a question's context is at most 300 characters and its recommendation one point. Older proposals and questions keep their form. The SDK is pinned at mesh-sdk 9588871 (fix/639-an-ask-reads-at-a-glance), which adds Message.Note and asks.Glance.
537 lines
22 KiB
Go
537 lines
22 KiB
Go
package main
|
|
|
|
// The findings of the review of 2026-10-09, each held here by a test that failed before its fix.
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
)
|
|
|
|
// H3: approval rests on the controller's positive, recent word that no agent can become root where the router
|
|
// and the channel run (its verb root-free), asked live. Every other answer fails closed.
|
|
func TestApprovalNeedsTheControllersWordThatRootIsFree(t *testing.T) {
|
|
for name, tc := range map[string]struct {
|
|
setup func(h *Holder)
|
|
want string
|
|
}{
|
|
"a router that does not know its machine": {func(h *Holder) { h.Node = "" }, "does not know which machine"},
|
|
"a channel whose machine is not said": {func(h *Holder) {
|
|
k, _ := h.Bench.Kind("telegram")
|
|
k.Node = ""
|
|
h.Bench.SetKinds([]KindInfo{k})
|
|
}, "has not said which machine"},
|
|
"a controller that does not answer": {func(h *Holder) {
|
|
h.RootFree = func([]string) (map[string]RootVerdict, error) { return nil, errors.New("no responders") }
|
|
}, "could not be asked: no responders"},
|
|
"a machine the answer leaves out": {func(h *Holder) {
|
|
h.RootFree = func([]string) (map[string]RootVerdict, error) { return map[string]RootVerdict{}, nil }
|
|
}, "said nothing of whether an agent can become root on anchor"},
|
|
"a machine not free": {func(h *Holder) { rootFreeOf[h]["anchor"] = "the agent account is not judged" },
|
|
"may become root on anchor"},
|
|
"the router's own machine not free": {func(h *Holder) {
|
|
h.Node = "router-machine"
|
|
rootFreeOf[h]["router-machine"] = "execute is served there"
|
|
}, "may become root on router-machine"},
|
|
} {
|
|
t.Run(name, func(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)
|
|
release := ticketFor(tg.sends[0], "Release")
|
|
if release == "" {
|
|
t.Fatal("not offered while root was free")
|
|
}
|
|
tc.setup(h)
|
|
out, err := h.Chosen("telegram", choiceOf(release, "42", c.now()))
|
|
if out != "refused" || err == nil || !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")
|
|
}
|
|
if got := h.carriers(asks.Approve); len(got) != 0 {
|
|
t.Errorf("approval still carried on %v", got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// H3: the word is asked of the controller's verb, as this module, and read from its answer.
|
|
func TestTheRootWordIsAskedOfTheControllersVerb(t *testing.T) {
|
|
h, _, _, _, _, _ := newRouter(t)
|
|
h.RootFree = nil
|
|
var asked []string
|
|
h.Ask = func(key string, body any) (json.RawMessage, error) {
|
|
asked = append(asked, key)
|
|
raw, _ := json.Marshal(body)
|
|
if !strings.Contains(string(raw), `"machines":["anchor"]`) {
|
|
t.Errorf("asked about %s", raw)
|
|
}
|
|
return json.RawMessage(`{"machines":[{"machine":"anchor","free":true,"why":"judged","judged":"2026-10-09T10:00:00Z"}]}`), nil
|
|
}
|
|
info, _ := h.Bench.Kind("telegram")
|
|
if why := h.rootFree(info, false); why != "" {
|
|
t.Fatalf("free not read: %s", why)
|
|
}
|
|
if len(asked) != 1 || asked[0] != "seat:mesh-controller.root-free" {
|
|
t.Errorf("asked %v", asked)
|
|
}
|
|
h.Ask = func(string, any) (json.RawMessage, error) { return json.RawMessage(`not json`), nil }
|
|
if why := h.rootFree(info, false); !strings.Contains(why, "could not be read") {
|
|
t.Errorf("an unreadable answer was a pass: %q", why)
|
|
}
|
|
h.Ask = nil
|
|
if why := h.rootFree(info, false); why == "" {
|
|
t.Error("no way to ask was a pass")
|
|
}
|
|
}
|
|
|
|
// H3: a message rendered while root is not free offers no approval; a word kept for rendering is never the
|
|
// word an answer is judged on.
|
|
func TestAMessageRenderedWhileRootIsNotFreeOffersNoApproval(t *testing.T) {
|
|
h, tg, _, c, store, _ := 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)
|
|
rec := h.openAsk(h.open["ask.mesh-delivery.d-1"])
|
|
if got := h.offers(rec, "telegram"); len(got) != 3 {
|
|
t.Fatalf("offers %v", got)
|
|
}
|
|
rootFreeOf[h]["anchor"] = "not judged"
|
|
c.pass(RootFreeKept)
|
|
if got := h.offers(rec, "telegram"); len(got) != 0 {
|
|
t.Errorf("rendered with an approval while root is not free: %v", got)
|
|
}
|
|
// Free again, but the kept word is not: the answer asks anew and is taken.
|
|
delete(rootFreeOf[h], "anchor")
|
|
out, err := h.Chosen("telegram", choiceOf(ticketFor(tg.sends[0], "Release"), "42", c.now()))
|
|
if err != nil || out != "chosen" {
|
|
t.Errorf("judged on a kept word: %s %v", out, err)
|
|
}
|
|
}
|
|
|
|
// H3: a capability the controller's word no longer says stays no longer than the word is fresh: a router that
|
|
// cannot read the kinds again promises no verified sender.
|
|
func TestAStaleWordOnTheKindsPromisesNoVerifiedSender(t *testing.T) {
|
|
h, _, _, c, store, _ := newRouter(t)
|
|
link(h, store, c, "42", c.now().Add(-2*time.Hour))
|
|
if !h.Bench.Promises("telegram", "verified-sender") {
|
|
t.Fatal("not promised while fresh")
|
|
}
|
|
c.pass(KindsFresh + time.Second)
|
|
h.Bench.Stand("telegram", Standing{Ready: true, At: c.now()})
|
|
if h.Bench.Promises("telegram", "verified-sender") || len(h.carriers(asks.Approve)) != 0 {
|
|
t.Error("a stale word still promises a verified sender")
|
|
}
|
|
if !h.Bench.Promises("telegram", "choice") {
|
|
t.Error("a stale word stopped the channel sending")
|
|
}
|
|
}
|
|
|
|
// C6: a link's code is marked secret on every path to a channel, the desk's own role included, so the channel
|
|
// keeps no copy (the review found it dropped where the desk's role sends).
|
|
func TestALinksCodeIsSecretOnEveryPath(t *testing.T) {
|
|
c := start()
|
|
bench := NewBench(c.now)
|
|
bench.SetKinds([]KindInfo{
|
|
{Kind: "telegram", Module: "telegram", Node: "anchor", Capabilities: []string{"reaches-away", "choice", "verified-sender", "exact-render"}},
|
|
{Kind: "desktop", Module: "desk-channel", Node: "anchor", Capabilities: []string{"choice", "exact-render", "private"}},
|
|
})
|
|
bench.Stand("telegram", Standing{Ready: true, At: c.now()})
|
|
bench.Stand("desktop", Standing{Ready: true, At: c.now()})
|
|
var mu sync.Mutex
|
|
put := map[string][]asks.Message{}
|
|
publish := func(subject string, body any, id string) error {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if m, ok := body.(asks.Message); ok {
|
|
put[subject] = append(put[subject], m)
|
|
}
|
|
return nil
|
|
}
|
|
h := &Holder{Away: &SeatChannel{Bench: bench, Role: RoleAway, Publish: publish},
|
|
Desk: &SeatChannel{Bench: bench, Role: RoleDesk, Publish: publish}, Bench: bench, Publish: publish,
|
|
Asks: newMemAsks(), Node: "anchor", Now: c.now, Logf: t.Logf, Zone: func() *time.Location { return time.UTC }}
|
|
h.init()
|
|
h.Link("telegram", asks.LinkAsked{ID: "l1", At: c.now(), Sender: asks.Sender{Identity: "42", Display: "Jo", Authenticated: true}})
|
|
shown := put[asks.ChannelSubject(asks.Send, "desktop")]
|
|
if len(shown) != 1 || !strings.Contains(shown[0].Body, "send this code") {
|
|
t.Fatalf("the code was not shown on the desk: %+v", put)
|
|
}
|
|
if !shown[0].Secret {
|
|
t.Error("the code went to the desk not marked secret: the channel would keep a copy")
|
|
}
|
|
}
|
|
|
|
// L1: a warrant's message id is new each time: one anyone could foresee lets another publisher on the stream
|
|
// take it first, and the bus would drop the warrant as a duplicate.
|
|
func TestAWarrantsMessageIdCannotBeForeseen(t *testing.T) {
|
|
h, tg, _, c, store, _ := newRouter(t)
|
|
link(h, store, c, "42", c.now().Add(-2*time.Hour))
|
|
var ids []string
|
|
h.Publish = func(subject string, body any, id string) error {
|
|
if _, ok := body.(asks.Warrant); ok {
|
|
ids = append(ids, id)
|
|
}
|
|
return nil
|
|
}
|
|
submit(t, h, "mesh-delivery", heldAsk(c, "d-1"))
|
|
settle(h, c)
|
|
if out, err := h.Chosen("telegram", choiceOf(ticketFor(tg.sends[0], "Release"), "42", c.now())); out != "chosen" {
|
|
t.Fatalf("%s %v", out, err)
|
|
}
|
|
_ = h.tell(*store.asks["mesh-delivery.d-1"].Warrant)
|
|
if len(ids) != 2 || ids[0] == ids[1] {
|
|
t.Fatalf("ids %v", ids)
|
|
}
|
|
for _, id := range ids {
|
|
if strings.Contains(id, "d-1") || strings.Contains(id, "mesh-delivery") {
|
|
t.Errorf("the id %q is made of what anyone knows of the ask", id)
|
|
}
|
|
}
|
|
}
|
|
|
|
// M2: an ask's message says who asks, as the bus proves it, what each answer does, and what happens if nobody
|
|
// answers.
|
|
func TestAnAsksMessageSaysWhoAsksWhatEachAnswerDoesAndWhatExpiryDoes(t *testing.T) {
|
|
h, tg, _, c, store, _ := 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)
|
|
// In the note, after the body and the buttons (issue 639): the body is the asker's own words alone.
|
|
body := tg.sends[0].Note
|
|
for _, want := range []string{"Asked by mesh-delivery at ", "Release: the delivery goes on. Stop: the delivery ends.",
|
|
"Silence for a week: nothing more is said of it for a week.",
|
|
"Unanswered by " + c.now().Add(2*time.Hour-BurstWindow).In(time.UTC).Format("15:04") + ": the delivery stays held."} {
|
|
if !strings.Contains(body, want) || strings.Contains(tg.sends[0].Body, want) {
|
|
t.Errorf("the message does not say %q:\n%s", want, body)
|
|
}
|
|
}
|
|
}
|
|
|
|
// M3: the router's own unlink binds the account it asks about: an approval from that account, after another was
|
|
// linked in its place, unlinks nothing.
|
|
func TestAnUnlinkUnlinksOnlyTheAccountItNamed(t *testing.T) {
|
|
h, tg, _, c, store, _ := newRouter(t)
|
|
link(h, store, c, "42", c.now().Add(-2*time.Hour))
|
|
if _, err := h.Unlink("telegram"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unlink := ticketFor(tg.sends[len(tg.sends)-1], "Unlink")
|
|
delete(store.ids, identityKey("telegram", "42"))
|
|
store.ids[identityKey("telegram", "77")] = Identity{Kind: "telegram", Identity: "77", Display: "Al",
|
|
Linked: c.now().Add(-2 * time.Hour), ApproveFrom: c.now().Add(-time.Hour)}
|
|
_, _ = h.Chosen("telegram", choiceOf(unlink, "42", c.now()))
|
|
if _, still := store.ids[identityKey("telegram", "77")]; !still {
|
|
t.Error("an approval to unlink 42 unlinked 77")
|
|
}
|
|
if got := store.asks; len(got) == 0 {
|
|
t.Fatal("no ask")
|
|
}
|
|
for _, rec := range store.asks {
|
|
if rec.Act != nil && rec.Act.Identity != "42" {
|
|
t.Errorf("the ask binds %q", rec.Act.Identity)
|
|
}
|
|
}
|
|
}
|
|
|
|
// M3: a link is checked again as it is made: an approved relink does not link a second account of a kind.
|
|
func TestALinkIsNotMadeWhileAnotherAccountIsLinked(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])
|
|
_ = h.Prove("telegram", asks.Code{Sender: asks.Sender{Identity: "666", Authenticated: true}, Code: code})
|
|
asked := tg.sends[len(tg.sends)-1]
|
|
// Meanwhile root linked another account (allowed at the machine).
|
|
store.ids[identityKey("telegram", "77")] = Identity{Kind: "telegram", Identity: "77", Linked: c.now(), ApproveFrom: c.now().Add(time.Hour)}
|
|
_, _ = h.Chosen("telegram", choiceOf(ticketFor(asked, "Allow"), "42", c.now()))
|
|
if _, linked := store.ids[identityKey("telegram", "666")]; linked {
|
|
t.Errorf("a second account of a kind was linked: %v", store.ids)
|
|
}
|
|
}
|
|
|
|
// M3: an account unlinked less than a day ago is not linked again by a code: no code is shown for it.
|
|
func TestAnUnlinkedAccountDoesNotComeBackByACodeWithinADay(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()))
|
|
shown := len(dt.sends)
|
|
h.Link("telegram", asks.LinkAsked{ID: "l1", At: c.now(), Sender: asks.Sender{Identity: "42", Display: "Jo", Authenticated: true}})
|
|
if _, pending := store.links["telegram"]; pending {
|
|
t.Error("a link was begun for the account unlinked an hour ago")
|
|
}
|
|
for _, m := range dt.sends[shown:] {
|
|
if strings.Contains(m.Body, "send this code") {
|
|
t.Error("a code was shown for it")
|
|
}
|
|
}
|
|
if len(store.ids) != 0 {
|
|
t.Error("linked again")
|
|
}
|
|
}
|
|
|
|
// H4: an account still in its first hour cannot approve yet, so whoever is at the desk may unlink it — asked
|
|
// there when another account asks to be linked — and past its hour the desk no longer can.
|
|
func TestAnAccountInItsFirstHourIsUnlinkedFromTheDesk(t *testing.T) {
|
|
h, tg, dt, c, store, w := newRouter(t)
|
|
link(h, store, c, "666", c.now().Add(-10*time.Minute)) // an account the operator did not link
|
|
h.Link("telegram", asks.LinkAsked{ID: "l1", At: c.now(), Sender: asks.Sender{Identity: "42", Display: "Jo", Authenticated: true}})
|
|
if reply := tg.sends[len(tg.sends)-1]; !strings.Contains(reply.Body, "unlink it at your desk") {
|
|
t.Fatalf("the sender was not told how: %q", reply.Body)
|
|
}
|
|
asked := dt.sends[len(dt.sends)-1]
|
|
if labelsOf(asked) != "Unlink it" || !strings.Contains(asked.Body, "number 666") {
|
|
t.Fatalf("the desk was not asked: %+v", asked)
|
|
}
|
|
ch := choiceOf(ticketFor(asked, "Unlink it"), "g14", c.now())
|
|
ch.Sender.Authenticated = false
|
|
if out, err := h.Chosen("desktop", ch); err != nil || out != "chosen" {
|
|
t.Fatalf("%s %v", out, err)
|
|
}
|
|
if len(store.ids) != 0 {
|
|
t.Errorf("not unlinked: %v", store.ids)
|
|
}
|
|
if len(w.of(SelfAsker)) != 0 {
|
|
t.Error("the router's own act was told to an asker")
|
|
}
|
|
// Past its hour, no question goes to the desk.
|
|
h2, tg2, dt2, c2, store2, _ := newRouter(t)
|
|
link(h2, store2, c2, "666", c2.now().Add(-2*time.Hour))
|
|
h2.Link("telegram", asks.LinkAsked{ID: "l1", At: c2.now(), Sender: asks.Sender{Identity: "42", Authenticated: true}})
|
|
for _, m := range dt2.sends {
|
|
if labelsOf(m) == "Unlink it" {
|
|
t.Error("the desk was asked to unlink an account past its first hour")
|
|
}
|
|
}
|
|
if !strings.Contains(tg2.sends[len(tg2.sends)-1].Body, "own approval, or as root") {
|
|
t.Errorf("%q", tg2.sends[len(tg2.sends)-1].Body)
|
|
}
|
|
}
|
|
|
|
// H4 (C4): while the desk cannot show a code now — its notifications paused — no link is begun and no code is
|
|
// made or queued; the sender is told why.
|
|
func TestNoLinkIsBegunWhileTheDeskCannotShowTheCode(t *testing.T) {
|
|
h, tg, dt, c, store, _ := newRouter(t)
|
|
h.Bench.Stand("desktop", Standing{Ready: false, Why: "notifications are paused on the desk", At: c.now()})
|
|
h.Link("telegram", asks.LinkAsked{ID: "l1", At: c.now(), Sender: asks.Sender{Identity: "42", Display: "Jo", Authenticated: true}})
|
|
if _, pending := store.links["telegram"]; pending {
|
|
t.Error("a link was begun")
|
|
}
|
|
if len(dt.sends) != 0 {
|
|
t.Errorf("something was put to the desk: %+v", dt.sends)
|
|
}
|
|
if reply := tg.sends[len(tg.sends)-1].Body; !strings.Contains(reply, "notifications are paused on the desk") {
|
|
t.Errorf("the sender was not told why: %q", reply)
|
|
}
|
|
}
|
|
|
|
// L5: links are bounded per account, and kept in the router's state: a stranger's tries do not stop the
|
|
// operator's link, and a restart forgets no count.
|
|
func TestLinksAreBoundedPerAccountAndTheCountIsKept(t *testing.T) {
|
|
h, tg, dt, c, store, _ := newRouter(t)
|
|
eve := asks.Sender{Identity: "666", Display: "Eve", Authenticated: true}
|
|
for i := 0; i < LinksAnHour; i++ {
|
|
h.Link("telegram", asks.LinkAsked{ID: "e" + string(rune('0'+i)), At: c.now(), Sender: eve})
|
|
}
|
|
shown := len(dt.sends)
|
|
h.Link("telegram", asks.LinkAsked{ID: "jo", At: c.now(), Sender: asks.Sender{Identity: "42", Display: "Jo", Authenticated: true}})
|
|
if len(dt.sends) != shown+1 || !strings.Contains(dt.sends[len(dt.sends)-1].Body, "(number 42)") {
|
|
t.Fatalf("a stranger's tries stopped the operator's link: %q", tg.sends[len(tg.sends)-1].Body)
|
|
}
|
|
// A router started again on the same state still counts the stranger's tries.
|
|
h2, tg2, dt2, c2, _, _ := newRouter(t)
|
|
h2.Asks = store
|
|
c2.pass(c.now().Sub(c2.now()))
|
|
stand(h2, c2)
|
|
before := len(dt2.sends)
|
|
h2.Link("telegram", asks.LinkAsked{ID: "e9", At: c2.now(), Sender: eve})
|
|
if len(dt2.sends) != before || !strings.Contains(tg2.sends[len(tg2.sends)-1].Body, "Try again in an hour") {
|
|
t.Error("a restart forgot the count")
|
|
}
|
|
}
|
|
|
|
// L7: two answers to one ask arriving at once — on two routers sharing the state — make exactly one warrant.
|
|
func TestTwoAnswersAtOnceMakeOneWarrant(t *testing.T) {
|
|
h1, tg, _, c, store, w1 := newRouter(t)
|
|
link(h1, store, c, "42", c.now().Add(-2*time.Hour))
|
|
submit(t, h1, "mesh-delivery", heldAsk(c, "d-1"))
|
|
settle(h1, c)
|
|
release, stop := ticketFor(tg.sends[0], "Release"), ticketFor(tg.sends[0], "Stop")
|
|
h2, _, _, _, _, w2 := newRouter(t)
|
|
h2.Asks = store
|
|
h2.Now = h1.Now
|
|
if err := h2.loadAsks(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var wg sync.WaitGroup
|
|
outs := make([]string, 2)
|
|
for i, pick := range []struct {
|
|
h *Holder
|
|
ticket string
|
|
}{{h1, release}, {h2, stop}} {
|
|
wg.Add(1)
|
|
go func(i int, h *Holder, ticket string) {
|
|
defer wg.Done()
|
|
outs[i], _ = h.Chosen("telegram", choiceOf(ticket, "42", c.now()))
|
|
}(i, pick.h, pick.ticket)
|
|
}
|
|
wg.Wait()
|
|
n := len(w1.of("mesh-delivery")) + len(w2.of("mesh-delivery"))
|
|
if n != 1 {
|
|
t.Fatalf("%d warrants for one ask (%v)", n, outs)
|
|
}
|
|
if (outs[0] == "chosen") == (outs[1] == "chosen") {
|
|
t.Errorf("not exactly one chosen: %v", outs)
|
|
}
|
|
}
|
|
|
|
// The confirmation review of 2026-10-09, N1-router: another account's /start offers the desk an unlink of the
|
|
// operator's account while it is in its first hour; a desk click — which a program on the desk machine can forge
|
|
// — unlinked it and barred it for a day, again and again, so the operator never approved anything. A desk
|
|
// unlink in the first hour bars nothing: the operator links again at once with a code from the desk, and the
|
|
// next link waits for nobody. The bar is for an account removed deliberately later.
|
|
func TestAnUnlinkFromTheDeskInTheFirstHourBarsNothing(t *testing.T) {
|
|
h, tg, dt, c, store, _ := newRouter(t)
|
|
link(h, store, c, "42", c.now().Add(-10*time.Minute)) // the operator's own, linked just now
|
|
h.Link("telegram", asks.LinkAsked{ID: "l1", At: c.now(), Sender: asks.Sender{Identity: "666", Display: "X", Authenticated: true}})
|
|
asked := dt.sends[len(dt.sends)-1]
|
|
ch := choiceOf(ticketFor(asked, "Unlink it"), "g14", c.now()) // a click nobody can prove was the operator's
|
|
ch.Sender.Authenticated = false
|
|
if out, err := h.Chosen("desktop", ch); err != nil || out != "chosen" {
|
|
t.Fatalf("%s %v", out, err)
|
|
}
|
|
if len(store.ids) != 0 {
|
|
t.Fatalf("not unlinked: %v", store.ids)
|
|
}
|
|
if u := h.recentUnlink("telegram"); u != nil {
|
|
t.Errorf("a desk unlink in the first hour bars relinking: %+v", u)
|
|
}
|
|
// The operator sends /start again: a code is shown at once, and the code links without waiting for anybody.
|
|
c.pass(time.Minute)
|
|
stand(h, c)
|
|
h.Link("telegram", asks.LinkAsked{ID: "l2", At: c.now(), Sender: asks.Sender{Identity: "42", Display: "Jo", Authenticated: true}})
|
|
code := ""
|
|
for _, m := range dt.sends {
|
|
if strings.Contains(m.Body, "number 42") && strings.Contains(m.Body, "send this code") {
|
|
code = codeIn(t, m.Body)
|
|
}
|
|
}
|
|
if code == "" {
|
|
t.Fatalf("no code was shown to relink the operator's account; said %q", tg.sends[len(tg.sends)-1].Body)
|
|
}
|
|
if a := h.Prove("telegram", asks.Code{Sender: asks.Sender{Identity: "42", Authenticated: true}, Code: code, Purpose: "link"}); !a.Accepted ||
|
|
!strings.Contains(a.Words, "Linked") {
|
|
t.Fatalf("the operator's code was not taken at once: %+v", a)
|
|
}
|
|
}
|
|
|
|
// And break-glass lifts any bar: root's allow-link at the router's machine, with no link waiting, ends a day's bar.
|
|
func TestBreakGlassLiftsABarOnRelinking(t *testing.T) {
|
|
h, tg, _, 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()))
|
|
if h.recentUnlink("telegram") == nil {
|
|
t.Fatal("the control: a deliberate unlink bars relinking for a day")
|
|
}
|
|
h.StateDir = t.TempDir()
|
|
was := fileOwner
|
|
t.Cleanup(func() { fileOwner = was })
|
|
fileOwner = func(os.FileInfo) uint32 { return 0 }
|
|
_ = os.WriteFile(filepath.Join(h.StateDir, "allow-link.telegram"), nil, 0o600)
|
|
h.Tick()
|
|
if u := h.recentUnlink("telegram"); u != nil {
|
|
t.Errorf("root's allow-link left the bar: %+v", u)
|
|
}
|
|
}
|
|
|
|
// codeIn is the six digits a link's notice shows, "123 456".
|
|
func codeIn(t *testing.T, body string) string {
|
|
t.Helper()
|
|
m := regexp.MustCompile(`(\d{3}) (\d{3})\.`).FindStringSubmatch(body)
|
|
if m == nil {
|
|
t.Fatalf("no code in %q", body)
|
|
}
|
|
return m[1] + m[2]
|
|
}
|
|
|
|
// The confirmation review of 2026-10-09 (low): the router asked the controller's root-free word — up to ten
|
|
// seconds — while holding its work, so every ask, answer and link waited behind one slow controller. It now
|
|
// asks before it takes the work, and judges with the word it was given then.
|
|
func TestTheRootFreeWordIsNotAskedWhileTheWorkIsHeld(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)
|
|
release := ticketFor(tg.sends[0], "Release")
|
|
asking, slow := make(chan struct{}), make(chan struct{})
|
|
var once sync.Once
|
|
free := h.RootFree
|
|
h.RootFree = func(machines []string) (map[string]RootVerdict, error) {
|
|
once.Do(func() { close(asking); <-slow })
|
|
return free(machines)
|
|
}
|
|
done := make(chan string, 1)
|
|
go func() {
|
|
out, _ := h.Chosen("telegram", choiceOf(release, "42", c.now()))
|
|
done <- out
|
|
}()
|
|
<-asking
|
|
linked := make(chan struct{})
|
|
go func() {
|
|
h.Link("telegram", asks.LinkAsked{ID: "l9", At: c.now(), Sender: asks.Sender{Identity: "77", Authenticated: true}})
|
|
close(linked)
|
|
}()
|
|
select {
|
|
case <-linked:
|
|
case <-time.After(2 * time.Second):
|
|
close(slow)
|
|
t.Fatal("a link waited for the controller's root-free word: the work was held while it was asked")
|
|
}
|
|
close(slow)
|
|
if out := <-done; out != "chosen" || len(w.of("mesh-delivery")) != 1 {
|
|
t.Fatalf("the answer after the word: %s, %v", out, w.of("mesh-delivery"))
|
|
}
|
|
}
|
|
|
|
// The confirmation review of 2026-10-09 (low, on M2): an answer whose line — what it does — the content rule
|
|
// withholds is not offered either; its button went out alone, and a tap would choose what was not shown.
|
|
func TestAnAnswerWhoseEffectIsWithheldIsNotOffered(t *testing.T) {
|
|
h, _, _, c, store, _ := newRouter(t)
|
|
link(h, store, c, "42", c.now().Add(-2*time.Hour))
|
|
submit(t, h, "mesh-delivery", heldAsk(c, "d-1"))
|
|
key := askKey("mesh-delivery", "d-1")
|
|
h.mu.Lock()
|
|
e := h.asks[key]
|
|
e.rec.Ask.Options[0].Does = "the delivery reads /etc/shadow"
|
|
h.asks[key] = e
|
|
r := h.open["ask."+key]
|
|
h.mu.Unlock()
|
|
m := h.sayOn(r, "asked", "telegram")
|
|
for _, a := range m.Actions {
|
|
if a.Label == "Release" {
|
|
t.Errorf("Release was offered with its effect withheld: %+v", m.Actions)
|
|
}
|
|
}
|
|
if strings.Contains(m.Body, "/etc/shadow") {
|
|
t.Errorf("the withheld words were said: %q", m.Body)
|
|
}
|
|
if len(m.Actions) == 0 {
|
|
t.Error("the answers whose effect was said were not offered either")
|
|
}
|
|
}
|