forked from TrueCloudLab/frostfs-node
1208 lines
29 KiB
Go
1208 lines
29 KiB
Go
|
package object
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/multiformats/go-multiaddr"
|
||
|
"github.com/nspcc-dev/neofs-api-go/hash"
|
||
|
"github.com/nspcc-dev/neofs-api-go/object"
|
||
|
"github.com/nspcc-dev/neofs-api-go/service"
|
||
|
"github.com/nspcc-dev/neofs-node/internal"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/core"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/implementations"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/localstore"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/transport"
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
// Entity for mocking interfaces.
|
||
|
// Implementation of any interface intercepts arguments via f (if not nil).
|
||
|
// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error.
|
||
|
testExecutionEntity struct {
|
||
|
// Set of interfaces which testExecutionEntity must implement, but some methods from those does not call.
|
||
|
transport.MetaInfo
|
||
|
localstore.Localstore
|
||
|
containerTraverser
|
||
|
|
||
|
// Argument interceptor. Used for ascertain of correct parameter passage between components.
|
||
|
f func(...interface{})
|
||
|
// Mocked result of any interface.
|
||
|
res interface{}
|
||
|
// Mocked error of any interface.
|
||
|
err error
|
||
|
}
|
||
|
)
|
||
|
|
||
|
func (s *testExecutionEntity) HandleResult(_ context.Context, n multiaddr.Multiaddr, r interface{}, e error) {
|
||
|
if s.f != nil {
|
||
|
s.f(n, r, e)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
_ transport.ResultHandler = (*testExecutionEntity)(nil)
|
||
|
_ interceptorPreparer = (*testExecutionEntity)(nil)
|
||
|
_ implementations.ContainerTraverseExecutor = (*testExecutionEntity)(nil)
|
||
|
_ WorkerPool = (*testExecutionEntity)(nil)
|
||
|
_ operationExecutor = (*testExecutionEntity)(nil)
|
||
|
_ placementBuilder = (*testExecutionEntity)(nil)
|
||
|
_ implementations.AddressStore = (*testExecutionEntity)(nil)
|
||
|
_ executionParamsComputer = (*testExecutionEntity)(nil)
|
||
|
_ operationFinalizer = (*testExecutionEntity)(nil)
|
||
|
_ EpochReceiver = (*testExecutionEntity)(nil)
|
||
|
_ localstore.Localstore = (*testExecutionEntity)(nil)
|
||
|
_ containerTraverser = (*testExecutionEntity)(nil)
|
||
|
_ responseItemHandler = (*testExecutionEntity)(nil)
|
||
|
_ resultTracker = (*testExecutionEntity)(nil)
|
||
|
_ localObjectStorer = (*testExecutionEntity)(nil)
|
||
|
_ localFullObjectReceiver = (*testExecutionEntity)(nil)
|
||
|
_ localHeadReceiver = (*testExecutionEntity)(nil)
|
||
|
_ localQueryImposer = (*testExecutionEntity)(nil)
|
||
|
_ localRangeReader = (*testExecutionEntity)(nil)
|
||
|
_ localRangeHasher = (*testExecutionEntity)(nil)
|
||
|
)
|
||
|
|
||
|
func (s *testExecutionEntity) prepareInterceptor(p interceptorItems) (func(context.Context, multiaddr.Multiaddr) bool, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(p)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.(func(context.Context, multiaddr.Multiaddr) bool), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Execute(_ context.Context, p implementations.TraverseParams) {
|
||
|
if s.f != nil {
|
||
|
s.f(p)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Submit(func()) error {
|
||
|
return s.err
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) executeOperation(ctx context.Context, r transport.MetaInfo, h responseItemHandler) error {
|
||
|
if s.f != nil {
|
||
|
s.f(r, h)
|
||
|
}
|
||
|
return s.err
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) buildPlacement(_ context.Context, a Address, n ...multiaddr.Multiaddr) ([]multiaddr.Multiaddr, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(a, n)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.([]multiaddr.Multiaddr), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) getHashes(_ context.Context, a Address, r []Range, sa []byte) ([]Hash, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(a, r, sa)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.([]Hash), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) getRange(_ context.Context, addr Address, rngs Range) ([]byte, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(addr, rngs)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.([]byte), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) imposeQuery(_ context.Context, c CID, d []byte, v int) ([]Address, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(c, d, v)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.([]Address), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) headObject(_ context.Context, addr Address) (*Object, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(addr)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.(*Object), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) getObject(_ context.Context, addr Address) (*Object, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(addr)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.(*Object), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) putObject(_ context.Context, obj *Object) error {
|
||
|
if s.f != nil {
|
||
|
s.f(obj)
|
||
|
}
|
||
|
return s.err
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) trackResult(_ context.Context, p resultItems) {
|
||
|
if s.f != nil {
|
||
|
s.f(p)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) handleItem(v interface{}) {
|
||
|
if s.f != nil {
|
||
|
s.f(v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) add(n multiaddr.Multiaddr, b bool) {
|
||
|
if s.f != nil {
|
||
|
s.f(n, b)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) done(n multiaddr.Multiaddr) bool {
|
||
|
if s.f != nil {
|
||
|
s.f(n)
|
||
|
}
|
||
|
return s.res.(bool)
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) close() {
|
||
|
if s.f != nil {
|
||
|
s.f()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) PRead(ctx context.Context, addr Address, rng Range) ([]byte, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(addr, rng)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.([]byte), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Put(ctx context.Context, obj *Object) error {
|
||
|
if s.f != nil {
|
||
|
s.f(ctx, obj)
|
||
|
}
|
||
|
return s.err
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Get(addr Address) (*Object, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(addr)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.(*Object), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Meta(addr Address) (*Meta, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(addr)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.(*Meta), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Has(addr Address) (bool, error) {
|
||
|
if s.f != nil {
|
||
|
s.f(addr)
|
||
|
}
|
||
|
if s.err != nil {
|
||
|
return false, s.err
|
||
|
}
|
||
|
return s.res.(bool), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Epoch() uint64 { return s.res.(uint64) }
|
||
|
|
||
|
func (s *testExecutionEntity) completeExecution(_ context.Context, p operationParams) error {
|
||
|
if s.f != nil {
|
||
|
s.f(p)
|
||
|
}
|
||
|
return s.err
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) computeParams(p *computableParams, r transport.MetaInfo) {
|
||
|
if s.f != nil {
|
||
|
s.f(p, r)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) SelfAddr() (multiaddr.Multiaddr, error) {
|
||
|
if s.err != nil {
|
||
|
return nil, s.err
|
||
|
}
|
||
|
return s.res.(multiaddr.Multiaddr), nil
|
||
|
}
|
||
|
|
||
|
func (s *testExecutionEntity) Type() object.RequestType {
|
||
|
return s.res.(object.RequestType)
|
||
|
}
|
||
|
|
||
|
func Test_typeOfRequest(t *testing.T) {
|
||
|
t.Run("correct mapping", func(t *testing.T) {
|
||
|
items := []struct {
|
||
|
exp object.RequestType
|
||
|
v transport.MetaInfo
|
||
|
}{
|
||
|
{exp: object.RequestSearch, v: &transportRequest{serviceRequest: new(object.SearchRequest)}},
|
||
|
{exp: object.RequestSearch, v: newRawSearchInfo()},
|
||
|
{exp: object.RequestPut, v: new(putRequest)},
|
||
|
{exp: object.RequestPut, v: &transportRequest{serviceRequest: new(object.PutRequest)}},
|
||
|
{exp: object.RequestGet, v: newRawGetInfo()},
|
||
|
{exp: object.RequestGet, v: &transportRequest{serviceRequest: new(object.GetRequest)}},
|
||
|
{exp: object.RequestHead, v: newRawHeadInfo()},
|
||
|
{exp: object.RequestHead, v: &transportRequest{serviceRequest: new(object.HeadRequest)}},
|
||
|
{exp: object.RequestRange, v: newRawRangeInfo()},
|
||
|
{exp: object.RequestRange, v: &transportRequest{serviceRequest: new(GetRangeRequest)}},
|
||
|
{exp: object.RequestRangeHash, v: newRawRangeHashInfo()},
|
||
|
{exp: object.RequestRangeHash, v: &transportRequest{serviceRequest: new(object.GetRangeHashRequest)}},
|
||
|
}
|
||
|
|
||
|
for i := range items {
|
||
|
require.Equal(t, items[i].exp, items[i].v.Type())
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func Test_coreExecParamsComp_computeParams(t *testing.T) {
|
||
|
s := new(coreExecParamsComp)
|
||
|
addr := testObjectAddress(t)
|
||
|
|
||
|
t.Run("put", func(t *testing.T) {
|
||
|
addr := testObjectAddress(t)
|
||
|
|
||
|
p := new(computableParams)
|
||
|
r := &putRequest{PutRequest: &object.PutRequest{
|
||
|
R: &object.PutRequest_Header{
|
||
|
Header: &object.PutRequest_PutHeader{
|
||
|
Object: &Object{
|
||
|
SystemHeader: SystemHeader{
|
||
|
ID: addr.ObjectID,
|
||
|
CID: addr.CID,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}}
|
||
|
|
||
|
s.computeParams(p, r)
|
||
|
|
||
|
t.Run("non-forwarding behavior", func(t *testing.T) {
|
||
|
require.Equal(t, 1, p.stopCount)
|
||
|
})
|
||
|
|
||
|
r.SetTTL(service.NonForwardingTTL)
|
||
|
|
||
|
s.computeParams(p, r)
|
||
|
|
||
|
require.False(t, p.allowPartialResult)
|
||
|
require.False(t, p.tryPreviousNetMap)
|
||
|
require.False(t, p.selfForward)
|
||
|
require.Equal(t, addr, p.addr)
|
||
|
require.Equal(t, 0, p.maxRecycleCount)
|
||
|
require.Equal(t, 0, int(r.CopiesNumber()))
|
||
|
})
|
||
|
|
||
|
t.Run("get", func(t *testing.T) {
|
||
|
p := new(computableParams)
|
||
|
|
||
|
r := newRawGetInfo()
|
||
|
r.setAddress(addr)
|
||
|
|
||
|
s.computeParams(p, r)
|
||
|
|
||
|
require.Equal(t, 1, p.stopCount)
|
||
|
require.False(t, p.allowPartialResult)
|
||
|
require.True(t, p.tryPreviousNetMap)
|
||
|
require.False(t, p.selfForward)
|
||
|
require.Equal(t, addr, p.addr)
|
||
|
require.Equal(t, 0, p.maxRecycleCount)
|
||
|
})
|
||
|
|
||
|
t.Run("head", func(t *testing.T) {
|
||
|
p := new(computableParams)
|
||
|
r := &transportRequest{serviceRequest: &object.HeadRequest{Address: addr}}
|
||
|
|
||
|
s.computeParams(p, r)
|
||
|
|
||
|
require.Equal(t, 1, p.stopCount)
|
||
|
require.False(t, p.allowPartialResult)
|
||
|
require.True(t, p.tryPreviousNetMap)
|
||
|
require.False(t, p.selfForward)
|
||
|
require.Equal(t, addr, p.addr)
|
||
|
require.Equal(t, 0, p.maxRecycleCount)
|
||
|
})
|
||
|
|
||
|
t.Run("search", func(t *testing.T) {
|
||
|
p := new(computableParams)
|
||
|
r := &transportRequest{serviceRequest: &object.SearchRequest{ContainerID: addr.CID}}
|
||
|
|
||
|
s.computeParams(p, r)
|
||
|
|
||
|
require.Equal(t, -1, p.stopCount)
|
||
|
require.True(t, p.allowPartialResult)
|
||
|
require.True(t, p.tryPreviousNetMap)
|
||
|
require.False(t, p.selfForward)
|
||
|
require.Equal(t, addr.CID, p.addr.CID)
|
||
|
require.True(t, p.addr.ObjectID.Empty())
|
||
|
require.Equal(t, 0, p.maxRecycleCount)
|
||
|
})
|
||
|
|
||
|
t.Run("range", func(t *testing.T) {
|
||
|
p := new(computableParams)
|
||
|
|
||
|
r := newRawRangeInfo()
|
||
|
r.setAddress(addr)
|
||
|
|
||
|
s.computeParams(p, r)
|
||
|
|
||
|
require.Equal(t, 1, p.stopCount)
|
||
|
require.False(t, p.allowPartialResult)
|
||
|
require.False(t, p.tryPreviousNetMap)
|
||
|
require.False(t, p.selfForward)
|
||
|
require.Equal(t, addr, p.addr)
|
||
|
require.Equal(t, 0, p.maxRecycleCount)
|
||
|
})
|
||
|
|
||
|
t.Run("range hash", func(t *testing.T) {
|
||
|
p := new(computableParams)
|
||
|
|
||
|
r := newRawRangeHashInfo()
|
||
|
r.setAddress(addr)
|
||
|
|
||
|
s.computeParams(p, r)
|
||
|
|
||
|
require.Equal(t, 1, p.stopCount)
|
||
|
require.False(t, p.allowPartialResult)
|
||
|
require.False(t, p.tryPreviousNetMap)
|
||
|
require.False(t, p.selfForward)
|
||
|
require.Equal(t, addr, p.addr)
|
||
|
require.Equal(t, 0, p.maxRecycleCount)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func Test_coreOperationExecutor_executeOperation(t *testing.T) {
|
||
|
ctx := context.TODO()
|
||
|
|
||
|
t.Run("correct result", func(t *testing.T) {
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
p := new(testExecutionEntity)
|
||
|
req := newRawPutInfo()
|
||
|
req.setTTL(1)
|
||
|
finErr := internal.Error("test error for operation finalizer")
|
||
|
|
||
|
s := &coreOperationExecutor{
|
||
|
pre: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct params computer arguments", func(t *testing.T) {
|
||
|
require.Equal(t, computableParams{}, *items[0].(*computableParams))
|
||
|
require.Equal(t, req, items[1].(transport.MetaInfo))
|
||
|
})
|
||
|
},
|
||
|
},
|
||
|
fin: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
par := items[0].(operationParams)
|
||
|
require.Equal(t, req, par.metaInfo)
|
||
|
require.Equal(t, p, par.itemHandler)
|
||
|
},
|
||
|
err: finErr,
|
||
|
},
|
||
|
loc: new(testExecutionEntity),
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, p),
|
||
|
finErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("zero ttl", func(t *testing.T) {
|
||
|
p := new(testExecutionEntity)
|
||
|
req := newRawPutInfo()
|
||
|
finErr := internal.Error("test error for operation finalizer")
|
||
|
|
||
|
s := &coreOperationExecutor{
|
||
|
loc: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, req, items[0])
|
||
|
require.Equal(t, p, items[1])
|
||
|
},
|
||
|
err: finErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, p),
|
||
|
finErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func Test_localStoreExecutor(t *testing.T) {
|
||
|
ctx := context.TODO()
|
||
|
addr := testObjectAddress(t)
|
||
|
|
||
|
t.Run("put", func(t *testing.T) {
|
||
|
epoch := uint64(100)
|
||
|
obj := new(Object)
|
||
|
putErr := internal.Error("test error for put")
|
||
|
|
||
|
ls := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct local store put params", func(t *testing.T) {
|
||
|
v, ok := items[0].(context.Context).Value(localstore.StoreEpochValue).(uint64)
|
||
|
require.True(t, ok)
|
||
|
require.Equal(t, epoch, v)
|
||
|
|
||
|
require.Equal(t, obj, items[1].(*Object))
|
||
|
})
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
epochRecv: &testExecutionEntity{
|
||
|
res: epoch,
|
||
|
},
|
||
|
localStore: ls,
|
||
|
}
|
||
|
|
||
|
require.NoError(t, s.putObject(ctx, obj))
|
||
|
|
||
|
ls.err = putErr
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.putObject(ctx, obj),
|
||
|
errPutLocal.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("get", func(t *testing.T) {
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
getErr := internal.Error("test error for get")
|
||
|
|
||
|
ls := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct local store get params", func(t *testing.T) {
|
||
|
require.Equal(t, addr, items[0].(Address))
|
||
|
})
|
||
|
},
|
||
|
err: getErr,
|
||
|
}
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: ls,
|
||
|
}
|
||
|
|
||
|
res, err := s.getObject(ctx, addr)
|
||
|
require.EqualError(t, err, getErr.Error())
|
||
|
require.Nil(t, res)
|
||
|
|
||
|
ls.err = errors.Wrap(core.ErrNotFound, "wrap message")
|
||
|
|
||
|
res, err = s.getObject(ctx, addr)
|
||
|
require.EqualError(t, err, errIncompleteOperation.Error())
|
||
|
require.Nil(t, res)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
obj := new(Object)
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: &testExecutionEntity{
|
||
|
res: obj,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.getObject(ctx, addr)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, obj, res)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("head", func(t *testing.T) {
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
headErr := internal.Error("test error for head")
|
||
|
|
||
|
ls := &testExecutionEntity{
|
||
|
err: headErr,
|
||
|
}
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: ls,
|
||
|
}
|
||
|
|
||
|
res, err := s.headObject(ctx, addr)
|
||
|
require.EqualError(t, err, headErr.Error())
|
||
|
require.Nil(t, res)
|
||
|
|
||
|
ls.err = errors.Wrap(core.ErrNotFound, "wrap message")
|
||
|
|
||
|
res, err = s.headObject(ctx, addr)
|
||
|
require.EqualError(t, err, errIncompleteOperation.Error())
|
||
|
require.Nil(t, res)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
obj := new(Object)
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: &testExecutionEntity{
|
||
|
res: &Meta{Object: obj},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.headObject(ctx, addr)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, obj, res)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("get range", func(t *testing.T) {
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
rngErr := internal.Error("test error for range reader")
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: &testExecutionEntity{
|
||
|
err: rngErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.getRange(ctx, addr, Range{})
|
||
|
require.EqualError(t, err, rngErr.Error())
|
||
|
require.Empty(t, res)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
rng := Range{Offset: 1, Length: 1}
|
||
|
|
||
|
d := testData(t, 10)
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct local store pread params", func(t *testing.T) {
|
||
|
require.Equal(t, addr, items[0].(Address))
|
||
|
require.Equal(t, rng, items[1].(Range))
|
||
|
})
|
||
|
},
|
||
|
res: d,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.getRange(ctx, addr, rng)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, d, res)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("get range hash", func(t *testing.T) {
|
||
|
t.Run("empty range list", func(t *testing.T) {
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: new(testExecutionEntity),
|
||
|
}
|
||
|
|
||
|
res, err := s.getHashes(ctx, addr, nil, nil)
|
||
|
require.NoError(t, err)
|
||
|
require.Empty(t, res)
|
||
|
})
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
rhErr := internal.Error("test error for range hasher")
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
localStore: &testExecutionEntity{
|
||
|
err: rhErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.getHashes(ctx, addr, make([]Range, 1), nil)
|
||
|
require.EqualError(t, err, errors.Wrapf(rhErr, emRangeReadFail, 1).Error())
|
||
|
require.Empty(t, res)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
rngs := []Range{
|
||
|
{Offset: 0, Length: 0},
|
||
|
{Offset: 1, Length: 1},
|
||
|
}
|
||
|
|
||
|
d := testData(t, 64)
|
||
|
salt := testData(t, 20)
|
||
|
|
||
|
callNum := 0
|
||
|
|
||
|
s := &localStoreExecutor{
|
||
|
salitor: hash.SaltXOR,
|
||
|
localStore: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct local store pread params", func(t *testing.T) {
|
||
|
require.Equal(t, addr, items[0].(Address))
|
||
|
require.Equal(t, rngs[callNum], items[1].(Range))
|
||
|
callNum++
|
||
|
})
|
||
|
},
|
||
|
res: d,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.getHashes(ctx, addr, rngs, salt)
|
||
|
require.NoError(t, err)
|
||
|
require.Len(t, res, len(rngs))
|
||
|
for i := range rngs {
|
||
|
require.Equal(t, hash.Sum(hash.SaltXOR(d, salt)), res[i])
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func Test_coreHandler_HandleResult(t *testing.T) {
|
||
|
ctx := context.TODO()
|
||
|
node := testNode(t, 1)
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
handled := false
|
||
|
err := internal.Error("")
|
||
|
|
||
|
s := &coreHandler{
|
||
|
traverser: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct traverser params", func(t *testing.T) {
|
||
|
require.Equal(t, node, items[0].(multiaddr.Multiaddr))
|
||
|
require.False(t, items[1].(bool))
|
||
|
})
|
||
|
},
|
||
|
},
|
||
|
itemHandler: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
handled = true
|
||
|
},
|
||
|
},
|
||
|
resLogger: new(coreResultLogger),
|
||
|
}
|
||
|
|
||
|
s.HandleResult(ctx, node, nil, err)
|
||
|
|
||
|
require.False(t, handled)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
handled := false
|
||
|
res := testData(t, 10)
|
||
|
|
||
|
s := &coreHandler{
|
||
|
traverser: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct traverser params", func(t *testing.T) {
|
||
|
require.Equal(t, node, items[0].(multiaddr.Multiaddr))
|
||
|
require.True(t, items[1].(bool))
|
||
|
})
|
||
|
},
|
||
|
},
|
||
|
itemHandler: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, res, items[0])
|
||
|
},
|
||
|
},
|
||
|
resLogger: new(coreResultLogger),
|
||
|
}
|
||
|
|
||
|
s.HandleResult(ctx, node, res, nil)
|
||
|
|
||
|
require.False(t, handled)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func Test_localOperationExecutor_executeOperation(t *testing.T) {
|
||
|
ctx := context.TODO()
|
||
|
|
||
|
addr := testObjectAddress(t)
|
||
|
|
||
|
obj := &Object{
|
||
|
SystemHeader: SystemHeader{
|
||
|
ID: addr.ObjectID,
|
||
|
CID: addr.CID,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
t.Run("wrong type", func(t *testing.T) {
|
||
|
req := &testExecutionEntity{
|
||
|
res: object.RequestType(-1),
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
new(localOperationExecutor).executeOperation(ctx, req, nil),
|
||
|
errors.Errorf(pmWrongRequestType, req).Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("put", func(t *testing.T) {
|
||
|
req := &putRequest{PutRequest: &object.PutRequest{
|
||
|
R: &object.PutRequest_Header{
|
||
|
Header: &object.PutRequest_PutHeader{
|
||
|
Object: obj,
|
||
|
},
|
||
|
},
|
||
|
}}
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
putErr := internal.Error("test error for put")
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
objStore: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, obj, items[0].(*Object))
|
||
|
},
|
||
|
err: putErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, nil),
|
||
|
putErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
h := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, addr, *items[0].(*Address))
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
objStore: new(testExecutionEntity),
|
||
|
}
|
||
|
|
||
|
require.NoError(t, s.executeOperation(ctx, req, h))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("get", func(t *testing.T) {
|
||
|
req := newRawGetInfo()
|
||
|
req.setAddress(addr)
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
getErr := internal.Error("test error for get")
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
objRecv: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, addr, items[0].(Address))
|
||
|
},
|
||
|
err: getErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, nil),
|
||
|
getErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
h := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, obj, items[0].(*Object))
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
objRecv: &testExecutionEntity{
|
||
|
res: obj,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.NoError(t, s.executeOperation(ctx, req, h))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("head", func(t *testing.T) {
|
||
|
req := &transportRequest{serviceRequest: &object.HeadRequest{
|
||
|
Address: addr,
|
||
|
}}
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
headErr := internal.Error("test error for head")
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
headRecv: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, addr, items[0].(Address))
|
||
|
},
|
||
|
err: headErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, nil),
|
||
|
headErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
h := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, obj, items[0].(*Object))
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
headRecv: &testExecutionEntity{
|
||
|
res: obj,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.NoError(t, s.executeOperation(ctx, req, h))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("search", func(t *testing.T) {
|
||
|
cid := testObjectAddress(t).CID
|
||
|
testQuery := testData(t, 10)
|
||
|
|
||
|
req := &transportRequest{serviceRequest: &object.SearchRequest{
|
||
|
ContainerID: cid,
|
||
|
Query: testQuery,
|
||
|
}}
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
searchErr := internal.Error("test error for search")
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
queryImp: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, cid, items[0].(CID))
|
||
|
require.Equal(t, testQuery, items[1].([]byte))
|
||
|
require.Equal(t, 1, items[2].(int))
|
||
|
},
|
||
|
err: searchErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, nil),
|
||
|
searchErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
addrList := testAddrList(t, 5)
|
||
|
|
||
|
h := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, addrList, items[0].([]Address))
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
queryImp: &testExecutionEntity{
|
||
|
res: addrList,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.NoError(t, s.executeOperation(ctx, req, h))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("get range", func(t *testing.T) {
|
||
|
rng := Range{Offset: 1, Length: 1}
|
||
|
|
||
|
req := newRawRangeInfo()
|
||
|
req.setAddress(addr)
|
||
|
req.setRange(rng)
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
rrErr := internal.Error("test error for range reader")
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
rngReader: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, addr, items[0].(Address))
|
||
|
require.Equal(t, rng, items[1].(Range))
|
||
|
},
|
||
|
err: rrErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, nil),
|
||
|
rrErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
data := testData(t, 10)
|
||
|
|
||
|
h := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
d, err := ioutil.ReadAll(items[0].(io.Reader))
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, data, d)
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
rngReader: &testExecutionEntity{
|
||
|
res: data,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.NoError(t, s.executeOperation(ctx, req, h))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.Run("get range hash", func(t *testing.T) {
|
||
|
rngs := []Range{
|
||
|
{Offset: 0, Length: 0},
|
||
|
{Offset: 1, Length: 1},
|
||
|
}
|
||
|
|
||
|
salt := testData(t, 10)
|
||
|
|
||
|
req := newRawRangeHashInfo()
|
||
|
req.setAddress(addr)
|
||
|
req.setRanges(rngs)
|
||
|
req.setSalt(salt)
|
||
|
|
||
|
t.Run("error", func(t *testing.T) {
|
||
|
rhErr := internal.Error("test error for range hasher")
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
rngHasher: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, addr, items[0].(Address))
|
||
|
require.Equal(t, rngs, items[1].([]Range))
|
||
|
require.Equal(t, salt, items[2].([]byte))
|
||
|
},
|
||
|
err: rhErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t,
|
||
|
s.executeOperation(ctx, req, nil),
|
||
|
rhErr.Error(),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
t.Run("success", func(t *testing.T) {
|
||
|
hashes := []Hash{
|
||
|
hash.Sum(testData(t, 10)),
|
||
|
hash.Sum(testData(t, 10)),
|
||
|
}
|
||
|
|
||
|
h := &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, hashes, items[0].([]Hash))
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &localOperationExecutor{
|
||
|
rngHasher: &testExecutionEntity{
|
||
|
res: hashes,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.NoError(t, s.executeOperation(ctx, req, h))
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func Test_coreOperationFinalizer_completeExecution(t *testing.T) {
|
||
|
ctx := context.TODO()
|
||
|
|
||
|
t.Run("address store failure", func(t *testing.T) {
|
||
|
asErr := internal.Error("test error for address store")
|
||
|
|
||
|
s := &coreOperationFinalizer{
|
||
|
interceptorPreparer: &testExecutionEntity{
|
||
|
err: asErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
require.EqualError(t, s.completeExecution(ctx, operationParams{
|
||
|
metaInfo: &transportRequest{serviceRequest: new(object.SearchRequest)},
|
||
|
}), asErr.Error())
|
||
|
})
|
||
|
|
||
|
t.Run("correct execution construction", func(t *testing.T) {
|
||
|
req := &transportRequest{
|
||
|
serviceRequest: &object.SearchRequest{
|
||
|
ContainerID: testObjectAddress(t).CID,
|
||
|
Query: testData(t, 10),
|
||
|
QueryVersion: 1,
|
||
|
},
|
||
|
timeout: 10 * time.Second,
|
||
|
}
|
||
|
|
||
|
req.SetTTL(10)
|
||
|
|
||
|
itemHandler := new(testExecutionEntity)
|
||
|
opParams := operationParams{
|
||
|
computableParams: computableParams{
|
||
|
addr: testObjectAddress(t),
|
||
|
stopCount: 2,
|
||
|
allowPartialResult: false,
|
||
|
tryPreviousNetMap: false,
|
||
|
selfForward: true,
|
||
|
maxRecycleCount: 7,
|
||
|
},
|
||
|
metaInfo: req,
|
||
|
itemHandler: itemHandler,
|
||
|
}
|
||
|
|
||
|
curPl := new(testExecutionEntity)
|
||
|
prevPl := new(testExecutionEntity)
|
||
|
wp := new(testExecutionEntity)
|
||
|
s := &coreOperationFinalizer{
|
||
|
curPlacementBuilder: curPl,
|
||
|
prevPlacementBuilder: prevPl,
|
||
|
interceptorPreparer: &testExecutionEntity{
|
||
|
res: func(context.Context, multiaddr.Multiaddr) bool { return true },
|
||
|
},
|
||
|
workerPool: wp,
|
||
|
traverseExec: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct traverse executor params", func(t *testing.T) {
|
||
|
p := items[0].(implementations.TraverseParams)
|
||
|
|
||
|
require.True(t, p.ExecutionInterceptor(ctx, nil))
|
||
|
require.Equal(t, req, p.TransportInfo)
|
||
|
require.Equal(t, wp, p.WorkerPool)
|
||
|
|
||
|
tr := p.Traverser.(*coreTraverser)
|
||
|
require.Equal(t, opParams.addr, tr.addr)
|
||
|
require.Equal(t, opParams.tryPreviousNetMap, tr.tryPrevNM)
|
||
|
require.Equal(t, curPl, tr.curPlacementBuilder)
|
||
|
require.Equal(t, prevPl, tr.prevPlacementBuilder)
|
||
|
require.Equal(t, opParams.maxRecycleCount, tr.maxRecycleCount)
|
||
|
require.Equal(t, opParams.stopCount, tr.stopCount)
|
||
|
|
||
|
h := p.Handler.(*coreHandler)
|
||
|
require.Equal(t, tr, h.traverser)
|
||
|
require.Equal(t, itemHandler, h.itemHandler)
|
||
|
})
|
||
|
},
|
||
|
},
|
||
|
log: zap.L(),
|
||
|
}
|
||
|
|
||
|
require.EqualError(t, s.completeExecution(ctx, opParams), errIncompleteOperation.Error())
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func Test_coreInterceptorPreparer_prepareInterceptor(t *testing.T) {
|
||
|
t.Run("address store failure", func(t *testing.T) {
|
||
|
asErr := internal.Error("test error for address store")
|
||
|
|
||
|
s := &coreInterceptorPreparer{
|
||
|
addressStore: &testExecutionEntity{
|
||
|
err: asErr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.prepareInterceptor(interceptorItems{})
|
||
|
require.EqualError(t, err, asErr.Error())
|
||
|
require.Nil(t, res)
|
||
|
})
|
||
|
|
||
|
t.Run("correct interceptor", func(t *testing.T) {
|
||
|
ctx := context.TODO()
|
||
|
selfAddr := testNode(t, 0)
|
||
|
|
||
|
t.Run("local node", func(t *testing.T) {
|
||
|
req := new(transportRequest)
|
||
|
itemHandler := new(testExecutionEntity)
|
||
|
|
||
|
localErr := internal.Error("test error for local executor")
|
||
|
|
||
|
p := interceptorItems{
|
||
|
selfForward: true,
|
||
|
handler: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
t.Run("correct local executor params", func(t *testing.T) {
|
||
|
require.Equal(t, selfAddr, items[0].(multiaddr.Multiaddr))
|
||
|
require.Nil(t, items[1])
|
||
|
require.EqualError(t, items[2].(error), localErr.Error())
|
||
|
})
|
||
|
},
|
||
|
},
|
||
|
metaInfo: req,
|
||
|
itemHandler: itemHandler,
|
||
|
}
|
||
|
|
||
|
s := &coreInterceptorPreparer{
|
||
|
localExec: &testExecutionEntity{
|
||
|
f: func(items ...interface{}) {
|
||
|
require.Equal(t, req, items[0].(transport.MetaInfo))
|
||
|
require.Equal(t, itemHandler, items[1].(responseItemHandler))
|
||
|
},
|
||
|
err: localErr,
|
||
|
},
|
||
|
addressStore: &testExecutionEntity{
|
||
|
res: selfAddr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.prepareInterceptor(p)
|
||
|
require.NoError(t, err)
|
||
|
require.False(t, res(ctx, selfAddr))
|
||
|
})
|
||
|
|
||
|
t.Run("remote node", func(t *testing.T) {
|
||
|
node := testNode(t, 1)
|
||
|
remoteNode := testNode(t, 2)
|
||
|
|
||
|
p := interceptorItems{}
|
||
|
|
||
|
s := &coreInterceptorPreparer{
|
||
|
addressStore: &testExecutionEntity{
|
||
|
res: remoteNode,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
res, err := s.prepareInterceptor(p)
|
||
|
require.NoError(t, err)
|
||
|
require.False(t, res(ctx, node))
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// testAddrList returns count random object addresses.
|
||
|
func testAddrList(t *testing.T, count int) (res []Address) {
|
||
|
for i := 0; i < count; i++ {
|
||
|
res = append(res, testObjectAddress(t))
|
||
|
}
|
||
|
return
|
||
|
}
|