You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestStreamHubSubscribeReceivesPublish(t *testing.T) {
|
||||
hub := NewStreamHub()
|
||||
uid := uuid.New()
|
||||
ch, cancel := hub.Subscribe(uid)
|
||||
defer cancel()
|
||||
|
||||
want := Message{ID: uuid.New(), UserID: uid, Topic: "t", Title: "hi"}
|
||||
hub.Publish(uid, want)
|
||||
|
||||
select {
|
||||
case got := <-ch:
|
||||
if got.ID != want.ID {
|
||||
t.Fatalf("got %v, want %v", got.ID, want.ID)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("did not receive published message")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamHubUnsubscribeStopsDelivery(t *testing.T) {
|
||||
hub := NewStreamHub()
|
||||
uid := uuid.New()
|
||||
ch, cancel := hub.Subscribe(uid)
|
||||
cancel() // immediate unsubscribe closes the channel
|
||||
|
||||
// channel should be closed, draining returns zero value + !ok.
|
||||
if _, ok := <-ch; ok {
|
||||
t.Fatal("expected channel closed after cancel")
|
||||
}
|
||||
// Publishing to an unsubscribed user must not panic.
|
||||
hub.Publish(uid, Message{UserID: uid})
|
||||
}
|
||||
|
||||
func TestStreamHubDropsSlowSubscriber(t *testing.T) {
|
||||
hub := NewStreamHub()
|
||||
uid := uuid.New()
|
||||
ch, cancel := hub.Subscribe(uid)
|
||||
defer cancel()
|
||||
|
||||
// Flood beyond buffer; Publish must not block / deadlock.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for i := 0; i < streamSubBuffer*4; i++ {
|
||||
hub.Publish(uid, Message{UserID: uid, Topic: "flood"})
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("Publish deadlocked on slow subscriber")
|
||||
}
|
||||
// Buffer holds at most streamSubBuffer; the rest were dropped.
|
||||
count := 0
|
||||
for {
|
||||
select {
|
||||
case <-ch:
|
||||
count++
|
||||
default:
|
||||
if count > streamSubBuffer {
|
||||
t.Fatalf("buffered %d > cap %d (drop-on-slow violated)", count, streamSubBuffer)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDispatcherPublishesAfterInsert(t *testing.T) {
|
||||
repo := &fakeRepo{}
|
||||
d := NewDispatcher(repo, newTestLogger())
|
||||
hub := NewStreamHub()
|
||||
d.SetStreamHub(hub)
|
||||
|
||||
uid := uuid.New()
|
||||
ch, cancel := hub.Subscribe(uid)
|
||||
defer cancel()
|
||||
|
||||
if err := d.Dispatch(context.Background(), Message{UserID: uid, Topic: "x", Title: "y"}); err != nil {
|
||||
t.Fatalf("dispatch: %v", err)
|
||||
}
|
||||
select {
|
||||
case got := <-ch:
|
||||
if got.Topic != "x" {
|
||||
t.Fatalf("topic = %q", got.Topic)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("hub did not receive dispatched message")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDispatcherNoPublishOnInsertFailure(t *testing.T) {
|
||||
repo := &fakeRepo{failNext: true}
|
||||
d := NewDispatcher(repo, newTestLogger())
|
||||
hub := NewStreamHub()
|
||||
d.SetStreamHub(hub)
|
||||
|
||||
uid := uuid.New()
|
||||
ch, cancel := hub.Subscribe(uid)
|
||||
defer cancel()
|
||||
|
||||
if err := d.Dispatch(context.Background(), Message{UserID: uid}); err == nil {
|
||||
t.Fatal("expected dispatch error on insert failure")
|
||||
}
|
||||
select {
|
||||
case <-ch:
|
||||
t.Fatal("hub received message despite insert failure")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// expected: no publish.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user