Files
mesh-catalog/modules/messenger/cmd/messenger/restart_test.go
T
jochen cd85104988
mesh/merge-gate pass: builds desk-channel, messenger → novox; 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, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
A channel shows nothing from its memory after a restart; the router says again what is still open (issue 384)
On 2026-10-10 the desk's notifier started again on two machines and the desk showed, from its own
memory, nine old popups: resolved, silenced and cancelled ones among them. Its memory held every handle
ever shown with buttons, and the router's edits (silenced, resolved) had reached only the newest handle
of each problem, so the rest stood as if open.

A channel's memory serves only to replace or edit a message in place. A notifier that started again lost
its screen: the desk forgets that machine's notification ids and says in its standing since when the
screen is new (shows-since). The router, which alone knows what is still open and unanswered, says that
again on that kind: one message per problem under its handle with the words and answers of now, a
silenced one edited to say so with no answers and no urgency, nothing that ended. Each move of
shows-since is acted on once, and waits while the desk cannot send rather than going to Telegram.
Telegram re-shows nothing on a restart (its messages stay in the chat) and says no shows-since.

An edit that offers nothing and reaches no screen its message is on is taken with nothing to change;
one that offers answers is said as failed, so the router says it anew.
2026-10-10 16:02:37 +02:00

183 lines
8.0 KiB
Go

