Files
mesh-catalog/modules/mesh-mail-reader/cmd/mesh-mail-reader/tell_test.go
T

169 lines
5.7 KiB
Go

package main
import (
"context"
"testing"
"time"
)
func mattersEntry(sender, summary string) Entry {
return Entry{Account: "bob", SenderName: sender, Summary: summary, Mark: MarkMatters, Date: time.Now()}
}
// recordingCheck and recordingTell let a test see exactly what SendSignal asked, and answer however the
// test needs (1522/AC1, AC2).
func recordingCheck(allow bool) (MessengerCheck, *[]string) {
var asked []string
return func(text string) (bool, error) {
asked = append(asked, text)
return allow, nil
}, &asked
}
func recordingTell(sentOn string) (Tell, *[]string, *[]string) {
var headlines, subjects []string
return func(headline string, lines []string, subject string) (string, error) {
headlines = append(headlines, headline)
subjects = append(subjects, subject)
return sentOn, nil
}, &headlines, &subjects
}
// 1522/AC1: a batch marking mails as mattering asks messenger_check with the headline and lines, then
// sends one tell with subject "mail".
func TestSendSignalChecksWordsThenTellsWithSubjectMail(t *testing.T) {
ctx := context.Background()
store := newMemStore()
id, _ := store.AddEntry(ctx, mattersEntry("Alice", "pay the bill"))
check, asked := recordingCheck(true)
tell, headlines, subjects := recordingTell("2026-10-21T10:00:00Z")
if err := SendSignal(ctx, store, check, tell); err != nil {
t.Fatal(err)
}
if len(*asked) == 0 || (*asked)[0] != "1 mail matters" {
t.Fatalf("messenger_check was asked %v, want the headline first", *asked)
}
if len(*headlines) != 1 || (*headlines)[0] != "1 mail matters" {
t.Fatalf("tell's headlines = %v", *headlines)
}
if len(*subjects) != 1 || (*subjects)[0] != "mail" {
t.Fatalf("tell's subjects = %v, want \"mail\"", *subjects)
}
entry, _, _ := store.Entry(ctx, id)
if !entry.Told {
t.Fatal("the entry was not marked told")
}
}
// 1522/AC1: words messenger_check refuses are replaced by the safe form.
func TestSendSignalFallsBackToTheSafeFormWhenRefused(t *testing.T) {
ctx := context.Background()
store := newMemStore()
store.AddEntry(ctx, mattersEntry("Alice", "pay the bill"))
check, _ := recordingCheck(false)
tell, headlines, _ := recordingTell("sent")
if err := SendSignal(ctx, store, check, tell); err != nil {
t.Fatal(err)
}
if len(*headlines) != 1 {
t.Fatalf("tell called %d time(s), want 1", len(*headlines))
}
}
// 1522/AC2: a tell whose answer has sent_on non-empty marks the mails told; one with sent_on empty leaves
// them untold, and the next mail signal counts them (1429/AC4).
func TestSendSignalSentOnEmptyLeavesMailsUntold(t *testing.T) {
ctx := context.Background()
store := newMemStore()
id, _ := store.AddEntry(ctx, mattersEntry("Alice", "pay the bill"))
check, _ := recordingCheck(true)
tellDropped, calls, _ := recordingTell("") // every channel dropped it
if err := SendSignal(ctx, store, check, tellDropped); err != nil {
t.Fatal(err)
}
entry, _, _ := store.Entry(ctx, id)
if entry.Told {
t.Fatal("the entry was marked told even though sent_on was empty")
}
if len(*calls) != 1 {
t.Fatalf("tell called %d time(s)", len(*calls))
}
// The next signal counts the still-untold mail (1429/AC4): SendSignal asks about it again.
tellSent, headlines, _ := recordingTell("sent-now")
if err := SendSignal(ctx, store, check, tellSent); err != nil {
t.Fatal(err)
}
if len(*headlines) != 1 {
t.Fatalf("the second signal was not sent")
}
entry, _, _ = store.Entry(ctx, id)
if !entry.Told {
t.Fatal("the entry was still not marked told after the second, successful signal")
}
}
// 1522/AC3: a batch whose only marks are scam warnings sends no tell.
func TestSendSignalSendsNothingForScamWarningsAlone(t *testing.T) {
ctx := context.Background()
store := newMemStore()
store.AddEntry(ctx, Entry{Account: "bob", Mark: MarkScamWarning, SenderName: "Eve", Summary: "a scam"})
check, asked := recordingCheck(true)
tell, headlines, _ := recordingTell("sent")
if err := SendSignal(ctx, store, check, tell); err != nil {
t.Fatal(err)
}
if len(*asked) != 0 || len(*headlines) != 0 {
t.Fatalf("messenger_check asked %v, tell called %d time(s); want neither for a scam-only batch", *asked, len(*headlines))
}
}
// 1522/AC3: no tell while the reader is unhealthy; the untold mails are counted by the first signal after
// recovery.
func TestSendSignalSendsNothingWhileUnhealthy(t *testing.T) {
ctx := context.Background()
store := newMemStore()
id, _ := store.AddEntry(ctx, mattersEntry("Alice", "pay the bill"))
store.SetReaderHealth(ctx, ReaderHealth{Healthy: false, Cause: OutcomeRefused, Why: "holds no licence"})
check, asked := recordingCheck(true)
tell, headlines, _ := recordingTell("sent")
if err := SendSignal(ctx, store, check, tell); err != nil {
t.Fatal(err)
}
if len(*asked) != 0 || len(*headlines) != 0 {
t.Fatalf("messenger_check asked %v, tell called %d time(s); want neither while unhealthy", *asked, len(*headlines))
}
// After recovery, the first signal counts the mail that went untold while unhealthy.
store.SetReaderHealth(ctx, ReaderHealth{Healthy: true})
if err := SendSignal(ctx, store, check, tell); err != nil {
t.Fatal(err)
}
if len(*headlines) != 1 {
t.Fatalf("after recovery, tell called %d time(s), want 1", len(*headlines))
}
entry, _, _ := store.Entry(ctx, id)
if !entry.Told {
t.Fatal("the entry was not marked told after recovery")
}
}
// 1522/AC4: the manifest invokes seat:operator-channel.tell and messenger.messenger_check.
func TestTheManifestInvokesTellAndMessengerCheck(t *testing.T) {
m, _ := readManifest(t)
found := map[string]bool{}
for _, inv := range m.Invokes {
found[inv] = true
}
for _, want := range []string{"seat:operator-channel.tell", "messenger.messenger_check"} {
if !found[want] {
t.Fatalf("invokes %v, want it to include %q", m.Invokes, want)
}
}
}