Files
Maven/internal/store/ack_test.go
claude 76d123edf3 store: the first repeat-til-ack send, and a digest entry that expired unswept (V-617)
LastSent scanned MAX(sent_at) over an empty ack_sends into a bare int64, so
the ordinary "nothing sent yet" case came back as a scan error rather than the
zero time its doc promises. ack_sends is written only by MarkSent, and MarkSent
runs only after a repeat has gone out, so every rule's FIRST repeat read an
empty table — and RepeatUnacked aborts its whole sweep on that error. The
repeat-til-ack loop could never take its first step. Scans into a NullInt64,
the same way OldestPendingTelegram already does two files over.

EnqueueDigestEntry deduped against any row still marked pending, including one
already past its expires_ts. The tick enqueues before it sweeps, so a suppressed
nudge arriving on the tick after an expiry was told deduped=true against an
entry PendingDigestEntries will never hand back: the caller drops the phrasing
it just paid the LLM for and nothing reaches the bundle. The dedupe now carries
the same expiry test the read side does. Its lookup also stops treating a real
read failure as "nothing there".
2026-08-06 04:50:07 +04:00

61 lines
1.9 KiB
Go

package store
import (
"context"
"testing"
"time"
)
// TestLastSentOnEmptyTableIsZero pins the aggregate-over-nothing trap that
// nudges.go's OldestPendingTelegram already documents: MAX over an empty set is
// one row holding NULL, not zero rows. Scanning that into a bare int64 is an
// error, and the doc on LastSent promises a zero time instead.
//
// It is not a cosmetic promise. ack_sends is written only by MarkSent, and
// MarkSent is called only after a repeat has already gone out, so the first
// repeat for every rule reads an empty table. delivery.Dispatcher.RepeatUnacked
// aborts the whole sweep on that error, which means the repeat-til-ack loop can
// never take its first step for any rule.
func TestLastSentOnEmptyTableIsZero(t *testing.T) {
s := newTestStore(t)
ctx := context.Background()
last, err := s.LastSent(ctx, "service_down")
if err != nil {
t.Fatalf("last sent on an empty table must not error: %v", err)
}
if !last.IsZero() {
t.Fatalf("want the zero time before anything was sent, got %v", last)
}
}
// TestLastSentIsScopedToItsRule — a send for another rule must not answer for
// this one, or the repeat interval is clocked off somebody else's alarm.
func TestLastSentIsScopedToItsRule(t *testing.T) {
s := newTestStore(t)
ctx := context.Background()
at := time.UnixMilli(1_700_000_000_000).UTC()
if err := s.MarkSent(ctx, "other_rule", at); err != nil {
t.Fatalf("mark sent: %v", err)
}
last, err := s.LastSent(ctx, "service_down")
if err != nil {
t.Fatalf("last sent: %v", err)
}
if !last.IsZero() {
t.Fatalf("want zero for a rule with no sends, got %v", last)
}
if err := s.MarkSent(ctx, "service_down", at); err != nil {
t.Fatalf("mark sent: %v", err)
}
last, err = s.LastSent(ctx, "service_down")
if err != nil {
t.Fatalf("last sent: %v", err)
}
if !last.Equal(at) {
t.Fatalf("want %v, got %v", at, last)
}
}