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
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.
183 lines
8.0 KiB
Go
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)
|
|
}
|
|
}
|