yao/event/sub_test.go
Max fd24e31912 Update Makefile for enhanced testing and add new event documentation
- Modify unit test commands in the Makefile to include additional skip patterns for memory leak tests, improving test coverage and accuracy.
- Expand benchmark and memory leak detection to include the event module, ensuring comprehensive testing across all components.
- Add new design and TODO documentation files for the event module to facilitate future development.
2026-02-23 12:18:13 +08:00

181 lines
4.5 KiB
Go

package event_test
import (
"context"
"testing"
"time"
"github.com/yaoapp/yao/event"
"github.com/yaoapp/yao/event/types"
)
// --- Phase 7: Subscriber tests ---
func TestSubscribe_Basic(t *testing.T) {
event.Reset()
defer event.Reset()
event.Register("foo", &recordHandler{})
_ = event.Start()
defer func() { _ = event.Stop(context.Background()) }()
ch := make(chan *types.Event, 10)
subID := event.Subscribe("foo.*", ch)
if subID == "" {
t.Fatal("expected non-empty subscription ID")
}
defer event.Unsubscribe(subID)
_, _ = event.Push(context.Background(), "foo.bar", "payload")
_, _ = event.Push(context.Background(), "foo.baz", "payload2")
received := drainChan(ch, 2, 200*time.Millisecond)
if len(received) != 2 {
t.Fatalf("expected 2 events, got %d", len(received))
}
if received[0].Type != "foo.bar" {
t.Fatalf("expected foo.bar, got %s", received[0].Type)
}
if received[1].Type != "foo.baz" {
t.Fatalf("expected foo.baz, got %s", received[1].Type)
}
}
func TestSubscribe_PatternFilter(t *testing.T) {
event.Reset()
defer event.Reset()
event.Register("foo", &recordHandler{})
event.Register("bar", &recordHandler{})
_ = event.Start()
defer func() { _ = event.Stop(context.Background()) }()
ch := make(chan *types.Event, 10)
subID := event.Subscribe("foo.*", ch, event.Filter(func(ev *types.Event) bool {
return ev.Type == "foo.keep"
}))
defer event.Unsubscribe(subID)
_, _ = event.Push(context.Background(), "foo.keep", nil)
_, _ = event.Push(context.Background(), "foo.drop", nil)
_, _ = event.Push(context.Background(), "bar.thing", nil)
received := drainChan(ch, 1, 200*time.Millisecond)
if len(received) != 1 {
t.Fatalf("expected 1 filtered event, got %d", len(received))
}
if received[0].Type != "foo.keep" {
t.Fatalf("expected foo.keep, got %s", received[0].Type)
}
}
func TestSubscribe_Unsubscribe(t *testing.T) {
event.Reset()
defer event.Reset()
event.Register("foo", &recordHandler{})
_ = event.Start()
defer func() { _ = event.Stop(context.Background()) }()
ch := make(chan *types.Event, 10)
subID := event.Subscribe("foo.*", ch)
_, _ = event.Push(context.Background(), "foo.first", nil)
time.Sleep(50 * time.Millisecond)
event.Unsubscribe(subID)
_, _ = event.Push(context.Background(), "foo.second", nil)
time.Sleep(50 * time.Millisecond)
received := drainChan(ch, 10, 100*time.Millisecond)
for _, ev := range received {
if ev.Type == "foo.second" {
t.Fatal("should not receive events after Unsubscribe")
}
}
}
func TestSubscribe_ChanFull_Skip(t *testing.T) {
event.Reset()
defer event.Reset()
event.Register("foo", &recordHandler{})
_ = event.Start()
defer func() { _ = event.Stop(context.Background()) }()
ch := make(chan *types.Event, 1) // tiny buffer
subID := event.Subscribe("foo.*", ch)
defer event.Unsubscribe(subID)
// Push multiple events quickly; only 1 should fit in buffer
for i := 0; i < 5; i++ {
_, _ = event.Push(context.Background(), "foo.item", i)
}
time.Sleep(100 * time.Millisecond)
// Should have at most 1 in channel (rest skipped)
count := len(ch)
if count > 1 {
t.Fatalf("expected at most 1 buffered event, got %d", count)
}
}
func TestSubscribe_WildcardAll(t *testing.T) {
event.Reset()
defer event.Reset()
event.Register("foo", &recordHandler{})
event.Register("bar", &recordHandler{})
_ = event.Start()
defer func() { _ = event.Stop(context.Background()) }()
ch := make(chan *types.Event, 10)
subID := event.Subscribe("*", ch)
defer event.Unsubscribe(subID)
_, _ = event.Push(context.Background(), "foo.one", nil)
_, _ = event.Push(context.Background(), "bar.two", nil)
received := drainChan(ch, 2, 200*time.Millisecond)
if len(received) != 2 {
t.Fatalf("wildcard * should receive all events, got %d", len(received))
}
}
func TestSubscribe_StopClearsSubscribers(t *testing.T) {
event.Reset()
defer event.Reset()
event.Register("foo", &recordHandler{})
_ = event.Start()
ch := make(chan *types.Event, 10)
_ = event.Subscribe("foo.*", ch)
_ = event.Stop(context.Background())
// After Stop, Push should fail
_, err := event.Push(context.Background(), "foo.bar", nil)
if err != event.ErrNotStarted {
t.Fatalf("expected ErrNotStarted after Stop, got %v", err)
}
}
// drainChan reads up to n events from ch within timeout.
func drainChan(ch chan *types.Event, n int, timeout time.Duration) []*types.Event {
var result []*types.Event
timer := time.NewTimer(timeout)
defer timer.Stop()
for range n {
select {
case ev := <-ch:
result = append(result, ev)
case <-timer.C:
return result
}
}
return result
}