35c6ff5a71
Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
85 lines
2.8 KiB
Go
85 lines
2.8 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestDroppedDeliveryAttemptRoundTrips — Vikunja #370. A suppressed nudge is
|
|
// recorded as 'dropped'. The status column has a CHECK constraint, so this
|
|
// only works if migration #12 widened it; a fake outbox in a unit test would
|
|
// not catch that.
|
|
func TestDroppedDeliveryAttemptRoundTrips(t *testing.T) {
|
|
s := newTestStore(t)
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
id, err := s.BeginDeliveryAttempt(ctx, "nudge", "water", 0, "", "drop", "abc123", now)
|
|
if err != nil {
|
|
t.Fatalf("BeginDeliveryAttempt: %v", err)
|
|
}
|
|
if err := s.CompleteDeliveryAttempt(ctx, id, DeliveryDropped, now); err != nil {
|
|
t.Fatalf("CompleteDeliveryAttempt: %v", err)
|
|
}
|
|
|
|
var status string
|
|
err = s.db.QueryRowContext(ctx, `SELECT status FROM delivery_attempts WHERE id = ?`, id).Scan(&status)
|
|
if err != nil {
|
|
t.Fatalf("read back: %v", err)
|
|
}
|
|
if status != DeliveryDropped {
|
|
t.Fatalf("status: want %q, got %q", DeliveryDropped, status)
|
|
}
|
|
}
|
|
|
|
// TestListDeliveryAttempts — the read path the outbox lacked until #390. The
|
|
// two questions it must answer are "what was dropped" and "what is pending".
|
|
func TestListDeliveryAttempts(t *testing.T) {
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
base := time.Date(2026, 8, 4, 9, 0, 0, 0, time.UTC)
|
|
|
|
sent, err := s.BeginDeliveryAttempt(ctx, "nudge", "water", 0, "", "telegram", "h1", base)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.CompleteDeliveryAttempt(ctx, sent, DeliverySent, base.Add(time.Second)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dropped, err := s.BeginDeliveryAttempt(ctx, "nudge", "care", 0, "", "telegram", "h2", base.Add(time.Minute))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.CompleteDeliveryAttempt(ctx, dropped, DeliveryDropped, base.Add(time.Minute)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.BeginDeliveryAttempt(ctx, "reminder", "", 7, "reminder:test", "voice", "h3", base.Add(2*time.Minute)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
all, err := s.ListDeliveryAttempts(ctx, "", 10)
|
|
if err != nil || len(all) != 3 {
|
|
t.Fatalf("ListDeliveryAttempts = %d rows, err=%v, want 3", len(all), err)
|
|
}
|
|
// Newest first.
|
|
if all[0].Kind != "reminder" || all[0].ReminderID != 7 {
|
|
t.Fatalf("newest row is %+v, want the reminder", all[0])
|
|
}
|
|
if all[0].HasComplete {
|
|
t.Fatalf("a pending row must have no completion time: %+v", all[0])
|
|
}
|
|
if !all[2].HasComplete || !all[2].Completed.Equal(base.Add(time.Second)) {
|
|
t.Fatalf("completed row lost its time: %+v", all[2])
|
|
}
|
|
|
|
only, err := s.ListDeliveryAttempts(ctx, DeliveryDropped, 10)
|
|
if err != nil || len(only) != 1 || only[0].Rule != "care" {
|
|
t.Fatalf("dropped filter = %+v, err=%v", only, err)
|
|
}
|
|
pending, err := s.ListDeliveryAttempts(ctx, DeliveryPending, 10)
|
|
if err != nil || len(pending) != 1 || pending[0].Kind != "reminder" {
|
|
t.Fatalf("pending filter = %+v, err=%v", pending, err)
|
|
}
|
|
}
|