A delivery's own restarts reached the operator as notifications while it ran, and every restart of the tool runner popped the four silenced messages up again on the desk, twice. The router now reads the controller's walk start and end on plan-moved, holds what an open walk touches until its end or bound, tells the end as one message, and makes the restart pass once per restart from its own state, leaving silenced, withheld, answered and held messages as they are.
207 lines
9.1 KiB
Go
207 lines
9.1 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 when the answers were
|
|
// lost, and this router says again only what is still open and unanswered: with the desk showing one open
|
|
// message with an answer, one silenced, one open asking nothing and one resolved, one is said again under its
|
|
// handle (in place of its message), with its answers; nothing is said for the others — the silenced one
|
|
// included (novox/hq ADR 0308 §6, issue 531: its edit showed it on the desk again). 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 TestLostAnswersGetOnlyWhatIsOpenAndUnansweredSaidAgain(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)
|
|
}
|
|
delete(dt.edits, "g14=2") // so an edit said again would be 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 when the answers were lost.
|
|
c.pass(time.Minute)
|
|
newSince := c.now()
|
|
h.Stand("desktop", Standing{Ready: true, At: c.now(), AnswersLostAt: 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 {
|
|
t.Fatalf("the silenced one was said again for the restart: %+v", e)
|
|
}
|
|
if len(dt.edits) != edits {
|
|
t.Fatalf("an edit reached the desk for the restart: %+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(), AnswersLostAt: newSince})
|
|
h.Stand("desktop", Standing{Ready: true, At: c.now(), AnswersLostAt: 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)
|
|
}
|
|
}
|
|
|
|
// Whether a message is said again is its own: last said before the answers were lost. A loss before it was
|
|
// last said says nothing; a router that started again (it remembers no standing it acted on) says it again
|
|
// once, and then nothing more. A loss while the desk cannot send is said once it can — never pushed to the
|
|
// channel that reaches the operator away. Telegram, whose answers are never lost, says no time and gets nothing.
|
|
func TestLostAnswersAreJudgedPerMessageAndWaitForTheDesk(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)
|
|
// Lost before the message was last said: its answers are those of the notifier that runs now.
|
|
h.Stand("desktop", Standing{Ready: true, At: c.now(), AnswersLostAt: open.Raised.Add(-time.Minute)})
|
|
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 for answers that were not lost: desk %+v, telegram %+v", dt.replaced[replaced:], tg.sends)
|
|
}
|
|
// Lost while the desk cannot send: waits, and goes to the desk once it can — never to Telegram.
|
|
c.pass(time.Minute)
|
|
lost := c.now()
|
|
dt.notReady = errorf("notifications paused")
|
|
h.Stand("desktop", Standing{Ready: false, Why: "notifications paused", At: c.now(), AnswersLostAt: lost})
|
|
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(), AnswersLostAt: lost})
|
|
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)
|
|
}
|
|
// The router started again, reading back the pass it made from its state: nothing.
|
|
h.mu.Lock()
|
|
h.passes, h.lost = map[string]Pass{}, map[string]time.Time{}
|
|
if err := h.loadWalks(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
h.mu.Unlock()
|
|
h.Stand("desktop", Standing{Ready: true, At: c.now(), AnswersLostAt: lost})
|
|
runFor(h, c, time.Minute)
|
|
if len(dt.replaced) != replaced+1 {
|
|
t.Fatalf("said again twice for one loss: %+v", dt.replaced[replaced:])
|
|
}
|
|
// A send that fails is tried again at the next standing, on the desk alone.
|
|
c.pass(time.Minute)
|
|
lost = c.now()
|
|
dt.fail = errorf("no session answers")
|
|
h.Stand("desktop", Standing{Ready: true, At: c.now(), AnswersLostAt: lost})
|
|
runFor(h, c, 30*time.Second)
|
|
dt.fail = nil
|
|
if len(dt.replaced) != replaced+1 || saidOn(tg) != tgSaid {
|
|
t.Fatalf("after a failed send: desk %+v, telegram %+v", dt.replaced[replaced:], tg.sends)
|
|
}
|
|
runFor(h, c, time.Minute)
|
|
if len(dt.replaced) != replaced+2 || saidOn(tg) != tgSaid {
|
|
t.Fatalf("the failed send was not tried again on the desk: desk %+v, telegram %+v", dt.replaced[replaced:], tg.sends)
|
|
}
|
|
}
|