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 }