Reverts 45b6c347, which kept nothing of such a message and raised an aged-out
condition: that departed from an accepted decision on a lead's word alone. The
concern behind it (the record keeps the subject, with no age) is its own issue.
70 lines
3.1 KiB
Go
70 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/conditions"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// **A dead letter that ages out clears its condition and says so** (novox/hq ADR 0376 §6), on a fake clock: while
|
|
// DEAD_LETTERS holds a restricted stream's messages for a consumer its max-deliveries condition is open; once they
|
|
// have aged out it clears, with words saying how many aged out undelivered and from which stream. One dropped before
|
|
// its age is not counted as aged out, and a consumer of EVENTS clears as before.
|
|
func TestADeadLetterThatAgesOutClearsItsConditionAndSaysSo(t *testing.T) {
|
|
now := time.Date(2026, 10, 15, 12, 0, 0, 0, time.UTC)
|
|
store := conditions.NewInMemory()
|
|
told := &conditions.Told{}
|
|
k := conditions.NewKeeper(t.Context(), conditions.Options{Store: store, History: store, Teller: told,
|
|
Now: func() time.Time { return now }})
|
|
w := &watchdogs{keeper: k, started: now.Add(-time.Hour)}
|
|
|
|
look := func(at time.Time, held map[string]int, aging map[string][]link.Aging) {
|
|
f := calm(at)
|
|
f.deadLetters, f.deadLettersAging = held, aging
|
|
w.see(t.Context(), f)
|
|
}
|
|
mail := "MAIL.ace_mailer"
|
|
look(now, map[string]int{mail: 3, "EVENTS.media_sonarr": 1}, map[string][]link.Aging{mail: {
|
|
{ID: 1, AgesOut: now.Add(time.Hour)}, {ID: 2, AgesOut: now.Add(2 * time.Hour)}, {ID: 3, AgesOut: now.Add(3 * time.Hour)}}})
|
|
if open, _ := k.Open(t.Context()); len(open) != 2 {
|
|
t.Fatalf("open: %+v", open)
|
|
}
|
|
// One dropped by a person before its age: not aged out.
|
|
look(now.Add(30*time.Minute), map[string]int{mail: 2, "EVENTS.media_sonarr": 1}, map[string][]link.Aging{mail: {
|
|
{ID: 2, AgesOut: now.Add(2 * time.Hour)}, {ID: 3, AgesOut: now.Add(3 * time.Hour)}}})
|
|
look(now.Add(150*time.Minute), map[string]int{mail: 1}, map[string][]link.Aging{mail: {{ID: 3, AgesOut: now.Add(3 * time.Hour)}}})
|
|
open, _ := k.Open(t.Context())
|
|
if len(open) != 1 || open[0].Key != conditions.Key(conditions.ScopeBus, mail, link.AdvisoryMaxDeliveries) {
|
|
t.Fatalf("after one aged out: %+v", open)
|
|
}
|
|
look(now.Add(4*time.Hour), map[string]int{}, map[string][]link.Aging{})
|
|
if open, _ := k.Open(t.Context()); len(open) != 0 {
|
|
t.Fatalf("after all aged out: %+v", open)
|
|
}
|
|
k.Close(context.Background()) // everything said is said once it closes
|
|
var mailCleared, sonarrCleared string
|
|
for _, e := range told.Said() {
|
|
if e.Change != conditions.ChangeCleared {
|
|
continue
|
|
}
|
|
switch e.Condition.Key {
|
|
case conditions.Key(conditions.ScopeBus, mail, link.AdvisoryMaxDeliveries):
|
|
mailCleared = e.Condition.Resolved
|
|
case conditions.Key(conditions.ScopeBus, "EVENTS.media_sonarr", link.AdvisoryMaxDeliveries):
|
|
sonarrCleared = e.Condition.Resolved
|
|
}
|
|
}
|
|
if mailCleared != "Resolved: 2 messages of the stream MAIL aged out undelivered" {
|
|
t.Fatalf("MAIL's condition cleared with %q", mailCleared)
|
|
}
|
|
if sonarrCleared != "Resolved: the messages it gave up on were delivered again or dropped" {
|
|
t.Fatalf("an EVENTS consumer's condition cleared with %q", sonarrCleared)
|
|
}
|
|
if why, ok := conditions.Plain(mailCleared); !ok {
|
|
t.Fatalf("not plain: %s", why)
|
|
}
|
|
}
|