2022-02-25 10:20:15 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2022-08-23 18:56:55 +00:00
|
|
|
"fmt"
|
2022-02-25 10:20:15 +00:00
|
|
|
"io"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2022-08-23 18:56:55 +00:00
|
|
|
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
|
2022-02-25 10:20:15 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
2023-04-25 08:31:27 +00:00
|
|
|
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
|
|
|
|
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
|
2022-02-25 10:20:15 +00:00
|
|
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
|
|
|
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestObjectSearch(t *testing.T) {
|
|
|
|
ids := make([]oid.ID, 20)
|
|
|
|
for i := range ids {
|
2022-04-11 16:25:14 +00:00
|
|
|
ids[i] = oidtest.ID()
|
2022-02-25 10:20:15 +00:00
|
|
|
}
|
|
|
|
|
2022-08-23 18:56:55 +00:00
|
|
|
p, resp := testListReaderResponse(t)
|
2022-02-25 10:20:15 +00:00
|
|
|
|
|
|
|
buf := make([]oid.ID, 2)
|
|
|
|
checkRead := func(t *testing.T, expected []oid.ID) {
|
|
|
|
n, ok := resp.Read(buf)
|
|
|
|
require.True(t, ok == (len(expected) == len(buf)), "expected no error")
|
|
|
|
require.Equal(t, len(expected), n, "expected %d items to be read", len(expected))
|
|
|
|
require.Equal(t, expected, buf[:len(expected)])
|
|
|
|
}
|
|
|
|
|
|
|
|
// nil panic
|
|
|
|
require.Panics(t, func() { resp.Read(nil) })
|
|
|
|
|
|
|
|
// both ID fetched
|
2022-08-23 18:56:55 +00:00
|
|
|
resp.stream = newSearchStream(p, nil, ids[:3])
|
2022-02-25 10:20:15 +00:00
|
|
|
checkRead(t, ids[:2])
|
|
|
|
|
|
|
|
// one ID cached, second fetched
|
2022-08-23 18:56:55 +00:00
|
|
|
resp.stream = newSearchStream(p, nil, ids[3:6])
|
2022-02-25 10:20:15 +00:00
|
|
|
checkRead(t, ids[2:4])
|
|
|
|
|
|
|
|
// both ID cached
|
2022-08-23 18:56:55 +00:00
|
|
|
resp.stream = nil // shouldn't be called, panic if so
|
2022-02-25 10:20:15 +00:00
|
|
|
checkRead(t, ids[4:6])
|
|
|
|
|
|
|
|
// both ID fetched in 2 requests, with empty one in the middle
|
2022-08-23 18:56:55 +00:00
|
|
|
resp.stream = newSearchStream(p, nil, ids[6:7], nil, ids[7:8])
|
2022-02-25 10:20:15 +00:00
|
|
|
checkRead(t, ids[6:8])
|
|
|
|
|
|
|
|
// read from tail multiple times
|
2022-08-23 18:56:55 +00:00
|
|
|
resp.stream = newSearchStream(p, nil, ids[8:11])
|
2022-02-25 10:20:15 +00:00
|
|
|
buf = buf[:1]
|
|
|
|
checkRead(t, ids[8:9])
|
|
|
|
checkRead(t, ids[9:10])
|
|
|
|
checkRead(t, ids[10:11])
|
|
|
|
|
|
|
|
// handle EOF
|
|
|
|
buf = buf[:2]
|
2022-08-23 18:56:55 +00:00
|
|
|
resp.stream = newSearchStream(p, io.EOF, ids[11:12])
|
2022-02-25 10:20:15 +00:00
|
|
|
checkRead(t, ids[11:12])
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestObjectIterate(t *testing.T) {
|
|
|
|
ids := make([]oid.ID, 3)
|
|
|
|
for i := range ids {
|
2022-04-11 16:25:14 +00:00
|
|
|
ids[i] = oidtest.ID()
|
2022-02-25 10:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("iterate all sequence", func(t *testing.T) {
|
2022-08-23 18:56:55 +00:00
|
|
|
p, resp := testListReaderResponse(t)
|
|
|
|
|
|
|
|
resp.stream = newSearchStream(p, io.EOF, ids[0:2], nil, ids[2:3])
|
2022-02-25 10:20:15 +00:00
|
|
|
|
|
|
|
var actual []oid.ID
|
|
|
|
require.NoError(t, resp.Iterate(func(id oid.ID) bool {
|
|
|
|
actual = append(actual, id)
|
|
|
|
return false
|
|
|
|
}))
|
|
|
|
require.Equal(t, ids[:3], actual)
|
|
|
|
})
|
|
|
|
t.Run("stop by return value", func(t *testing.T) {
|
2022-08-23 18:56:55 +00:00
|
|
|
p, resp := testListReaderResponse(t)
|
2022-02-25 10:20:15 +00:00
|
|
|
|
|
|
|
var actual []oid.ID
|
2023-04-25 08:31:27 +00:00
|
|
|
resp.stream = &singleStreamResponder{signer: p, idList: [][]oid.ID{ids}}
|
2022-02-25 10:20:15 +00:00
|
|
|
require.NoError(t, resp.Iterate(func(id oid.ID) bool {
|
|
|
|
actual = append(actual, id)
|
|
|
|
return len(actual) == 2
|
|
|
|
}))
|
|
|
|
require.Equal(t, ids[:2], actual)
|
|
|
|
})
|
|
|
|
t.Run("stop after error", func(t *testing.T) {
|
2022-08-23 18:56:55 +00:00
|
|
|
p, resp := testListReaderResponse(t)
|
2022-02-25 10:20:15 +00:00
|
|
|
expectedErr := errors.New("test error")
|
|
|
|
|
2022-08-23 18:56:55 +00:00
|
|
|
resp.stream = newSearchStream(p, expectedErr, ids[:2])
|
2022-02-25 10:20:15 +00:00
|
|
|
|
2022-08-23 18:56:55 +00:00
|
|
|
var actual []oid.ID
|
2022-02-25 10:20:15 +00:00
|
|
|
err := resp.Iterate(func(id oid.ID) bool {
|
|
|
|
actual = append(actual, id)
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
require.True(t, errors.Is(err, expectedErr), "got: %v", err)
|
|
|
|
require.Equal(t, ids[:2], actual)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-04-25 08:31:27 +00:00
|
|
|
func testListReaderResponse(t *testing.T) (neofscrypto.Signer, *ObjectListReader) {
|
2022-02-25 10:20:15 +00:00
|
|
|
p, err := keys.NewPrivateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-04-25 08:31:27 +00:00
|
|
|
return neofsecdsa.Signer(p.PrivateKey), &ObjectListReader{
|
2022-02-25 10:20:15 +00:00
|
|
|
cancelCtxStream: func() {},
|
2022-08-23 18:56:55 +00:00
|
|
|
client: &Client{},
|
|
|
|
tail: nil,
|
2022-02-25 10:20:15 +00:00
|
|
|
}
|
2022-08-23 18:56:55 +00:00
|
|
|
}
|
2022-02-25 10:20:15 +00:00
|
|
|
|
2023-04-25 08:31:27 +00:00
|
|
|
func newSearchStream(signer neofscrypto.Signer, endError error, idList ...[]oid.ID) *singleStreamResponder {
|
2022-08-23 18:56:55 +00:00
|
|
|
return &singleStreamResponder{
|
2023-04-25 08:31:27 +00:00
|
|
|
signer: signer,
|
2022-08-23 18:56:55 +00:00
|
|
|
endError: endError,
|
|
|
|
idList: idList,
|
|
|
|
}
|
|
|
|
}
|
2022-02-25 10:20:15 +00:00
|
|
|
|
2022-08-23 18:56:55 +00:00
|
|
|
type singleStreamResponder struct {
|
2023-04-25 08:31:27 +00:00
|
|
|
signer neofscrypto.Signer
|
2022-08-23 18:56:55 +00:00
|
|
|
n int
|
|
|
|
endError error
|
|
|
|
idList [][]oid.ID
|
|
|
|
}
|
2022-04-11 16:25:14 +00:00
|
|
|
|
2022-08-23 18:56:55 +00:00
|
|
|
func (s *singleStreamResponder) Read(resp *v2object.SearchResponse) error {
|
|
|
|
if s.n >= len(s.idList) {
|
|
|
|
if s.endError != nil {
|
|
|
|
return s.endError
|
2022-02-25 10:20:15 +00:00
|
|
|
}
|
2022-08-23 18:56:55 +00:00
|
|
|
panic("unexpected call to `Read`")
|
|
|
|
}
|
|
|
|
|
|
|
|
var body v2object.SearchResponseBody
|
|
|
|
|
|
|
|
if s.idList[s.n] != nil {
|
|
|
|
ids := make([]refs.ObjectID, len(s.idList[s.n]))
|
|
|
|
for i := range s.idList[s.n] {
|
|
|
|
s.idList[s.n][i].WriteToV2(&ids[i])
|
2022-02-25 10:20:15 +00:00
|
|
|
}
|
2022-08-23 18:56:55 +00:00
|
|
|
body.SetIDList(ids)
|
|
|
|
}
|
|
|
|
resp.SetBody(&body)
|
|
|
|
|
2023-04-25 08:31:27 +00:00
|
|
|
err := signServiceMessage(s.signer, resp)
|
2022-08-23 18:56:55 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("error: %w", err))
|
2022-02-25 10:20:15 +00:00
|
|
|
}
|
2022-08-23 18:56:55 +00:00
|
|
|
|
|
|
|
s.n++
|
|
|
|
return nil
|
2022-02-25 10:20:15 +00:00
|
|
|
}
|