fa7c01bd0b
For some tests enough empty results. Signed-off-by: Denis Kirillov <denis@nspcc.ru>
541 lines
15 KiB
Go
541 lines
15 KiB
Go
package pool
|
|
|
|
//go:generate mockgen -destination mock_test.go -package pool . Client
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
crand "crypto/rand"
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/google/uuid"
|
|
"github.com/nspcc-dev/neofs-sdk-go/client"
|
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestBuildPoolClientFailed(t *testing.T) {
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
return nil, fmt.Errorf("error")
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
}
|
|
|
|
_, err := pb.Build(context.TODO(), opts)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestBuildPoolCreateSessionFailed(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
|
|
ni := &netmap.NodeInfo{}
|
|
ni.SetAddresses("addr1", "addr2")
|
|
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error session")).AnyTimes()
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfoRes{}, nil).AnyTimes()
|
|
return mockClient, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
}
|
|
|
|
_, err := pb.Build(context.TODO(), opts)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func newPrivateKey(t *testing.T) *ecdsa.PrivateKey {
|
|
p, err := ecdsa.GenerateKey(elliptic.P256(), crand.Reader)
|
|
require.NoError(t, err)
|
|
return p
|
|
}
|
|
|
|
func TestBuildPoolOneNodeFailed(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
ctrl2 := gomock.NewController(t)
|
|
|
|
ni := &netmap.NodeInfo{}
|
|
ni.SetAddresses("addr1", "addr2")
|
|
|
|
var expectedToken *session.Token
|
|
clientCount := -1
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
clientCount++
|
|
mockClient := NewMockClient(ctrl)
|
|
mockInvokes := 0
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
mockInvokes++
|
|
if mockInvokes == 1 {
|
|
expectedToken = newToken(t)
|
|
return nil, fmt.Errorf("error session")
|
|
}
|
|
return expectedToken, nil
|
|
}).AnyTimes()
|
|
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
mockClient2 := NewMockClient(ctrl2)
|
|
mockClient2.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
mockClient2.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
if clientCount == 0 {
|
|
return mockClient, nil
|
|
}
|
|
return mockClient2, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 9, 1)
|
|
pb.AddNode("peer1", 1, 1)
|
|
|
|
log, err := zap.NewProduction()
|
|
require.NoError(t, err)
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
ClientRebalanceInterval: 1000 * time.Millisecond,
|
|
Logger: log,
|
|
}
|
|
|
|
clientPool, err := pb.Build(context.TODO(), opts)
|
|
require.NoError(t, err)
|
|
t.Cleanup(clientPool.Close)
|
|
|
|
condition := func() bool {
|
|
_, st, err := clientPool.Connection()
|
|
return err == nil && st == expectedToken
|
|
}
|
|
require.Never(t, condition, 900*time.Millisecond, 100*time.Millisecond)
|
|
require.Eventually(t, condition, 3*time.Second, 300*time.Millisecond)
|
|
}
|
|
|
|
func TestBuildPoolZeroNodes(t *testing.T) {
|
|
pb := new(Builder)
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
}
|
|
_, err := pb.Build(context.TODO(), opts)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestOneNode(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
tok := session.NewToken()
|
|
uid, err := uuid.New().MarshalBinary()
|
|
require.NoError(t, err)
|
|
tok.SetID(uid)
|
|
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(tok, nil)
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfo{}, nil).AnyTimes()
|
|
return mockClient, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
}
|
|
|
|
pool, err := pb.Build(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
t.Cleanup(pool.Close)
|
|
|
|
_, st, err := pool.Connection()
|
|
require.NoError(t, err)
|
|
require.Equal(t, tok, st)
|
|
}
|
|
|
|
func TestTwoNodes(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
var tokens []*session.Token
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
tok := session.NewToken()
|
|
uid, err := uuid.New().MarshalBinary()
|
|
require.NoError(t, err)
|
|
tok.SetID(uid)
|
|
tokens = append(tokens, tok)
|
|
return tok, err
|
|
})
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfo{}, nil).AnyTimes()
|
|
return mockClient, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
pb.AddNode("peer1", 1, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
}
|
|
|
|
pool, err := pb.Build(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
t.Cleanup(pool.Close)
|
|
|
|
_, st, err := pool.Connection()
|
|
require.NoError(t, err)
|
|
require.Contains(t, tokens, st)
|
|
}
|
|
|
|
func TestOneOfTwoFailed(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
ctrl2 := gomock.NewController(t)
|
|
|
|
var tokens []*session.Token
|
|
clientCount := -1
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
clientCount++
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
tok := newToken(t)
|
|
tokens = append(tokens, tok)
|
|
return tok, nil
|
|
}).AnyTimes()
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
mockClient2 := NewMockClient(ctrl2)
|
|
mockClient2.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
tok := newToken(t)
|
|
tokens = append(tokens, tok)
|
|
return tok, nil
|
|
}).AnyTimes()
|
|
mockClient2.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).DoAndReturn(func(_ interface{}, _ ...interface{}) (*client.EndpointInfo, error) {
|
|
return nil, fmt.Errorf("error")
|
|
}).AnyTimes()
|
|
|
|
if clientCount == 0 {
|
|
return mockClient, nil
|
|
}
|
|
return mockClient2, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
pb.AddNode("peer1", 9, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
ClientRebalanceInterval: 200 * time.Millisecond,
|
|
}
|
|
|
|
pool, err := pb.Build(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
t.Cleanup(pool.Close)
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
for i := 0; i < 5; i++ {
|
|
_, st, err := pool.Connection()
|
|
require.NoError(t, err)
|
|
require.Equal(t, tokens[0], st)
|
|
}
|
|
}
|
|
|
|
func TestTwoFailed(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).AnyTimes()
|
|
return mockClient, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
pb.AddNode("peer1", 1, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
ClientRebalanceInterval: 200 * time.Millisecond,
|
|
}
|
|
|
|
pool, err := pb.Build(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
t.Cleanup(pool.Close)
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
_, _, err = pool.Connection()
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "no healthy")
|
|
}
|
|
|
|
func TestSessionCache(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
var tokens []*session.Token
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
tok := session.NewToken()
|
|
uid, err := uuid.New().MarshalBinary()
|
|
require.NoError(t, err)
|
|
tok.SetID(uid)
|
|
tokens = append(tokens, tok)
|
|
return tok, err
|
|
}).MaxTimes(3)
|
|
|
|
mockClient.EXPECT().GetObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("session token does not exist"))
|
|
mockClient.EXPECT().PutObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
|
|
|
|
return mockClient, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
ClientRebalanceInterval: 30 * time.Second,
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
pool, err := pb.Build(ctx, opts)
|
|
require.NoError(t, err)
|
|
t.Cleanup(pool.Close)
|
|
|
|
// cache must contain session token
|
|
_, st, err := pool.Connection()
|
|
require.NoError(t, err)
|
|
require.Contains(t, tokens, st)
|
|
|
|
_, err = pool.GetObject(ctx, nil, retry())
|
|
require.Error(t, err)
|
|
|
|
// cache must not contain session token
|
|
_, st, err = pool.Connection()
|
|
require.NoError(t, err)
|
|
require.Nil(t, st)
|
|
|
|
_, err = pool.PutObject(ctx, nil)
|
|
require.NoError(t, err)
|
|
|
|
// cache must contain session token
|
|
_, st, err = pool.Connection()
|
|
require.NoError(t, err)
|
|
require.Contains(t, tokens, st)
|
|
}
|
|
|
|
func TestPriority(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
ctrl2 := gomock.NewController(t)
|
|
|
|
var tokens []*session.Token
|
|
clientCount := -1
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
clientCount++
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
tok := newToken(t)
|
|
tokens = append(tokens, tok)
|
|
return tok, nil
|
|
}).AnyTimes()
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).AnyTimes()
|
|
|
|
mockClient2 := NewMockClient(ctrl2)
|
|
mockClient2.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
tok := newToken(t)
|
|
tokens = append(tokens, tok)
|
|
return tok, nil
|
|
}).AnyTimes()
|
|
mockClient2.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
if clientCount == 0 {
|
|
return mockClient, nil
|
|
}
|
|
return mockClient2, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
pb.AddNode("peer1", 2, 100)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
ClientRebalanceInterval: 1500 * time.Millisecond,
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
pool, err := pb.Build(ctx, opts)
|
|
require.NoError(t, err)
|
|
t.Cleanup(pool.Close)
|
|
|
|
firstNode := func() bool {
|
|
_, st, err := pool.Connection()
|
|
require.NoError(t, err)
|
|
return st == tokens[0]
|
|
}
|
|
secondNode := func() bool {
|
|
_, st, err := pool.Connection()
|
|
require.NoError(t, err)
|
|
return st == tokens[1]
|
|
}
|
|
require.Never(t, secondNode, time.Second, 200*time.Millisecond)
|
|
|
|
require.Eventually(t, secondNode, time.Second, 200*time.Millisecond)
|
|
require.Never(t, firstNode, time.Second, 200*time.Millisecond)
|
|
}
|
|
|
|
func TestSessionCacheWithKey(t *testing.T) {
|
|
t.Skip("NeoFS API client can't be mocked") // neofs-sdk-go#85
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
var tokens []*session.Token
|
|
clientBuilder := func(opts ...client.Option) (client.Client, error) {
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) {
|
|
tok := session.NewToken()
|
|
uid, err := uuid.New().MarshalBinary()
|
|
require.NoError(t, err)
|
|
tok.SetID(uid)
|
|
tokens = append(tokens, tok)
|
|
return tok, err
|
|
}).MaxTimes(2)
|
|
|
|
mockClient.EXPECT().GetObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
|
|
|
|
return mockClient, nil
|
|
}
|
|
|
|
pb := new(Builder)
|
|
pb.AddNode("peer0", 1, 1)
|
|
|
|
opts := &BuilderOptions{
|
|
Key: newPrivateKey(t),
|
|
clientBuilder: clientBuilder,
|
|
ClientRebalanceInterval: 30 * time.Second,
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
pool, err := pb.Build(ctx, opts)
|
|
require.NoError(t, err)
|
|
|
|
// cache must contain session token
|
|
_, st, err := pool.Connection()
|
|
require.NoError(t, err)
|
|
require.Contains(t, tokens, st)
|
|
|
|
_, err = pool.GetObject(ctx, nil, WithKey(newPrivateKey(t)))
|
|
require.NoError(t, err)
|
|
require.Len(t, tokens, 2)
|
|
}
|
|
|
|
func newToken(t *testing.T) *session.Token {
|
|
tok := session.NewToken()
|
|
uid, err := uuid.New().MarshalBinary()
|
|
require.NoError(t, err)
|
|
tok.SetID(uid)
|
|
|
|
return tok
|
|
}
|
|
|
|
func TestWaitPresence(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
|
|
mockClient := NewMockClient(ctrl)
|
|
mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
mockClient.EXPECT().GetContainer(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
cache, err := NewCache()
|
|
require.NoError(t, err)
|
|
|
|
inner := &innerPool{
|
|
sampler: NewSampler([]float64{1}, rand.NewSource(0)),
|
|
clientPacks: []*clientPack{{
|
|
client: mockClient,
|
|
healthy: true,
|
|
}},
|
|
}
|
|
|
|
p := &pool{
|
|
innerPools: []*innerPool{inner},
|
|
key: newPrivateKey(t),
|
|
cache: cache,
|
|
}
|
|
|
|
t.Run("context canceled", func(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
cancel()
|
|
}()
|
|
|
|
err := p.WaitForContainerPresence(ctx, nil, DefaultPollingParams())
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "context canceled")
|
|
})
|
|
|
|
t.Run("context deadline exceeded", func(t *testing.T) {
|
|
ctx := context.Background()
|
|
err := p.WaitForContainerPresence(ctx, nil, &ContainerPollingParams{
|
|
CreationTimeout: 500 * time.Millisecond,
|
|
PollInterval: 5 * time.Second,
|
|
})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "context deadline exceeded")
|
|
})
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
ctx := context.Background()
|
|
err := p.WaitForContainerPresence(ctx, nil, &ContainerPollingParams{
|
|
CreationTimeout: 10 * time.Second,
|
|
PollInterval: 500 * time.Millisecond,
|
|
})
|
|
require.NoError(t, err)
|
|
})
|
|
}
|