mesh/merge-gate head b7d9590fdc95 merged into trunk e1c8965ddbf1 — pass: builds messenger, telegram → novox; no bus step; every machine composes with the c…
mesh/repo-check head b7d9590fdc95 merged into trunk e1c8965ddbf1 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
Issue 372's fix with issue 639's one line edited an answered ask down to 'You chose X at HH:MM · headline', so the operator could not read back what they approved. The headline stays the title, the summary, points, link and Details stay the body, and how it ended and who acts on it become the note. Telegram cuts the body from its end past 4096 characters, never the title or the note.
93 lines
4.6 KiB
Go
93 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
)
|
|
|
|
// novox/hq issue 639: an ask's message reads at a glance on the phone. Its body is the asker's summary and points
|
|
// alone; who asked and when, what each answer does, the expiry and the asker's Details are its note. Once answered
|
|
// it keeps the question whole, so the operator can read back what they answered (issue 714).
|
|
|
|
// decisionAsk is the ask for ADR 0343 as the knowledge base composes it now (its glance form).
|
|
func decisionAsk(c *clock) asks.Ask {
|
|
return asks.Ask{ID: "kb-decision-343-0d1e2f3a", Headline: "Your agents' memory follows you to every machine",
|
|
Explanation: "What an agent learns in a project's memory is copied to every machine that runs the agent, so a session " +
|
|
"anywhere starts with it. A file changed on two machines keeps both versions and warns you; secrets in memory files " +
|
|
"are screened, though not perfectly.\n" +
|
|
"• Only memory folders travel: links and files over 128 KiB stay where they are\n" +
|
|
"• A conflict reaches you as a warning that needs you\n" +
|
|
"• A deleted file is marked, and a newer change elsewhere outlives the deletion",
|
|
Who: asks.Operator, Expires: c.now().Add(23*time.Hour + 50*time.Minute), OnExpiry: "the record stays proposed; nothing of it is in force",
|
|
About: "knowledge.decision.343",
|
|
Details: "ADR 0343, proposed by fix-367@g14; the knowledge base's own review approved it. Fingerprint a59f 361b f60e cb5e d237 a837.\n" +
|
|
"Read its 13 clauses with the knowledge base's get, number 343.",
|
|
Options: []asks.Option{
|
|
{ID: "approve", Label: "Approve", Does: "the record is accepted and its clauses are in force", Level: asks.Approve, Binds: bound("approve")},
|
|
{ID: "decline", Label: "Decline", Does: "the record stays proposed; nothing of it is in force", Level: asks.Approve, Binds: bound("decline")},
|
|
}}
|
|
}
|
|
|
|
func TestAnAsksMessageIsItsSummaryWithTheRestInItsNote(t *testing.T) {
|
|
h, tg, _, c, store, _ := newRouter(t)
|
|
link(h, store, c, "42", c.now().Add(-2*time.Hour))
|
|
a := decisionAsk(c)
|
|
submit(t, h, "mesh-knowledge", a)
|
|
settle(h, c)
|
|
if len(tg.sends) != 1 {
|
|
t.Fatalf("telegram got %d messages", len(tg.sends))
|
|
}
|
|
m := tg.sends[0]
|
|
if m.Title != a.Headline || m.Body != a.Explanation {
|
|
t.Errorf("the body is not the asker's summary and points alone:\n%s\n%s", m.Title, m.Body)
|
|
}
|
|
note := strings.Split(m.Note, "\n")
|
|
asked := c.now().In(time.UTC).Format("15:04")
|
|
want := []string{
|
|
"Asked by mesh-knowledge at " + asked + ". Unanswered by tomorrow " + a.Expires.In(time.UTC).Format("15:04") +
|
|
": the record stays proposed; nothing of it is in force.",
|
|
"Approve: the record is accepted and its clauses are in force. Decline: the record stays proposed; nothing of it is in force.",
|
|
"ADR 0343, proposed by fix-367@g14; the knowledge base's own review approved it. Fingerprint a59f 361b f60e cb5e d237 a837.",
|
|
"Read its 13 clauses with the knowledge base's get, number 343.",
|
|
}
|
|
if strings.Join(note, "\n") != strings.Join(want, "\n") {
|
|
t.Errorf("the note:\n%s\nwant:\n%s", m.Note, strings.Join(want, "\n"))
|
|
}
|
|
if n := len(strings.Split(m.Text(), "\n")); n > 9 || len(m.Actions) != 2 {
|
|
t.Errorf("the message is %d lines offering %s:\n%s", n, labelsOf(m), m.Text())
|
|
}
|
|
// Answered, it keeps its headline, summary, points and Details; below them, how it ended and who acts on it;
|
|
// its buttons are gone (issue 714: the one line issue 639 made left nothing to read back).
|
|
if out, err := h.Chosen("telegram", choiceOf(ticketFor(m, "Approve"), "42", c.now())); err != nil || out != "chosen" {
|
|
t.Fatalf("Approve: %q %v", out, err)
|
|
}
|
|
var edited Message
|
|
for _, e := range tg.edits {
|
|
edited = e
|
|
}
|
|
if edited.Title != a.Headline || edited.Body != a.Explanation+"\n"+want[2]+"\n"+want[3] ||
|
|
edited.Note != "You chose Approve at "+asked+" on Telegram.\nTold to mesh-knowledge, which acts on it itself." ||
|
|
len(edited.Actions) != 0 {
|
|
t.Errorf("the answered copy: %q\n%q\n%q", edited.Title, edited.Body, edited.Note)
|
|
}
|
|
}
|
|
|
|
// An ask that does not read at a glance is shown all the same, and said: refusing it would leave the operator
|
|
// unasked, and the bound is its asker's to keep.
|
|
func TestAnAskThatIsAWallOfTextIsShownAndSaid(t *testing.T) {
|
|
h, tg, _, c, store, _ := newRouter(t)
|
|
link(h, store, c, "42", c.now().Add(-2*time.Hour))
|
|
var said []string
|
|
h.Logf = func(f string, a ...any) { said = append(said, f) }
|
|
a := decisionAsk(c)
|
|
a.Explanation += "\nA fourth point\nA fifth point"
|
|
submit(t, h, "mesh-knowledge", a)
|
|
settle(h, c)
|
|
if len(tg.sends) != 1 || !strings.Contains(strings.Join(said, "\n"), "is a wall of text on the phone") {
|
|
t.Errorf("sent %d, said %v", len(tg.sends), said)
|
|
}
|
|
}
|