122 lines
3.3 KiB
Go
122 lines
3.3 KiB
Go
package notify
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestPutAndDrainDelivers(t *testing.T) {
|
|
s := NewStore()
|
|
n := s.Put("alice", "bob", map[string]any{"text": "hi"}, false, 0)
|
|
if n.Trace == "" {
|
|
t.Fatal("Put should assign a trace id")
|
|
}
|
|
if s.PendingCount("bob") != 1 {
|
|
t.Fatalf("pending for bob = %d, want 1", s.PendingCount("bob"))
|
|
}
|
|
|
|
got := s.Drain("bob")
|
|
if len(got) != 1 || got[0].Trace != n.Trace {
|
|
t.Fatalf("Drain = %+v, want the queued notification", got)
|
|
}
|
|
if !got[0].Delivered {
|
|
t.Fatal("drained notification should be marked delivered")
|
|
}
|
|
if s.PendingCount("bob") != 0 {
|
|
t.Fatal("pending should be empty after drain")
|
|
}
|
|
|
|
// Status still works after delivery.
|
|
st, ok := s.Status(n.Trace)
|
|
if !ok || !st.Delivered {
|
|
t.Fatalf("Status after drain = %+v, ok=%v", st, ok)
|
|
}
|
|
}
|
|
|
|
func TestExpiryDropsNotification(t *testing.T) {
|
|
s := NewStore()
|
|
now := time.Unix(1000, 0)
|
|
s.SetClock(func() time.Time { return now })
|
|
|
|
s.Put("srv", "bob", "payload", false, 5*time.Second)
|
|
|
|
// Advance past expiry; the queued message must not be delivered.
|
|
now = now.Add(10 * time.Second)
|
|
if got := s.Drain("bob"); len(got) != 0 {
|
|
t.Fatalf("expired notification was delivered: %+v", got)
|
|
}
|
|
if removed := s.PurgeExpired(); removed != 0 {
|
|
// Drain already dropped it from byTrace; nothing left to purge.
|
|
t.Fatalf("PurgeExpired removed %d, want 0 (already dropped on drain)", removed)
|
|
}
|
|
}
|
|
|
|
func TestPurgeExpiredReclaimsUndeliveredTraces(t *testing.T) {
|
|
s := NewStore()
|
|
now := time.Unix(0, 0)
|
|
s.SetClock(func() time.Time { return now })
|
|
|
|
// A target that never connects: its messages must still be reclaimed.
|
|
s.Put("srv", "ghost", "a", false, time.Second)
|
|
s.Put("srv", "ghost", "b", false, time.Second)
|
|
|
|
now = now.Add(2 * time.Second)
|
|
removed := s.PurgeExpired()
|
|
if removed != 2 {
|
|
t.Fatalf("PurgeExpired removed %d, want 2", removed)
|
|
}
|
|
if s.PendingCount("ghost") != 0 {
|
|
t.Fatal("expired pending queue should be gone")
|
|
}
|
|
}
|
|
|
|
func TestPerTargetCapEvictsOldest(t *testing.T) {
|
|
s := NewStore()
|
|
s.maxPerTarget = 3
|
|
|
|
var traces []string
|
|
for i := 0; i < 5; i++ {
|
|
traces = append(traces, s.Put("srv", "bob", i, false, 0).Trace)
|
|
}
|
|
if s.PendingCount("bob") != 3 {
|
|
t.Fatalf("pending = %d, want capped at 3", s.PendingCount("bob"))
|
|
}
|
|
// The two oldest must have been evicted from the trace index too.
|
|
if _, ok := s.Status(traces[0]); ok {
|
|
t.Fatal("oldest notification should have been evicted")
|
|
}
|
|
if _, ok := s.Status(traces[4]); !ok {
|
|
t.Fatal("newest notification should be retained")
|
|
}
|
|
}
|
|
|
|
func TestSuitReply(t *testing.T) {
|
|
s := NewStore()
|
|
n := s.Put("srv", "bob", "question", true, 0)
|
|
|
|
// A non-suit reply target must fail.
|
|
plain := s.Put("srv", "bob", "fyi", false, 0)
|
|
if _, ok := s.Reply(plain.Trace, "nope"); ok {
|
|
t.Fatal("replying to a non-suit notification should fail")
|
|
}
|
|
|
|
updated, ok := s.Reply(n.Trace, map[string]any{"answer": 42})
|
|
if !ok || !updated.Replied {
|
|
t.Fatalf("Reply = %+v ok=%v", updated, ok)
|
|
}
|
|
st, _ := s.Status(n.Trace)
|
|
if !st.Replied {
|
|
t.Fatal("status should reflect the reply")
|
|
}
|
|
if m, _ := st.Reply.(map[string]any); m["answer"] != 42 {
|
|
t.Fatalf("stored reply = %v", st.Reply)
|
|
}
|
|
}
|
|
|
|
func TestUnknownTraceStatus(t *testing.T) {
|
|
s := NewStore()
|
|
if _, ok := s.Status("does-not-exist"); ok {
|
|
t.Fatal("unknown trace should not resolve")
|
|
}
|
|
}
|