mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-16 12:51:44 +00:00
572 lines
18 KiB
Go
572 lines
18 KiB
Go
package messaging
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
type connectionProviderMock struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (p *connectionProviderMock) NewAmqpConnection(config *AmqpConnectionConfig, connectionName string) *AmqpConnection {
|
|
args := p.Called(config, connectionName)
|
|
return args.Get(0).(*AmqpConnection)
|
|
}
|
|
|
|
var _ connectionProvider = (*connectionProviderMock)(nil)
|
|
|
|
func Test_AmqpConnectionPool_GetHandle(t *testing.T) {
|
|
|
|
t.Run("next handle", func(t *testing.T) {
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
handle := pool.NewHandle()
|
|
assert.NotNil(t, handle)
|
|
assert.Equal(t, 0, handle.connectionOffset)
|
|
assert.Equal(t, 1, pool.handleOffset)
|
|
})
|
|
|
|
t.Run("next handle high offset", func(t *testing.T) {
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 13,
|
|
lock: sync.RWMutex{},
|
|
}
|
|
|
|
handle := pool.NewHandle()
|
|
assert.NotNil(t, handle)
|
|
assert.Equal(t, 3, handle.connectionOffset)
|
|
assert.Equal(t, 14, pool.handleOffset)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnectionPool_internalAddConnection(t *testing.T) {
|
|
|
|
t.Run("internal add connection", func(t *testing.T) {
|
|
conn := &amqpConnMock{}
|
|
|
|
dialer := &amqpDialMock{}
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
|
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: dialer,
|
|
}
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
err := pool.internalAddConnection()
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 1, len(pool.connections))
|
|
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1)
|
|
dialer.AssertNumberOfCalls(t, "Dial", 1)
|
|
})
|
|
|
|
t.Run("dialer error", func(t *testing.T) {
|
|
conn := &amqpConnMock{}
|
|
|
|
dialer := &amqpDialMock{}
|
|
var c *amqpConnMock = nil
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error")).Once()
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
|
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: dialer,
|
|
}
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
err := pool.internalAddConnection()
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 1, len(pool.connections))
|
|
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1)
|
|
dialer.AssertNumberOfCalls(t, "Dial", 2)
|
|
})
|
|
|
|
t.Run("repetitive dialer error", func(t *testing.T) {
|
|
dialer := &amqpDialMock{}
|
|
var c *amqpConnMock = nil
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error"))
|
|
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: dialer,
|
|
}
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
err := pool.internalAddConnection()
|
|
assert.EqualError(t, err, "new connection: failed to connect to amqp broker: internal connection connect: dial: test error")
|
|
|
|
assert.Equal(t, 0, len(pool.connections))
|
|
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1)
|
|
dialer.AssertNumberOfCalls(t, "Dial", 2)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnectionPool_initializeConnections(t *testing.T) {
|
|
|
|
t.Run("initialize connections successfully", func(t *testing.T) {
|
|
|
|
conn := &amqpConnMock{}
|
|
dialer := &amqpDialMock{}
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
|
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: dialer,
|
|
}
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
err := pool.initializeConnections()
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 5, len(pool.connections))
|
|
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 5)
|
|
})
|
|
|
|
t.Run("fail initialization of connections", func(t *testing.T) {
|
|
|
|
var c *amqpConnMock = nil
|
|
failingDialer := &amqpDialMock{}
|
|
failingDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error"))
|
|
|
|
failingConnection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: failingDialer,
|
|
}
|
|
|
|
conn := &amqpConnMock{}
|
|
successfulDialer := &amqpDialMock{}
|
|
successfulDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
|
|
|
successfulConnection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: successfulDialer,
|
|
}
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(successfulConnection).Times(4)
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(failingConnection)
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
err := pool.initializeConnections()
|
|
assert.EqualError(t, err, "new connection: failed to connect to amqp broker: internal connection connect: dial: test error")
|
|
|
|
assert.Equal(t, 4, len(pool.connections))
|
|
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 5)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnectionPool_Close(t *testing.T) {
|
|
|
|
t.Run("close connection successfully", func(t *testing.T) {
|
|
// add 5 connections to the pool
|
|
conn := &amqpConnMock{}
|
|
conn.On("Close").Return(nil)
|
|
|
|
dialer := &amqpDialMock{}
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
|
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: dialer,
|
|
}
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
err := pool.initializeConnections()
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 5, len(pool.connections))
|
|
|
|
// close the pool
|
|
err = pool.Close()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, len(pool.connections))
|
|
})
|
|
|
|
t.Run("close connection fail", func(t *testing.T) {
|
|
// add 5 connections to the pool
|
|
failingConn := &amqpConnMock{}
|
|
failingConn.On("Close").Return(errors.New("test error"))
|
|
|
|
failingDialer := &amqpDialMock{}
|
|
failingDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(failingConn, nil)
|
|
|
|
failingConnection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: failingDialer,
|
|
}
|
|
|
|
successfulConn := &amqpConnMock{}
|
|
successfulConn.On("Close").Return(nil)
|
|
successfulDialer := &amqpDialMock{}
|
|
successfulDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(successfulConn, nil)
|
|
|
|
successfulConnection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: successfulDialer,
|
|
}
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(successfulConnection).Times(2)
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(failingConnection).Times(2)
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(successfulConnection).Times(1)
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
err := pool.initializeConnections()
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 5, len(pool.connections))
|
|
|
|
// close the pool
|
|
err = pool.Close()
|
|
assert.EqualError(t, err, "connection: close: close: internal connection close: test error\nconnection: close: close: internal connection close: test error")
|
|
assert.Equal(t, 0, len(pool.connections))
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) {
|
|
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
|
return channel
|
|
}
|
|
|
|
newActiveConnection := func() *AmqpConnection {
|
|
channel := make(chan struct{})
|
|
conn := &amqpConnMock{}
|
|
conn.On("Done", mock.Anything).Return(channelReceiver(channel))
|
|
return &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
conn: conn,
|
|
}
|
|
}
|
|
|
|
newClosedConnection := func() *AmqpConnection {
|
|
channel := make(chan struct{})
|
|
close(channel)
|
|
|
|
conn := &amqpConnMock{}
|
|
conn.On("Done", mock.Anything).Return(channelReceiver(channel))
|
|
return &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
conn: conn,
|
|
}
|
|
}
|
|
|
|
t.Run("next connection for requested handle", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
for i := 0; i < 5; i++ {
|
|
connections = append(connections, newActiveConnection())
|
|
}
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
}
|
|
|
|
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.NotNil(t, connection)
|
|
assert.False(t, addConnection)
|
|
})
|
|
|
|
t.Run("nil connection for requested handle", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, newActiveConnection())
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
}
|
|
|
|
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.NotNil(t, connection)
|
|
assert.True(t, addConnection)
|
|
})
|
|
|
|
t.Run("closed connection for requested handle", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, newClosedConnection())
|
|
connections = append(connections, newClosedConnection())
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, newActiveConnection())
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
}
|
|
|
|
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.NotNil(t, connection)
|
|
assert.True(t, addConnection)
|
|
})
|
|
|
|
t.Run("no connection for requested handle", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
}
|
|
|
|
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.Nil(t, connection)
|
|
assert.True(t, addConnection)
|
|
})
|
|
|
|
t.Run("connection for requested handle with large index", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, nil)
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
}
|
|
|
|
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 23})
|
|
assert.NotNil(t, connection)
|
|
assert.False(t, addConnection)
|
|
})
|
|
|
|
t.Run("connection for requested handle nil with large index", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, newActiveConnection())
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
}
|
|
|
|
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 23})
|
|
assert.NotNil(t, connection)
|
|
assert.True(t, addConnection)
|
|
})
|
|
}
|
|
|
|
func Test_AmqpConnectionPool_GetConnection(t *testing.T) {
|
|
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
|
return channel
|
|
}
|
|
|
|
newActiveConnection := func() *AmqpConnection {
|
|
channel := make(chan struct{})
|
|
conn := &amqpConnMock{}
|
|
conn.On("Done", mock.Anything).Return(channelReceiver(channel))
|
|
return &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
conn: conn,
|
|
}
|
|
}
|
|
|
|
t.Run("get connection for requested handle", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
for i := 0; i < 5; i++ {
|
|
connections = append(connections, newActiveConnection())
|
|
}
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
}
|
|
|
|
connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, connection)
|
|
assert.Equal(t, connections[1], connection)
|
|
assert.Equal(t, 5, len(connections))
|
|
})
|
|
|
|
t.Run("add connection if missing", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 5)
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(newActiveConnection())
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, connection)
|
|
assert.Equal(t, connections[1], connection)
|
|
assert.Equal(t, 5, len(connections))
|
|
})
|
|
|
|
t.Run("add connection fails returns alternative connection", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, nil)
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, newActiveConnection())
|
|
connections = append(connections, newActiveConnection())
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
|
|
dialer := &amqpDialMock{}
|
|
var c *amqpConnMock = nil
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, fmt.Errorf("dial error"))
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: dialer,
|
|
}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, connection)
|
|
assert.Nil(t, connections[1])
|
|
assert.Equal(t, connections[2], connection)
|
|
assert.Equal(t, 5, len(connections))
|
|
})
|
|
|
|
t.Run("add connection fails", func(t *testing.T) {
|
|
connections := make([]*AmqpConnection, 0)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
connections = append(connections, nil)
|
|
|
|
connectionProvider := &connectionProviderMock{}
|
|
|
|
dialer := &amqpDialMock{}
|
|
var c *amqpConnMock = nil
|
|
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, fmt.Errorf("dial error"))
|
|
connection := &AmqpConnection{
|
|
connectionName: "test",
|
|
lock: sync.RWMutex{},
|
|
dialer: dialer,
|
|
}
|
|
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
|
|
|
pool := AmqpConnectionPool{
|
|
config: AmqpConnectionPoolConfig{PoolSize: 5},
|
|
handleOffset: 0,
|
|
lock: sync.RWMutex{},
|
|
connections: connections,
|
|
connectionProvider: connectionProvider,
|
|
}
|
|
|
|
connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1})
|
|
assert.EqualError(t, err, "renew connection: failed to connect to amqp broker: internal connection connect: dial: dial error")
|
|
assert.Nil(t, connection)
|
|
assert.Equal(t, 5, len(connections))
|
|
})
|
|
}
|