2026-04-26 07:36:03 +00:00
|
|
|
package events
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestPublishDeliversToMatchingSubscriber(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
bus := NewBus()
|
|
|
|
|
defer closeBus(t, bus)
|
|
|
|
|
|
|
|
|
|
_, ch, err := bus.Channel().OfKind(KindAgentTurnStart).SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "turn-starts", Buffer: 1},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unmatched := bus.Publish(context.Background(), Event{Kind: KindAgentTurnEnd})
|
|
|
|
|
if unmatched.Matched != 0 || unmatched.Delivered != 0 {
|
|
|
|
|
t.Fatalf("unmatched Publish = %+v, want no delivery", unmatched)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := bus.Publish(context.Background(), Event{Kind: KindAgentTurnStart})
|
|
|
|
|
if result.Matched != 1 || result.Delivered != 1 || result.Dropped != 0 {
|
|
|
|
|
t.Fatalf("Publish = %+v, want one delivered event", result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
evt := receiveEvent(t, ch)
|
|
|
|
|
if evt.Kind != KindAgentTurnStart {
|
|
|
|
|
t.Fatalf("event kind = %q, want %q", evt.Kind, KindAgentTurnStart)
|
|
|
|
|
}
|
|
|
|
|
if evt.ID == "" {
|
|
|
|
|
t.Fatal("event ID is empty")
|
|
|
|
|
}
|
|
|
|
|
if evt.Time.IsZero() {
|
|
|
|
|
t.Fatal("event Time is zero")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDropNewestIncrementsStats(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
bus := NewBus()
|
|
|
|
|
defer closeBus(t, bus)
|
|
|
|
|
|
|
|
|
|
sub, _, err := bus.Channel().SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "drop-newest", Buffer: 1, Backpressure: DropNewest},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
first := bus.Publish(context.Background(), Event{Kind: KindAgentTurnStart})
|
|
|
|
|
if first.Delivered != 1 || first.Dropped != 0 {
|
|
|
|
|
t.Fatalf("first Publish = %+v, want one delivered event", first)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
second := bus.Publish(context.Background(), Event{Kind: KindAgentTurnEnd})
|
|
|
|
|
if second.Delivered != 0 || second.Dropped != 1 {
|
|
|
|
|
t.Fatalf("second Publish = %+v, want one dropped event", second)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if got := sub.Stats().Dropped; got != 1 {
|
|
|
|
|
t.Fatalf("subscription dropped = %d, want 1", got)
|
|
|
|
|
}
|
|
|
|
|
if got := bus.Stats().Dropped; got != 1 {
|
|
|
|
|
t.Fatalf("bus dropped = %d, want 1", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDropOldestKeepsNewestEvent(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
bus := NewBus()
|
|
|
|
|
defer closeBus(t, bus)
|
|
|
|
|
|
|
|
|
|
sub, ch, err := bus.Channel().SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "drop-oldest", Buffer: 1, Backpressure: DropOldest},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bus.Publish(context.Background(), Event{Kind: Kind("test.old"), Payload: "old"})
|
|
|
|
|
result := bus.Publish(context.Background(), Event{Kind: Kind("test.new"), Payload: "new"})
|
|
|
|
|
if result.Delivered != 1 || result.Dropped != 1 {
|
|
|
|
|
t.Fatalf("Publish = %+v, want replacement delivery", result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
evt := receiveEvent(t, ch)
|
|
|
|
|
if evt.Payload != "new" {
|
|
|
|
|
t.Fatalf("payload = %v, want new", evt.Payload)
|
|
|
|
|
}
|
|
|
|
|
if got := sub.Stats().Dropped; got != 1 {
|
|
|
|
|
t.Fatalf("subscription dropped = %d, want 1", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestBlockRespectsContext(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
bus := NewBus()
|
|
|
|
|
defer closeBus(t, bus)
|
|
|
|
|
|
|
|
|
|
_, _, err := bus.Channel().SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "block", Buffer: 1, Backpressure: Block},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
first := bus.Publish(context.Background(), Event{Kind: Kind("test.first")})
|
|
|
|
|
if first.Delivered != 1 {
|
|
|
|
|
t.Fatalf("first Publish = %+v, want one delivered event", first)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
second := bus.Publish(ctx, Event{Kind: Kind("test.second")})
|
|
|
|
|
if second.Blocked != 1 || second.Dropped != 1 || second.Delivered != 0 {
|
|
|
|
|
t.Fatalf("second Publish = %+v, want one blocked drop", second)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 05:09:03 +00:00
|
|
|
func TestPublishNonBlockingDropsForFullBlockSubscriber(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
bus := NewBus()
|
|
|
|
|
defer closeBus(t, bus)
|
|
|
|
|
|
|
|
|
|
sub, _, err := bus.Channel().SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "block", Buffer: 1, Backpressure: Block},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
first := bus.PublishNonBlocking(Event{Kind: Kind("test.first")})
|
|
|
|
|
if first.Delivered != 1 {
|
|
|
|
|
t.Fatalf("first PublishNonBlocking = %+v, want one delivered event", first)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resultCh := make(chan PublishResult, 1)
|
|
|
|
|
go func() {
|
|
|
|
|
resultCh <- bus.PublishNonBlocking(Event{Kind: Kind("test.second")})
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case second := <-resultCh:
|
|
|
|
|
if second.Matched != 1 || second.Delivered != 0 || second.Dropped != 1 || second.Blocked != 0 {
|
|
|
|
|
t.Fatalf("second PublishNonBlocking = %+v, want non-blocking drop", second)
|
|
|
|
|
}
|
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
|
|
|
t.Fatal("PublishNonBlocking blocked on full Block subscriber")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if got := sub.Stats().Dropped; got != 1 {
|
|
|
|
|
t.Fatalf("subscription dropped = %d, want 1", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestStatsSubscribersKeepPriorityOrder(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
bus := NewBus()
|
|
|
|
|
defer closeBus(t, bus)
|
|
|
|
|
|
|
|
|
|
low, _, err := bus.Channel().SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "low", Priority: -1},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan low failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
high, _, err := bus.Channel().SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "high", Priority: 10},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan high failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
peer, _, err := bus.Channel().SubscribeChan(
|
|
|
|
|
context.Background(),
|
|
|
|
|
SubscribeOptions{Name: "peer", Priority: 10},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("SubscribeChan peer failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stats := bus.Stats()
|
|
|
|
|
got := []string{
|
|
|
|
|
stats.SubscriberStats[0].Name,
|
|
|
|
|
stats.SubscriberStats[1].Name,
|
|
|
|
|
stats.SubscriberStats[2].Name,
|
|
|
|
|
}
|
|
|
|
|
want := []string{"high", "peer", "low"}
|
|
|
|
|
if got[0] != want[0] || got[1] != want[1] || got[2] != want[2] {
|
|
|
|
|
t.Fatalf("subscriber order = %v, want %v", got, want)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := high.Close(); err != nil {
|
|
|
|
|
t.Fatalf("Close high failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stats = bus.Stats()
|
|
|
|
|
got = []string{
|
|
|
|
|
stats.SubscriberStats[0].Name,
|
|
|
|
|
stats.SubscriberStats[1].Name,
|
|
|
|
|
}
|
|
|
|
|
want = []string{"peer", "low"}
|
|
|
|
|
if got[0] != want[0] || got[1] != want[1] {
|
|
|
|
|
t.Fatalf("subscriber order after unsubscribe = %v, want %v", got, want)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := peer.Close(); err != nil {
|
|
|
|
|
t.Fatalf("Close peer failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if err := low.Close(); err != nil {
|
|
|
|
|
t.Fatalf("Close low failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 07:36:03 +00:00
|
|
|
func receiveEvent(t *testing.T, ch <-chan Event) Event {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case evt, ok := <-ch:
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatal("event channel closed before receive")
|
|
|
|
|
}
|
|
|
|
|
return evt
|
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
|
t.Fatal("timed out waiting for event")
|
|
|
|
|
return Event{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func closeBus(t *testing.T, bus *EventBus) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
if err := bus.Close(); err != nil {
|
|
|
|
|
t.Fatalf("Close failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|