package main import ( "strings" "testing" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" ) // adr0343 is the ask for ADR 0343 as the router now sends it to this channel (novox/hq issue 639): the knowledge // base's brief as its title and body, the rest in its note. On 2026-10-13 the same ask arrived as about thirty plain // lines — every one of its thirteen clauses on a line of its own — and the operator would not read it. var adr0343 = oc.Message{Handle: "h-343", To: "42", Title: "Your agents' memory follows you to every machine", Body: "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", Note: "Asked by mesh-knowledge at 16:05. Unanswered by tomorrow 15:55: the record stays proposed; nothing of it is in force.\n" + "Approve: the record is accepted and its clauses are in force. Decline: the record stays proposed; nothing of it is in force.\n" + "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.", Buttons: []oc.Button{{Label: "Approve", Ticket: "t-approve"}, {Label: "Decline", Ticket: "t-decline"}}} // The ADR 0343 ask fits a phone's glance: a bold headline, the summary and its points, then the rest apart and // collapsed — at most twelve lines in all, and never a clause. func TestTheADR0343AskFitsAGlance(t *testing.T) { c, f, _, _ := newChannel(t, goodToken) subject, body := work(occ.Show, adr0343) if err := c.Take(subject, body); err != nil { t.Fatal(err) } sent := f.of("sendMessage") if len(sent) != 1 { t.Fatalf("sent %d", len(sent)) } html := sent[0].Body["text"].(string) t.Logf("as sent:\n%s", html) lines := strings.Split(html, "\n") if len(lines) > 12 { t.Errorf("the ask is %d lines, more than 12:\n%s", len(lines), html) } if lines[0] != "Your agents' memory follows you to every machine" && lines[0] != "Your agents' memory follows you to every machine" { t.Errorf("the headline is not first and bold: %q", lines[0]) } if !strings.HasPrefix(lines[1], "What an agent learns") || strings.Count(html, "\n• ") != 3 { t.Errorf("the summary and its three points do not follow the headline:\n%s", html) } if !strings.Contains(html, "\n\n
Asked by mesh-knowledge at 16:05.") || !strings.HasSuffix(html, "number 343.") { t.Errorf("the note is not apart and collapsed:\n%s", html) } for _, lead := range []string{"Only the memory folders are carried", "How it is checked", "13 clause(s)", "**"} { if strings.Contains(html, lead) { t.Errorf("the ask carries %q", lead) } } if sent[0].Body["parse_mode"] != "HTML" { t.Errorf("not sent as HTML: %v", sent[0].Body["parse_mode"]) } } // The note is kept whole when the message is too long: the body is cut first and the cut said; only a note that // cannot fit beside the title is cut, and that said too (the review of mesh-catalog #278). func TestALongBodyIsCutBeforeTheNote(t *testing.T) { m := oc.Message{Title: "t", Body: strings.Repeat("b", 5000), Note: "+ shares: media=/storage/media\nFingerprint 1234."} s := partsOf(m) if s.note != m.Note || !strings.HasSuffix(s.body, cutNote) || len([]rune(s.plain())) != MaxLength { t.Errorf("note %q, body ends %q, %d characters", s.note, s.body[len(s.body)-20:], len([]rune(s.plain()))) } m.Body, m.Note = "the summary", strings.Repeat("n", 5000) s = partsOf(m) if !strings.HasSuffix(s.note, cutNote) || !strings.Contains(s.body, "cut here") || len([]rune(s.plain())) != MaxLength { t.Errorf("body %q, note ends %q, %d characters", s.body, s.note[len(s.note)-20:], len([]rune(s.plain()))) } } // A '<', '>' or '&' in the note is escaped like any other part: it neither breaks the blockquote nor injects. func TestTheNoteIsEscaped(t *testing.T) { got := partsOf(oc.Message{Title: "t", Note: "Asked by x & y "}).html() want := "t\n\n
Asked by <b>x</b> & y </blockquote><i>" if got != want { t.Errorf("%q", got) } } // novox/hq issue 714: an answered ask keeps its question whole, with how it ended below it. Over Telegram's bound, // the end of the body (the asker's Details) is cut; the headline and the answer are never. func TestAnAnsweredAskOverTheBoundKeepsItsHeadlineAndItsAnswer(t *testing.T) { answer := "You chose Approve at 20:03 on Telegram.\nTold to mesh-knowledge, which acts on it itself." m := oc.Message{Handle: "h-714", To: "42", Title: "Fixes reach every machine at once, unchecked", Body: "A fix goes to every machine in one step.\n• No machine goes first\n" + strings.Repeat("Details line. ", 400), Note: answer} s := partsOf(m) if n := len([]rune(s.plain())); n > MaxLength { t.Fatalf("the answered ask is %d characters, over %d", n, MaxLength) } if s.title != m.Title || s.note != answer || !strings.HasPrefix(s.body, "A fix goes to every machine in one step.\n• No machine goes first") || !strings.HasSuffix(s.body, cutNote) { t.Errorf("the headline, the summary or the answer was cut:\n%s", s.plain()) } }