mesh/merge-gate pass: builds 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 delivered
The review's two nits. A write of bytes identical to what is stored was invisible to a comparison of content, so the test store counts its writes and the preview's tests read that count: a persist from inside Preview now fails five of them instead of one. And a router holding an ask's record and no condition said it held none, rather than saying what that key is; the branches are the other way round, with a test that holds nothing else.
289 lines
11 KiB
Go
289 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A condition's message can be read before it is sent (novox/hq issue 422): the words a send would
|
|
// carry, the tracking line among them, and nothing of this router moved while they were read.
|
|
|
|
// previewHolder is a router holding one urgent condition — one a send goes out for at once, so a preview
|
|
// that sends, queues or records anything is caught — with the issues given tracking its key.
|
|
func previewHolder(t *testing.T, issues []TrackedIssue) (*Holder, *fakeChannel, *fakeChannel, *clock, *memStore, Condition) {
|
|
t.Helper()
|
|
h, tg, dt, c, st := newHolder(t)
|
|
h.Tracking = func() (map[string][]TrackedIssue, error) {
|
|
return map[string][]TrackedIssue{"mesh.hand-acts.rolled-back.healer-wanted": issues}, nil
|
|
}
|
|
k := cond("mesh.hand-acts.rolled-back.healer-wanted", Urgent, "rolled-back was repaired by hand 4 times in 14 days")
|
|
k.Explanation = "Nothing for you to do. A person repaired rolled-back by hand 4 times in 14 days, so an automatic repair is wanted for it. Nothing is broken now."
|
|
k.Needs = ""
|
|
k.Raised = c.now()
|
|
h.Condition(EventRaised, k)
|
|
settle(h, c)
|
|
if len(tg.sends) == 0 && len(dt.sends) == 0 {
|
|
t.Fatal("the condition this is read from was never sent, so a preview sending something would not show")
|
|
}
|
|
return h, tg, dt, c, st, k
|
|
}
|
|
|
|
// unmoved fails when anything a send would move has moved: the sends, the queue, the history, what is
|
|
// stored, and the record itself.
|
|
type unmoved struct {
|
|
sends, queued, recent, refusals, puts int
|
|
stored Record
|
|
held Record
|
|
}
|
|
|
|
func mark(t *testing.T, h *Holder, tg, dt *fakeChannel, st *memStore, key string) unmoved {
|
|
t.Helper()
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
queued := 0 // the outbox is held per channel, never per condition
|
|
for _, list := range h.outbox {
|
|
queued += len(list)
|
|
}
|
|
return unmoved{sends: len(tg.sends) + len(dt.sends), queued: queued, recent: len(h.recent),
|
|
refusals: len(h.refusals), puts: st.puts, stored: st.recs[key], held: *h.open[key]}
|
|
}
|
|
|
|
func (was unmoved) check(t *testing.T, h *Holder, tg, dt *fakeChannel, st *memStore, c *clock, key string) {
|
|
t.Helper()
|
|
// A queued message waits for a tick, so the queue is read and then given one: either would catch it.
|
|
now := mark(t, h, tg, dt, st, key)
|
|
if now.sends != was.sends {
|
|
t.Errorf("a preview sent %d message(s)", now.sends-was.sends)
|
|
}
|
|
if now.queued != was.queued {
|
|
t.Errorf("a preview queued %d message(s)", now.queued-was.queued)
|
|
}
|
|
if now.recent != was.recent {
|
|
t.Errorf("a preview recorded %d send(s)", now.recent-was.recent)
|
|
}
|
|
if now.refusals != was.refusals {
|
|
t.Errorf("a preview recorded %d refusal(s): only a send does that", now.refusals-was.refusals)
|
|
}
|
|
if now.puts != was.puts {
|
|
t.Errorf("a preview wrote to the store %d time(s), even if the bytes were the same", now.puts-was.puts)
|
|
}
|
|
if !reflect.DeepEqual(now.stored, was.stored) {
|
|
t.Errorf("a preview wrote the record: %+v", now.stored)
|
|
}
|
|
if !reflect.DeepEqual(now.held, was.held) {
|
|
t.Errorf("a preview changed the record held: %+v", now.held)
|
|
}
|
|
settle(h, c)
|
|
if after := mark(t, h, tg, dt, st, key); after.sends != was.sends {
|
|
t.Errorf("a preview queued a message, which the next tick sent: %d", after.sends-was.sends)
|
|
}
|
|
}
|
|
|
|
func TestAConditionsMessageIsReadWithoutBeingSent(t *testing.T) {
|
|
h, tg, dt, c, st, k := previewHolder(t, []TrackedIssue{{Number: 352, Status: "located", Needs: "decision"}})
|
|
was := mark(t, h, tg, dt, st, k.Key)
|
|
|
|
out, err := h.Preview(k.Key, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out["what"] != "raised" {
|
|
t.Errorf("what %v; a preview with none asked is the first message", out["what"])
|
|
}
|
|
if out["title"] != "Urgent: mesh hand-acts rolled-back healer-wanted" {
|
|
t.Errorf("title %q", out["title"])
|
|
}
|
|
body, _ := out["body"].(string)
|
|
if !strings.Contains(body, "Tracked as issue 352: nobody is working on it; it waits for a decision first.") {
|
|
t.Errorf("the body says nothing of the issue that tracks it: %q", body)
|
|
}
|
|
if out["tracking"] != "Tracked as issue 352: nobody is working on it; it waits for a decision first." {
|
|
t.Errorf("tracking %q", out["tracking"])
|
|
}
|
|
if out["allowed"] != true {
|
|
t.Errorf("allowed %v, carried %v: these words may leave the mesh", out["allowed"], out["carried"])
|
|
}
|
|
// The verdict is left out while nobody works on the issue (ADR 0285 §4), as in a send.
|
|
if strings.Contains(body, "Nothing for you to do.") {
|
|
t.Errorf("the preview reassures where nothing is being done: %q", body)
|
|
}
|
|
keyless(t, Message{Title: out["title"].(string), Body: body})
|
|
was.check(t, h, tg, dt, st, c, k.Key)
|
|
}
|
|
|
|
func TestAPreviewSaysTheWordsTheChannelWasGiven(t *testing.T) {
|
|
issues := []TrackedIssue{{Number: 386, Status: "fixing", Worker: "fix-386@g14", Since: at(21, 10)}}
|
|
h, tg, dt, c, st, k := previewHolder(t, issues)
|
|
sent := dt.sends[len(dt.sends)-1] // the message the desk was actually handed
|
|
was := mark(t, h, tg, dt, st, k.Key)
|
|
|
|
out, err := h.Preview(k.Key, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out["title"] != sent.Title || out["body"] != sent.Body {
|
|
t.Fatalf("preview %q / %q; the channel was given %q / %q", out["title"], out["body"], sent.Title, sent.Body)
|
|
}
|
|
if !strings.Contains(sent.Body, "Tracked as issue 386: fix-386 on g14 is fixing it since ") {
|
|
t.Fatalf("the message this is compared against carries no tracking line: %q", sent.Body)
|
|
}
|
|
was.check(t, h, tg, dt, st, c, k.Key)
|
|
}
|
|
|
|
func TestAClearingIsPreviewedAsIfItClearedNow(t *testing.T) {
|
|
h, tg, dt, c, st, k := previewHolder(t, []TrackedIssue{{Number: 352, Status: "located"}})
|
|
c.pass(4 * time.Hour)
|
|
was := mark(t, h, tg, dt, st, k.Key)
|
|
|
|
out, err := h.Preview(k.Key, "cleared")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Composed from the open record, this said "after a moment": the clearing's own time is zero, so the
|
|
// time it was open came out negative.
|
|
if out["title"] != "Resolved: mesh hand-acts rolled-back healer-wanted, after 4.0 h" {
|
|
t.Errorf("title %q; a clearing says how long it was open", out["title"])
|
|
}
|
|
if out["as-if"] == nil {
|
|
t.Errorf("the answer does not say the clearing is composed as if it cleared now: %v", out)
|
|
}
|
|
if _, said := out["tracking"]; said {
|
|
t.Errorf("a clearing says a tracking line: %v", out["tracking"])
|
|
}
|
|
was.check(t, h, tg, dt, st, c, k.Key)
|
|
}
|
|
|
|
func TestASilenceIsPreviewedOnlyWhileThereIsOne(t *testing.T) {
|
|
h, tg, dt, c, st, k := previewHolder(t, []TrackedIssue{{Number: 352, Status: "located"}})
|
|
|
|
// Under no silence, a silence message would say a moment that does not exist.
|
|
_, err := h.Preview(k.Key, "silenced")
|
|
if err == nil || !strings.Contains(err.Error(), "under no silence") {
|
|
t.Fatalf("a silence previewed without one: %v", err)
|
|
}
|
|
|
|
h.mu.Lock()
|
|
h.open[k.Key].SilencedTill = c.now().Add(7 * 24 * time.Hour)
|
|
h.open[k.Key].SilencedWhy = "the operator knows"
|
|
h.mu.Unlock()
|
|
was := mark(t, h, tg, dt, st, k.Key)
|
|
|
|
out, err := h.Preview(k.Key, "silenced")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
title, _ := out["title"].(string)
|
|
if !strings.HasPrefix(title, "Silenced until ") || strings.Contains(title, "Jan 1 ") || strings.Contains(title, " 1 Jan ") {
|
|
t.Errorf("title %q; a silence says until when it is quiet", title)
|
|
}
|
|
if _, said := out["tracking"]; said {
|
|
t.Errorf("a silence says a tracking line: %v", out["tracking"])
|
|
}
|
|
was.check(t, h, tg, dt, st, c, k.Key)
|
|
}
|
|
|
|
func TestAPreviewOfWhatThisRouterDoesNotHoldIsRefusedByName(t *testing.T) {
|
|
h, _, _, _, _, k := previewHolder(t, nil)
|
|
|
|
_, err := h.Preview("machine.ace.silent", "")
|
|
if err == nil {
|
|
t.Fatal("a condition this router does not hold was previewed")
|
|
}
|
|
if !strings.Contains(err.Error(), "machine.ace.silent") || !strings.Contains(err.Error(), k.Key) {
|
|
t.Errorf("the refusal says neither what was asked nor what is held: %v", err)
|
|
}
|
|
|
|
if _, err := h.Preview("", ""); err == nil || !strings.Contains(err.Error(), "key is required") {
|
|
t.Errorf("no key: %v", err)
|
|
}
|
|
if _, err := h.Preview(k.Key, "shouted"); err == nil || !strings.Contains(err.Error(), "shouted") {
|
|
t.Errorf("an unknown message: %v", err)
|
|
}
|
|
|
|
// An ask's own record is held here too, and is said as part of an ask, never as a condition.
|
|
h.mu.Lock()
|
|
h.open["ask.someone.1"] = &Record{Key: "ask.someone.1", Origin: "ask", Severity: Urgent, Summary: "a question"}
|
|
h.mu.Unlock()
|
|
_, err = h.Preview("ask.someone.1", "")
|
|
if err == nil || !strings.Contains(err.Error(), "not a condition") || !strings.Contains(err.Error(), k.Key) {
|
|
t.Errorf("an ask's record previewed as a condition: %v", err)
|
|
}
|
|
|
|
// A router holding an ask and no condition says what that key is, not that it holds nothing.
|
|
bare, _, _, _, _ := newHolder(t)
|
|
bare.mu.Lock()
|
|
bare.open["ask.someone.2"] = &Record{Key: "ask.someone.2", Origin: "ask", Severity: Urgent, Summary: "a question"}
|
|
bare.mu.Unlock()
|
|
_, err = bare.Preview("ask.someone.2", "")
|
|
if err == nil || !strings.Contains(err.Error(), "not a condition") {
|
|
t.Errorf("the only record held is an ask's: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestAPreviewAsksTheTrackerWhenItsWordWentCold(t *testing.T) {
|
|
asked := 0
|
|
h, _, _, c, _, k := previewHolder(t, []TrackedIssue{{Number: 352, Status: "located"}})
|
|
h.Tracking = func() (map[string][]TrackedIssue, error) {
|
|
asked++
|
|
return map[string][]TrackedIssue{k.Key: {{Number: 352, Status: "located"}}}, nil
|
|
}
|
|
|
|
c.pass(TrackingKept + time.Minute) // older than an answer is used for
|
|
out, err := h.Preview(k.Key, "quiet-reminder")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if asked != 1 {
|
|
t.Fatalf("the tracker was asked %d times; a preview on a cold word asks once", asked)
|
|
}
|
|
if out["tracking"] != "Tracked as issue 352: nobody is working on it." {
|
|
t.Errorf("tracking %q; a preview that asked should say what it heard", out["tracking"])
|
|
}
|
|
}
|
|
|
|
func TestAPreviewSaysTheReducedWordsAndWhyWhenItsOwnMayNotLeave(t *testing.T) {
|
|
h, tg, dt, c, st, _ := previewHolder(t, nil)
|
|
stated := 0
|
|
h.Emit = func(string, any) error { stated++; return nil }
|
|
// A warning that needs nothing is held, not sent, so this refusal is one no send has met yet.
|
|
k := cond("machine.ace.unreadable", Warning, "a file on it could not be read")
|
|
k.Explanation = "Nothing for you to do. A file at /home/someone/secrets.txt could not be read."
|
|
k.Needs = ""
|
|
k.Raised = c.now()
|
|
h.Condition(EventRaised, k)
|
|
settle(h, c)
|
|
was := mark(t, h, tg, dt, st, k.Key)
|
|
|
|
out, err := h.Preview(k.Key, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out["allowed"] != false || out["carried"] == nil {
|
|
t.Fatalf("allowed %v, carried %v; the content rule refuses a path", out["allowed"], out["carried"])
|
|
}
|
|
// The words said are the ones that would go out, not the refused ones.
|
|
body, _ := out["body"].(string)
|
|
if strings.Contains(body, "/home/") {
|
|
t.Errorf("the preview says words no send would carry: %q", body)
|
|
}
|
|
if !strings.Contains(body, "withheld") {
|
|
t.Errorf("body %q; a reduced message says that part of it was withheld", body)
|
|
}
|
|
if !strings.Contains(body, "a file path") {
|
|
t.Errorf("body %q; a reduced message says what it carried", body)
|
|
}
|
|
if out["instead"] == nil {
|
|
t.Errorf("the answer does not say these are the reduced words: %v", out)
|
|
}
|
|
// Reading it recorded no refusal, stated nothing on the bus, and left the record's own words alone.
|
|
if stated != 0 {
|
|
t.Errorf("a preview stated %d fact(s) on the bus", stated)
|
|
}
|
|
if was.held.Refused != "" {
|
|
t.Fatalf("the record was already refused before the preview: %q", was.held.Refused)
|
|
}
|
|
was.check(t, h, tg, dt, st, c, k.Key)
|
|
}
|