You've already forked agentic-coding-workflow
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
package acp_test
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/yan1h/agent-coding-workflow/internal/acp"
|
|
)
|
|
|
|
func TestTurnBus_FanoutToMultipleSubscribers(t *testing.T) {
|
|
t.Parallel()
|
|
bus := acp.NewTurnBus(nil)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
var a, b atomic.Int32
|
|
bus.Subscribe("a", func(_ context.Context, _ acp.TurnEvent) { a.Add(1); wg.Done() })
|
|
bus.Subscribe("b", func(_ context.Context, _ acp.TurnEvent) { b.Add(1); wg.Done() })
|
|
|
|
bus.Publish(context.Background(), acp.TurnEvent{SessionID: uuid.New(), Kind: acp.TurnCompletedEvent})
|
|
|
|
done := make(chan struct{})
|
|
go func() { wg.Wait(); close(done) }()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("subscribers not invoked in time")
|
|
}
|
|
assert.Equal(t, int32(1), a.Load())
|
|
assert.Equal(t, int32(1), b.Load())
|
|
}
|
|
|
|
func TestTurnBus_PanicIsolatedAndNonBlocking(t *testing.T) {
|
|
t.Parallel()
|
|
bus := acp.NewTurnBus(nil)
|
|
|
|
var good atomic.Int32
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
bus.Subscribe("panicky", func(_ context.Context, _ acp.TurnEvent) { panic("boom") })
|
|
bus.Subscribe("good", func(_ context.Context, _ acp.TurnEvent) { good.Add(1); wg.Done() })
|
|
|
|
// Publish 必须立即返回(非阻塞),且 panicky 订阅者的 panic 被 recover 隔离,
|
|
// 不影响 good 订阅者执行。
|
|
bus.Publish(context.Background(), acp.TurnEvent{Kind: acp.SessionIdleEvent})
|
|
|
|
done := make(chan struct{})
|
|
go func() { wg.Wait(); close(done) }()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("good subscriber not invoked despite panicky peer")
|
|
}
|
|
assert.Equal(t, int32(1), good.Load())
|
|
}
|
|
|
|
func TestTurnBus_PublishDoesNotBlockOnSlowSubscriber(t *testing.T) {
|
|
t.Parallel()
|
|
bus := acp.NewTurnBus(nil)
|
|
release := make(chan struct{})
|
|
bus.Subscribe("slow", func(_ context.Context, _ acp.TurnEvent) { <-release })
|
|
|
|
start := time.Now()
|
|
bus.Publish(context.Background(), acp.TurnEvent{Kind: acp.SessionIdleEvent})
|
|
// Publish 应远快于订阅者的阻塞时长。
|
|
require.Less(t, time.Since(start), 500*time.Millisecond, "Publish must not block on slow subscriber")
|
|
close(release)
|
|
}
|