snapshot of https://github.com/ThreeDotsLabs/watermill.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
master / ci (push) Failing after 0s

This commit is contained in:
upstream-mirror
2026-09-15 21:27:46 +05:00
commit ce933d1277
538 changed files with 46504 additions and 0 deletions

View File

@@ -0,0 +1,457 @@
package cqrs_test
import (
"context"
"fmt"
"sync/atomic"
"testing"
"time"
"github.com/ThreeDotsLabs/watermill/components/cqrs"
"github.com/ThreeDotsLabs/watermill/message"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEventGroupProcessorConfig_Validate(t *testing.T) {
testCases := []struct {
Name string
ModifyValidConfig func(*cqrs.EventGroupProcessorConfig)
ExpectedErr error
}{
{
Name: "valid_config",
ModifyValidConfig: nil,
ExpectedErr: nil,
},
{
Name: "valid_with_group_handlers",
ExpectedErr: nil,
},
{
Name: "missing_GroupSubscriberConstructor",
ModifyValidConfig: func(config *cqrs.EventGroupProcessorConfig) {
config.SubscriberConstructor = nil
},
ExpectedErr: fmt.Errorf("missing SubscriberConstructor"),
},
{
Name: "missing_GenerateHandlerGroupSubscribeTopic",
ModifyValidConfig: func(config *cqrs.EventGroupProcessorConfig) {
config.GenerateSubscribeTopic = nil
},
ExpectedErr: fmt.Errorf("missing GenerateHandlerGroupTopic"),
},
{
Name: "missing_marshaler",
ModifyValidConfig: func(config *cqrs.EventGroupProcessorConfig) {
config.Marshaler = nil
},
ExpectedErr: fmt.Errorf("missing Marshaler"),
},
}
for i := range testCases {
tc := testCases[i]
t.Run(tc.Name, func(t *testing.T) {
validConfig := cqrs.EventGroupProcessorConfig{
GenerateSubscribeTopic: func(params cqrs.EventGroupProcessorGenerateSubscribeTopicParams) (string, error) {
return "", nil
},
SubscriberConstructor: func(params cqrs.EventGroupProcessorSubscriberConstructorParams) (message.Subscriber, error) {
return nil, nil
},
Marshaler: cqrs.JSONMarshaler{},
}
if tc.ModifyValidConfig != nil {
tc.ModifyValidConfig(&validConfig)
}
err := validConfig.Validate()
if tc.ExpectedErr == nil {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.ExpectedErr.Error())
}
})
}
}
func TestNewEventProcessor_OnGroupHandle(t *testing.T) {
ts := NewTestServices()
msg1, err := ts.Marshaler.Marshal(&TestEvent{ID: "1"})
require.NoError(t, err)
msg2, err := ts.Marshaler.Marshal(&TestEvent{ID: "2"})
require.NoError(t, err)
mockSub := &mockSubscriber{
MessagesToSend: []*message.Message{
msg1,
msg2,
},
}
handlerCalled := 0
defer func() {
// for msg 1 we are not calling handler - but returning before
assert.Equal(t, 1, handlerCalled)
}()
handler := cqrs.NewEventHandler("test", func(ctx context.Context, cmd *TestEvent) error {
handlerCalled++
return nil
})
onHandleCalled := int64(0)
router, err := message.NewRouter(message.RouterConfig{}, ts.Logger)
require.NoError(t, err)
config := cqrs.EventGroupProcessorConfig{
GenerateSubscribeTopic: func(params cqrs.EventGroupProcessorGenerateSubscribeTopicParams) (string, error) {
return "events", nil
},
SubscriberConstructor: func(params cqrs.EventGroupProcessorSubscriberConstructorParams) (message.Subscriber, error) {
return mockSub, nil
},
OnHandle: func(params cqrs.EventGroupProcessorOnHandleParams) error {
atomic.AddInt64(&onHandleCalled, 1)
assert.Equal(t, "some_group", params.GroupName)
assert.IsType(t, &TestEvent{}, params.Event)
assert.Equal(t, "cqrs_test.TestEvent", params.EventName)
assert.Equal(t, handler, params.Handler)
if params.Event.(*TestEvent).ID == "1" {
assert.Equal(t, msg1, params.Message)
return errors.New("test error")
} else {
assert.Equal(t, msg2, params.Message)
}
return params.Handler.Handle(params.Message.Context(), params.Event)
},
Marshaler: ts.Marshaler,
Logger: ts.Logger,
}
cp, err := cqrs.NewEventGroupProcessorWithConfig(router, config)
require.NoError(t, err)
err = cp.AddHandlersGroup("some_group", handler)
require.NoError(t, err)
go func() {
err := router.Run(context.Background())
assert.NoError(t, err)
}()
<-router.Running()
select {
case <-msg1.Nacked():
// ok
case <-msg1.Acked():
// ack received
t.Fatal("ack received, message should be nacked")
}
select {
case <-msg2.Acked():
// ok
case <-msg2.Nacked():
// nack received
}
assert.EqualValues(t, 2, onHandleCalled)
}
func TestNewEventProcessor_AckOnUnknownEvent_handler_group(t *testing.T) {
ts := NewTestServices()
msg, err := ts.Marshaler.Marshal(&UnknownEvent{})
require.NoError(t, err)
mockSub := &mockSubscriber{
MessagesToSend: []*message.Message{
msg,
},
}
router, err := message.NewRouter(message.RouterConfig{}, ts.Logger)
require.NoError(t, err)
cp, err := cqrs.NewEventGroupProcessorWithConfig(
router,
cqrs.EventGroupProcessorConfig{
GenerateSubscribeTopic: func(params cqrs.EventGroupProcessorGenerateSubscribeTopicParams) (string, error) {
return "events", nil
},
SubscriberConstructor: func(params cqrs.EventGroupProcessorSubscriberConstructorParams) (message.Subscriber, error) {
return mockSub, nil
},
AckOnUnknownEvent: true,
Marshaler: ts.Marshaler,
Logger: ts.Logger,
},
)
require.NoError(t, err)
err = cp.AddHandlersGroup(
"foo",
cqrs.NewEventHandler("test", func(ctx context.Context, cmd *TestEvent) error {
return nil
}),
)
require.NoError(t, err)
go func() {
err := router.Run(context.Background())
assert.NoError(t, err)
}()
<-router.Running()
select {
case <-msg.Acked():
// ok
case <-msg.Nacked():
// ack received
t.Fatal("ack received, message should be nacked")
}
}
func TestNewEventProcessor_AckOnUnknownEvent_disabled_handler_group(t *testing.T) {
ts := NewTestServices()
msg, err := ts.Marshaler.Marshal(&UnknownEvent{})
require.NoError(t, err)
mockSub := &mockSubscriber{
MessagesToSend: []*message.Message{
msg,
},
}
router, err := message.NewRouter(message.RouterConfig{}, ts.Logger)
require.NoError(t, err)
cp, err := cqrs.NewEventGroupProcessorWithConfig(
router,
cqrs.EventGroupProcessorConfig{
GenerateSubscribeTopic: func(params cqrs.EventGroupProcessorGenerateSubscribeTopicParams) (string, error) {
return "events", nil
},
SubscriberConstructor: func(params cqrs.EventGroupProcessorSubscriberConstructorParams) (message.Subscriber, error) {
return mockSub, nil
},
AckOnUnknownEvent: false,
Marshaler: ts.Marshaler,
Logger: ts.Logger,
},
)
require.NoError(t, err)
err = cp.AddHandlersGroup(
"foo",
cqrs.NewEventHandler("test", func(ctx context.Context, cmd *TestEvent) error {
return nil
}),
)
require.NoError(t, err)
go func() {
err := router.Run(context.Background())
assert.NoError(t, err)
}()
<-router.Running()
select {
case <-msg.Nacked():
// ok
case <-msg.Acked():
t.Fatal("ack received, message should be nacked")
}
}
func TestEventProcessor_handler_group(t *testing.T) {
ts := NewTestServices()
event1 := &TestEvent{ID: "1"}
msg1, err := ts.Marshaler.Marshal(event1)
require.NoError(t, err)
event2 := &AnotherTestEvent{ID: "2"}
msg2, err := ts.Marshaler.Marshal(event2)
require.NoError(t, err)
mockSub := &mockSubscriber{
MessagesToSend: []*message.Message{
msg1,
msg2,
},
WaitForAckBeforeSendingNext: true,
}
var handlersCalls []int
handlers := []cqrs.GroupEventHandler{
cqrs.NewGroupEventHandler(func(ctx context.Context, event *TestEvent) error {
assert.EqualValues(t, event1, event)
handlersCalls = append(handlersCalls, 1)
return nil
}),
cqrs.NewGroupEventHandler(func(ctx context.Context, event *AnotherTestEvent) error {
assert.EqualValues(t, event2, event)
handlersCalls = append(handlersCalls, 2)
return nil
}),
cqrs.NewGroupEventHandler(func(ctx context.Context, event *AnotherTestEvent) error {
assert.EqualValues(t, event2, event)
handlersCalls = append(handlersCalls, 3)
return nil
}),
}
router, err := message.NewRouter(message.RouterConfig{}, ts.Logger)
require.NoError(t, err)
eventProcessor, err := cqrs.NewEventGroupProcessorWithConfig(
router,
cqrs.EventGroupProcessorConfig{
GenerateSubscribeTopic: func(params cqrs.EventGroupProcessorGenerateSubscribeTopicParams) (string, error) {
assert.Equal(t, "some_group", params.EventGroupName)
assert.Equal(t, handlers, params.EventGroupHandlers)
return "events", nil
},
SubscriberConstructor: func(params cqrs.EventGroupProcessorSubscriberConstructorParams) (message.Subscriber, error) {
assert.Equal(t, "some_group", params.EventGroupName)
assert.Equal(t, handlers, params.EventGroupHandlers)
return mockSub, nil
},
Marshaler: ts.Marshaler,
Logger: ts.Logger,
},
)
require.NoError(t, err)
err = eventProcessor.AddHandlersGroup(
"some_group",
handlers...,
)
require.NoError(t, err)
err = eventProcessor.AddHandlersGroup(
"some_group",
handlers...,
)
require.ErrorContains(t, err, "event handler group 'some_group' already exists")
err = eventProcessor.AddHandlersGroup(
"some_group_2",
)
require.ErrorContains(t, err, "no handlers provided")
go func() {
err := router.Run(context.Background())
assert.NoError(t, err)
}()
<-router.Running()
select {
case <-msg1.Acked():
// ok
case <-time.After(time.Second):
t.Fatal("message 1 not acked")
}
select {
case <-msg2.Acked():
// ok
case <-time.After(time.Second):
t.Fatal("message 2 not acked")
}
assert.Equal(t, []int{1, 2, 3}, handlersCalls)
}
func TestEventGroupProcessor_original_msg_set_to_ctx(t *testing.T) {
ts := NewTestServices()
msg, err := ts.Marshaler.Marshal(&TestEvent{})
require.NoError(t, err)
mockSub := &mockSubscriber{
MessagesToSend: []*message.Message{
msg,
},
}
router, err := message.NewRouter(message.RouterConfig{}, ts.Logger)
require.NoError(t, err)
cp, err := cqrs.NewEventGroupProcessorWithConfig(
router,
cqrs.EventGroupProcessorConfig{
GenerateSubscribeTopic: func(params cqrs.EventGroupProcessorGenerateSubscribeTopicParams) (string, error) {
return "events", nil
},
SubscriberConstructor: func(params cqrs.EventGroupProcessorSubscriberConstructorParams) (message.Subscriber, error) {
return mockSub, nil
},
AckOnUnknownEvent: true,
Marshaler: ts.Marshaler,
Logger: ts.Logger,
},
)
require.NoError(t, err)
var msgFromCtx *message.Message
err = cp.AddHandlersGroup(
"some_group",
cqrs.NewGroupEventHandler(
func(ctx context.Context, cmd *TestEvent) error {
msgFromCtx = cqrs.OriginalMessageFromCtx(ctx)
return nil
}),
)
require.NoError(t, err)
go func() {
err := router.Run(context.Background())
assert.NoError(t, err)
}()
<-router.Running()
select {
case <-msg.Acked():
// ok
case <-msg.Nacked():
// nack received
t.Fatal("nack received, message should be acked")
case <-time.After(1 * time.Second):
t.Fatal("timeout waiting for ack")
}
require.NotNil(t, msgFromCtx)
assert.Equal(t, msg, msgFromCtx)
}