Phase 7b: combined brief keeps FR-9 order for every caller (AUD-115)

CombinedText now applies OrderChildren itself, so the CLI preview, the
dashboard and the Hub render Finn's block then Harry's exactly like the
email. Previously `notify preview --combined` showed the DB order (Harry
first). Adds TestCombinedTextKeepsBriefOrder with a reversed input.
This commit is contained in:
2026-10-16 12:39:35 +11:00
parent 11155df1fe
commit 80b54524ea
2 changed files with 35 additions and 0 deletions

View File

@@ -151,6 +151,10 @@ func (s *Service) decoder(ctx context.Context) (func(string) string, error) {
// CombinedText returns the single brief for Jo and Sam — both boys in one
// message — and the children whose scan did not complete.
func (s *Service) CombinedText(ctx context.Context, children []store.Child) (string, []string, error) {
// Finn's block first, then Harry's (FR-9). Enforce the order here, not only in
// the send plan, so every caller — the email, the CLI preview, the dashboard
// and the Hub — renders the combined brief in the same order as the email.
children = OrderChildren(children)
parts := make([]dashboard.CombinedBrief, 0, len(children))
var failed []string
var notices []string

View File

@@ -342,3 +342,34 @@ func TestUnconfiguredRecipientFails(t *testing.T) {
t.Fatal("want an error for no channels")
}
}
// TestCombinedTextKeepsBriefOrder proves FR-9 holds whatever order the caller
// passes: the combined brief is always Finn's block then Harry's, so the CLI
// preview, the dashboard and the Hub match the email that is sent.
func TestCombinedTextKeepsBriefOrder(t *testing.T) {
fake := &fakeApprise{}
srv := fake.server(t)
svc, st := newService(t, srv.URL, seedItems)
children, err := st.ListChildren(context.Background())
if err != nil {
t.Fatalf("children: %v", err)
}
if len(children) != 2 {
t.Fatalf("children = %d, want Harry and Finn", len(children))
}
// Reverse the input on purpose: the email order must not depend on it.
children[0], children[1] = children[1], children[0]
text, failed, err := svc.CombinedText(context.Background(), children)
if err != nil {
t.Fatalf("combined text: %v", err)
}
if len(failed) != 0 {
t.Fatalf("failed = %#v, want none", failed)
}
i, j := strings.Index(text, "=== FINN ==="), strings.Index(text, "=== HARRY ===")
if i < 0 || j < 0 || i > j {
t.Errorf("combined blocks are not Finn then Harry: %q", text)
}
}