d60a51c9e7
The table was write-only. Rows were recorded and nothing could show them, so the tests for #368 and #370 had to reach past the store into store.DB — if a test can only see it that way, so can nobody else. A durable record nobody reads answers no question, and why Maven went quiet is supposed to be a query. ListDeliveryAttempts returns recent rows newest first, filtered by status. Status is the filter worth having because the two real questions are "what got dropped" and "what is still pending", and neither is answerable by reading the whole list on a busy day. It reaches mavweb over IPC as DeliveryAttempts. The section goes on /notifications, which already answers "what did she send", rather than on a page of its own. Shared ui.css, the nav partial, the table in div.scroll. A failed outbox read leaves a log line and still renders the nudge list, because half the page beats none of it.
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, "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)
|
|
}
|
|
}
|