You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user