package main
import (
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/asks"
)
// Issue 384: the desk's notifier started again and the desk showed, from its own memory, nine old popups —
// resolved, silenced and cancelled ones among them. Now the desk says in its standing since when its screen
// is new, and this router says again only what is still open and unanswered: with three messages on the desk
// — one open with an answer, one silenced, one resolved — one is said again under its handle, with its
// answers; one is edited to the silenced words, with no answers and no urgency; nothing is said for the
// resolved one. A standing that repeats the time says nothing more; its ticket is one-time still, and the
// channel that reaches the operator away hears nothing of it.
func TestAScreenMadeNewGetsOnlyWhatIsOpenAndUnansweredSaidAgain(t *testing.T) {
h, tg, dt, c, _, w := newRouter(t)
h.Start()
c.pass(2 * time.Minute)
// Open, with a question the desk carries.
open := cond("machine.shanks.units", Warning, "2 failed services on shanks")
open.Raised = c.now()
h.Condition(EventRaised, open)
settle(h, c)
if len(dt.sends) != 1 {
t.Fatalf("desk %d", len(dt.sends))
}
ask := ackAsk(c, "silence-units")
ask.About = open.Key
submit(t, h, ConditionOwner, ask)
if e := dt.edits["g14=1"]; ticketFor(e, "Silence for a week") == "" {
t.Fatalf("the question was not put on the open message: %+v", e)
}
// Silenced, with a question of its own, and its message says it is silenced.
runFor(h, c, 20*time.Minute)
sil := cond("machine.ace.agent-home", Warning, "Agent-home shrank")
sil.Raised = c.now()
h.Condition(EventRaised, sil)
settle(h, c)
if len(dt.sends) != 2 {
t.Fatalf("desk %d", len(dt.sends))
}
stand(h, c) // the channels' word, fresh again after the minutes passed
silAsk := ackAsk(c, "silence-home")
silAsk.About = sil.Key
submit(t, h, ConditionOwner, silAsk)
if e := dt.edits["g14=2"]; ticketFor(e, "Silence for a week") == "" {
t.Fatalf("the question was not put on the message to be silenced: %+v", e)
}
sil.SilencedTill, sil.SilencedWhy, sil.At = c.now().Add(7*24*time.Hour), "known", c.now()
h.Condition(EventChanged, sil)
if e := dt.edits["g14=2"]; !strings.HasPrefix(e.Title, "Silenced until") {
t.Fatalf("the silence was not said: %+v", e)
}
// Open, told, and asking nothing: not unanswered, so not said again.
runFor(h, c, 20*time.Minute)
info := cond("seat.mesh-controller.assign-by-hand", Warning, "Assign keeps being fixed by hand")
info.Raised = c.now()
h.Condition(EventRaised, info)
settle(h, c)
if len(dt.sends) != 3 {
t.Fatalf("desk %d", len(dt.sends))
}
// Resolved, and its clearing said.
runFor(h, c, 20*time.Minute)
res := cond("seat.operator-channel.asks-undelivered", Warning, "Questions for you not delivered")
res.Raised = c.now()
h.Condition(EventRaised, res)
settle(h, c)
if len(dt.sends) != 4 {
t.Fatalf("desk %d", len(dt.sends))
}
h.Condition(EventCleared, Condition{Key: res.Key, At: c.now()})
runFor(h, c, 3*time.Minute)
if e := dt.edits["g14=4"]; !strings.HasPrefix(e.Title, "Resolved: ") {
t.Fatalf("the clearing was not said: %+v", e)
}
silencedBefore := dt.edits["g14=2"]
delete(dt.edits, "g14=2") // so the edit said again is seen as such
sends, edits, replaced, tgSaid := len(dt.sends), len(dt.edits), len(dt.replaced), saidOn(tg)
// The desk's notifier started again: its standing says since when its screen is new.
c.pass(time.Minute)
newSince := c.now()
h.Stand("desktop", Standing{Ready: true, At: c.now(), ShowsSince: newSince})
runFor(h, c, 2*time.Minute)
if len(dt.sends) != sends {
t.Fatalf("a new message was sent instead of one under its handle: %+v", dt.sends[sends:])
}
if len(dt.replaced) != replaced+1 || dt.replaced[replaced].id != "g14=1" {
t.Fatalf("the open question was not said again under its handle, once and alone: %+v", dt.replaced[replaced:])
}
again := dt.replaced[replaced].m
if ticketFor(again, "Silence for a week") == "" || !strings.Contains(again.Title, headlineOf(open.Key)) {
t.Fatalf("said again without its answer or its words: %+v", again)
}
if e, said := dt.edits["g14=2"]; !said || !strings.HasPrefix(e.Title, "Silenced until") || len(e.Actions) != 0 || e.Urgent ||
!e.Quiet || e.Title != silencedBefore.Title {
t.Fatalf("the silenced one was not edited to the silenced words again: %+v (said %v)", e, said)
}
if len(dt.edits) != edits+1 {
t.Fatalf("an edit reached a message that is not silenced: %+v", dt.edits)
}
if e := dt.edits["g14=4"]; !strings.HasPrefix(e.Title, "Resolved: ") {
t.Fatalf("the resolved one was changed: %+v", e)
}
if saidOn(tg) != tgSaid {
t.Fatalf("the channel that reaches the operator away heard of the desk's screen: %+v %+v", tg.sends, tg.edits)
}
// The standing said again with the same time, and one with an older time: nothing more.
h.Stand("desktop", Standing{Ready: true, At: c.now(), ShowsSince: newSince})
h.Stand("desktop", Standing{Ready: true, At: c.now(), ShowsSince: newSince.Add(-time.Hour)})
runFor(h, c, 2*time.Minute)
if len(dt.replaced) != replaced+1 || len(dt.sends) != sends {
t.Fatalf("said again more than once: replaced %d, sends %d", len(dt.replaced)-replaced, len(dt.sends)-sends)
}
// Its ticket is one-time still: one warrant, however often it is tapped, and refused from another machine.
tk := ticketFor(again, "Silence for a week")
ch := choiceOf(tk, "g14", c.now())
ch.Sender.Authenticated = false
if got, err := h.Chosen("desktop", ch); got != "chosen" || err != nil {
t.Fatalf("the answer on the message said again: %s, %v", got, err)
}
ch.ID = "c-second"
_, _ = h.Chosen("desktop", ch) // the same tap again: said as recorded (issue 372), nothing more done
other := choiceOf(tk, "shanks", c.now())
other.Sender.Authenticated = false
if got, _ := h.Chosen("desktop", other); got != "refused" {
t.Fatalf("the ticket taken from another machine after it was used: %s", got)
}
chosen := 0
for _, wt := range w.of(ConditionOwner) {
if wt.Outcome == asks.OutcomeChosen {
chosen++
}
}
if chosen != 1 {
t.Fatalf("warrants for one ticket: %d", chosen)
}
}
// A screen made new while the desk cannot send is said again once it can — and not pushed to the channel that
// reaches the operator away. A desk that never said its screen is new (or Telegram, whose messages stay in the
// chat) gets nothing said again; a screen made new before this router started is in the state it read.
func TestAScreenMadeNewWaitsForTheDeskAndIsNeverGuessed(t *testing.T) {
h, tg, dt, c, _, _ := newRouter(t)
h.Start()
c.pass(2 * time.Minute)
open := cond("machine.shanks.units", Warning, "2 failed services on shanks")
open.Raised = c.now()
h.Condition(EventRaised, open)
settle(h, c)
ask := ackAsk(c, "silence-units")
ask.About = open.Key
submit(t, h, ConditionOwner, ask)
runFor(h, c, time.Minute) // the question reaches Telegram too, as its own message
replaced, tgSaid := len(dt.replaced), saidOn(tg)
// Before this router started: already allowed for.
h.Stand("desktop", Standing{Ready: true, At: c.now(), ShowsSince: h.started.Add(-time.Minute)})
// Telegram says no screen: nothing.
h.Stand("telegram", Standing{Ready: true, At: c.now()})
runFor(h, c, time.Minute)
if len(dt.replaced) != replaced || saidOn(tg) != tgSaid {
t.Fatalf("said again without a screen made new: desk %+v, telegram %+v", dt.replaced[replaced:], tg.sends)
}
// Made new while the desk cannot send: waits, and goes to the desk once it can — never to Telegram.
c.pass(time.Minute)
since := c.now()
dt.notReady = errorf("notifications paused")
h.Stand("desktop", Standing{Ready: false, Why: "notifications paused", At: c.now(), ShowsSince: since})
runFor(h, c, 2*time.Minute)
if len(dt.replaced) != replaced || saidOn(tg) != tgSaid {
t.Fatalf("said while the desk cannot send: desk %+v, telegram %+v", dt.replaced[replaced:], tg.sends)
}
dt.notReady = nil
h.Stand("desktop", Standing{Ready: true, At: c.now(), ShowsSince: since})
runFor(h, c, time.Minute)
if len(dt.replaced) != replaced+1 || saidOn(tg) != tgSaid {
t.Fatalf("once the desk can send: desk %+v, telegram %+v", dt.replaced[replaced:], tg.sends)
}
}