mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-16 04:41:44 +00:00
415 lines
12 KiB
Go
415 lines
12 KiB
Go
package messaging
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"github.com/Azure/go-amqp"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
type amqpConnMock struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *amqpConnMock) Done() <-chan struct{} {
|
|
args := m.Called()
|
|
return args.Get(0).(<-chan struct{})
|
|
}
|
|
|
|
func (m *amqpConnMock) NewSession(ctx context.Context, opts *amqp.SessionOptions) (amqpSession, error) {
|
|
args := m.Called(ctx, opts)
|
|
return args.Get(0).(amqpSession), args.Error(1)
|
|
}
|
|
|
|
func (m *amqpConnMock) Close() error {
|
|
args := m.Called()
|
|
return args.Error(0)
|
|
}
|
|
|
|
var _ amqpConn = (*amqpConnMock)(nil)
|
|
|
|
type amqpDialMock struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *amqpDialMock) Dial(ctx context.Context, addr string, opts *amqp.ConnOptions) (amqpConn, error) {
|
|
args := m.Called(ctx, addr, opts)
|
|
return args.Get(0).(amqpConn), args.Error(1)
|
|
}
|
|
|
|
var _ amqpDial = (*amqpDialMock)(nil)
|
|
|
|
type amqpSessionMock struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *amqpSessionMock) NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (amqpSender, error) {
|
|
args := m.Called(ctx, target, opts)
|
|
return args.Get(0).(amqpSender), args.Error(1)
|
|
}
|
|
|
|
func (m *amqpSessionMock) Close(ctx context.Context) error {
|
|
args := m.Called(ctx)
|
|
return args.Error(0)
|
|
}
|
|
|
|
var _ amqpSession = (*amqpSessionMock)(nil)
|
|
|
|
func Test_AmqpConnection_IsClosed(t *testing.T) {
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
|
return channel
|
|
}
|
|
|
|
t.Run("is closed - connection nil", func(t *testing.T) {
|
|
assert.True(t, connection.IsClosed())
|
|
})
|
|
|
|
t.Run("is closed", func(t *testing.T) {
|
|
channel := make(chan struct{})
|
|
close(channel)
|
|
amqpConnMock := &amqpConnMock{}
|
|
amqpConnMock.On("Done").Return(channelReceiver(channel))
|
|
connection.conn = amqpConnMock
|
|
|
|
assert.True(t, connection.IsClosed())
|
|
})
|
|
|
|
t.Run("is not closed", func(t *testing.T) {
|
|
channel := make(chan struct{})
|
|
amqpConnMock := &amqpConnMock{}
|
|
amqpConnMock.On("Done").Return(channelReceiver(channel))
|
|
connection.conn = amqpConnMock
|
|
|
|
assert.False(t, connection.IsClosed())
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnection_Close(t *testing.T) {
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
t.Run("already closed", func(t *testing.T) {
|
|
assert.NoError(t, connection.Close())
|
|
})
|
|
|
|
t.Run("close error", func(t *testing.T) {
|
|
err := errors.New("test error")
|
|
|
|
amqpConnMock := &amqpConnMock{}
|
|
amqpConnMock.On("Close").Return(err)
|
|
connection.conn = amqpConnMock
|
|
|
|
assert.EqualError(t, connection.Close(), "close: internal connection close: test error")
|
|
assert.NotNil(t, connection.conn)
|
|
amqpConnMock.AssertNumberOfCalls(t, "Close", 1)
|
|
})
|
|
|
|
t.Run("close without error", func(t *testing.T) {
|
|
amqpConnMock := &amqpConnMock{}
|
|
amqpConnMock.On("Close").Return(nil)
|
|
connection.conn = amqpConnMock
|
|
|
|
assert.Nil(t, connection.Close())
|
|
assert.Nil(t, connection.conn)
|
|
amqpConnMock.AssertNumberOfCalls(t, "Close", 1)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnection_Connect(t *testing.T) {
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
t.Run("already connected", func(t *testing.T) {
|
|
connection.conn = &amqpConnMock{}
|
|
assert.NoError(t, connection.Connect())
|
|
})
|
|
|
|
t.Run("dial error", func(t *testing.T) {
|
|
connection.conn = nil
|
|
connection.username = "user"
|
|
connection.password = "pass"
|
|
|
|
amqpDialMock := &amqpDialMock{}
|
|
var c *amqpConnMock = nil
|
|
amqpDialMock.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error"))
|
|
connection.dialer = amqpDialMock
|
|
|
|
assert.EqualError(t, connection.Connect(), "internal connection connect: dial: test error")
|
|
assert.Nil(t, connection.conn)
|
|
})
|
|
|
|
t.Run("connect without error", func(t *testing.T) {
|
|
connection.conn = nil
|
|
|
|
amqpDialMock := &amqpDialMock{}
|
|
amqpConn := &amqpConnMock{}
|
|
amqpDialMock.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(amqpConn, nil)
|
|
connection.dialer = amqpDialMock
|
|
|
|
assert.NoError(t, connection.Connect())
|
|
assert.Equal(t, amqpConn, connection.conn)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnection_ResetConnection(t *testing.T) {
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
t.Run("reset connection - connect on first attempt", func(t *testing.T) {
|
|
connection.conn = nil
|
|
|
|
dialer := &amqpDialMock{}
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(&amqpConnMock{}, nil)
|
|
connection.dialer = dialer
|
|
|
|
assert.NoError(t, connection.ResetConnection(context.Background()))
|
|
assert.NotNil(t, connection.conn)
|
|
|
|
dialer.AssertNumberOfCalls(t, "Dial", 1)
|
|
})
|
|
|
|
t.Run("reset connection - connect on second attempt", func(t *testing.T) {
|
|
connection.conn = nil
|
|
|
|
dialer := &amqpDialMock{}
|
|
var conn *amqpConnMock = nil
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, errors.New("test error")).Once()
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(&amqpConnMock{}, nil)
|
|
connection.dialer = dialer
|
|
|
|
assert.NoError(t, connection.ResetConnection(context.Background()))
|
|
assert.NotNil(t, connection.conn)
|
|
|
|
dialer.AssertNumberOfCalls(t, "Dial", 2)
|
|
})
|
|
|
|
t.Run("reset connection - fail continuously", func(t *testing.T) {
|
|
connection.conn = nil
|
|
|
|
dialer := &amqpDialMock{}
|
|
var conn *amqpConnMock = nil
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, errors.New("test error"))
|
|
connection.dialer = dialer
|
|
|
|
assert.EqualError(t, connection.ResetConnection(context.Background()), "connect: dial: test error")
|
|
assert.Nil(t, connection.conn)
|
|
|
|
dialer.AssertNumberOfCalls(t, "Dial", 2)
|
|
})
|
|
}
|
|
|
|
type ResettableFunction struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (f *ResettableFunction) Run(ctx context.Context) (any, error) {
|
|
args := f.Called(ctx)
|
|
return args.Get(0), args.Error(1)
|
|
}
|
|
|
|
func Test_AmqpConnection_ResetConnectionAndRetryIfErrorWithReturnValue(t *testing.T) {
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
t.Run("no error", func(t *testing.T) {
|
|
value, err := connection.ResetConnectionAndRetryIfErrorWithReturnValue("test", func(ctx context.Context) (any, error) {
|
|
return struct{}{}, nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, struct{}{}, value)
|
|
})
|
|
|
|
t.Run("reset - success", func(t *testing.T) {
|
|
connection.conn = nil
|
|
|
|
dialer := &amqpDialMock{}
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(&amqpConnMock{}, nil)
|
|
connection.dialer = dialer
|
|
|
|
resettableFunction := &ResettableFunction{}
|
|
resettableFunction.On("Run", mock.Anything).Return(struct{}{}, errors.New("test error")).Once()
|
|
resettableFunction.On("Run", mock.Anything).Return(struct{}{}, nil)
|
|
|
|
value, err := connection.ResetConnectionAndRetryIfErrorWithReturnValue("test", resettableFunction.Run)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, struct{}{}, value)
|
|
|
|
resettableFunction.AssertNumberOfCalls(t, "Run", 2)
|
|
})
|
|
|
|
t.Run("reset - fail", func(t *testing.T) {
|
|
connection.conn = nil
|
|
|
|
dialer := &amqpDialMock{}
|
|
var conn *amqpConnMock = nil
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, errors.New("test error"))
|
|
connection.dialer = dialer
|
|
|
|
resettableFunction := &ResettableFunction{}
|
|
resettableFunction.On("Run", mock.Anything).Return(struct{}{}, errors.New("test error"))
|
|
|
|
value, err := connection.ResetConnectionAndRetryIfErrorWithReturnValue("test", resettableFunction.Run)
|
|
assert.EqualError(t, err, "reset connection: connect: dial: test error")
|
|
assert.Nil(t, value)
|
|
|
|
resettableFunction.AssertNumberOfCalls(t, "Run", 1)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnection_NewSender(t *testing.T) {
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
|
return channel
|
|
}
|
|
|
|
t.Run("connection not initialized", func(t *testing.T) {
|
|
sender, err := connection.NewSender(context.Background(), "topic")
|
|
assert.EqualError(t, err, "connection is not initialized")
|
|
assert.Nil(t, sender)
|
|
})
|
|
|
|
t.Run("connection is closed", func(t *testing.T) {
|
|
channel := make(chan struct{})
|
|
close(channel)
|
|
|
|
conn := &amqpConnMock{}
|
|
conn.On("Done").Return(channelReceiver(channel))
|
|
connection.conn = conn
|
|
|
|
sender, err := connection.NewSender(context.Background(), "topic")
|
|
assert.EqualError(t, err, "amqp connection is closed")
|
|
assert.Nil(t, sender)
|
|
})
|
|
|
|
t.Run("session error", func(t *testing.T) {
|
|
channel := make(chan struct{})
|
|
|
|
var session *amqpSessionMock = nil
|
|
conn := &amqpConnMock{}
|
|
conn.On("NewSession", mock.Anything, mock.Anything).Return(session, errors.New("test error"))
|
|
conn.On("Done").Return(channelReceiver(channel))
|
|
connection.conn = conn
|
|
|
|
sender, err := connection.NewSender(context.Background(), "topic")
|
|
assert.EqualError(t, err, "new session: test error")
|
|
assert.Nil(t, sender)
|
|
})
|
|
|
|
t.Run("sender error", func(t *testing.T) {
|
|
channel := make(chan struct{})
|
|
|
|
sessionMock := &amqpSessionMock{}
|
|
var amqpSender *amqp.Sender = nil
|
|
sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(amqpSender, errors.New("test error"))
|
|
sessionMock.On("Close", mock.Anything).Return(nil)
|
|
|
|
conn := &amqpConnMock{}
|
|
conn.On("Done").Return(channelReceiver(channel))
|
|
conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil)
|
|
connection.conn = conn
|
|
|
|
sender, err := connection.NewSender(context.Background(), "topic")
|
|
assert.EqualError(t, err, "new internal sender: test error")
|
|
assert.Nil(t, sender)
|
|
})
|
|
|
|
t.Run("session close error", func(t *testing.T) {
|
|
channel := make(chan struct{})
|
|
|
|
sessionMock := &amqpSessionMock{}
|
|
var amqpSender *amqp.Sender = nil
|
|
sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(amqpSender, errors.New("test error"))
|
|
sessionMock.On("Close", mock.Anything).Return(errors.New("close error"))
|
|
|
|
conn := &amqpConnMock{}
|
|
conn.On("Done").Return(channelReceiver(channel))
|
|
conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil)
|
|
connection.conn = conn
|
|
|
|
sender, err := connection.NewSender(context.Background(), "topic")
|
|
assert.EqualError(t, err, "new internal sender: test error\nclose session: close error")
|
|
assert.Nil(t, sender)
|
|
})
|
|
|
|
t.Run("get sender", func(t *testing.T) {
|
|
channel := make(chan struct{})
|
|
|
|
amqpSender := &amqp.Sender{}
|
|
sessionMock := &amqpSessionMock{}
|
|
sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(amqpSender, nil)
|
|
|
|
conn := &amqpConnMock{}
|
|
conn.On("Done").Return(channelReceiver(channel))
|
|
conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil)
|
|
connection.conn = conn
|
|
|
|
sender, err := connection.NewSender(context.Background(), "topic")
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, sender)
|
|
assert.Equal(t, amqpSender, sender.sender)
|
|
assert.Equal(t, sessionMock, sender.session)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnection_NewAmqpConnection(t *testing.T) {
|
|
config := AmqpConnectionConfig{
|
|
BrokerUrl: "brokerUrl",
|
|
Username: "username",
|
|
Password: "password",
|
|
}
|
|
connection := NewAmqpConnection(&config, "connectionName")
|
|
assert.NotNil(t, connection)
|
|
assert.Equal(t, connection.connectionName, "connectionName")
|
|
assert.Equal(t, connection.brokerUrl, "brokerUrl")
|
|
assert.Equal(t, connection.username, "username")
|
|
assert.Equal(t, connection.password, "password")
|
|
assert.NotNil(t, connection.dialer)
|
|
}
|
|
|
|
func Test_As(t *testing.T) {
|
|
|
|
t.Run("error", func(t *testing.T) {
|
|
value, err := As[amqp.Message](nil, errors.New("test error"))
|
|
assert.EqualError(t, err, "test error")
|
|
assert.Nil(t, value)
|
|
})
|
|
|
|
t.Run("value nil", func(t *testing.T) {
|
|
value, err := As[amqp.Message](nil, nil)
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, value)
|
|
})
|
|
|
|
t.Run("value not not type", func(t *testing.T) {
|
|
value, err := As[amqp.Message](struct{}{}, nil)
|
|
assert.EqualError(t, err, "could not cast value: struct {}")
|
|
assert.Nil(t, value)
|
|
})
|
|
|
|
t.Run("cast", func(t *testing.T) {
|
|
var sessionAny any = &amqpSessionMock{}
|
|
value, err := As[amqpSessionMock](sessionAny, nil)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, value)
|
|
})
|
|
}
|