git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/client/object_search_test.go (about) 1 package client 2 3 import ( 4 "crypto/ecdsa" 5 "errors" 6 "fmt" 7 "io" 8 "testing" 9 10 v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" 11 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" 12 signatureV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" 13 oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" 14 oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" 15 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func TestObjectSearch(t *testing.T) { 20 ids := make([]oid.ID, 20) 21 for i := range ids { 22 ids[i] = oidtest.ID() 23 } 24 25 p, resp := testListReaderResponse(t) 26 27 buf := make([]oid.ID, 2) 28 checkRead := func(t *testing.T, expected []oid.ID) { 29 n, ok := resp.Read(buf) 30 require.True(t, ok == (len(expected) == len(buf)), "expected no error") 31 require.Equal(t, len(expected), n, "expected %d items to be read", len(expected)) 32 require.Equal(t, expected, buf[:len(expected)]) 33 } 34 35 // nil panic 36 require.Panics(t, func() { resp.Read(nil) }) 37 38 // both ID fetched 39 resp.stream = newSearchStream(p, nil, ids[:3]) 40 checkRead(t, ids[:2]) 41 42 // one ID cached, second fetched 43 resp.stream = newSearchStream(p, nil, ids[3:6]) 44 checkRead(t, ids[2:4]) 45 46 // both ID cached 47 resp.stream = nil // shouldn't be called, panic if so 48 checkRead(t, ids[4:6]) 49 50 // both ID fetched in 2 requests, with empty one in the middle 51 resp.stream = newSearchStream(p, nil, ids[6:7], nil, ids[7:8]) 52 checkRead(t, ids[6:8]) 53 54 // read from tail multiple times 55 resp.stream = newSearchStream(p, nil, ids[8:11]) 56 buf = buf[:1] 57 checkRead(t, ids[8:9]) 58 checkRead(t, ids[9:10]) 59 checkRead(t, ids[10:11]) 60 61 // handle EOF 62 buf = buf[:2] 63 resp.stream = newSearchStream(p, io.EOF, ids[11:12]) 64 checkRead(t, ids[11:12]) 65 } 66 67 func TestObjectIterate(t *testing.T) { 68 ids := make([]oid.ID, 3) 69 for i := range ids { 70 ids[i] = oidtest.ID() 71 } 72 73 t.Run("iterate all sequence", func(t *testing.T) { 74 p, resp := testListReaderResponse(t) 75 76 resp.stream = newSearchStream(p, io.EOF, ids[0:2], nil, ids[2:3]) 77 78 var actual []oid.ID 79 require.NoError(t, resp.Iterate(func(id oid.ID) bool { 80 actual = append(actual, id) 81 return false 82 })) 83 require.Equal(t, ids[:3], actual) 84 }) 85 t.Run("stop by return value", func(t *testing.T) { 86 p, resp := testListReaderResponse(t) 87 88 var actual []oid.ID 89 resp.stream = &singleStreamResponder{key: p, idList: [][]oid.ID{ids}} 90 require.NoError(t, resp.Iterate(func(id oid.ID) bool { 91 actual = append(actual, id) 92 return len(actual) == 2 93 })) 94 require.Equal(t, ids[:2], actual) 95 }) 96 t.Run("stop after error", func(t *testing.T) { 97 p, resp := testListReaderResponse(t) 98 expectedErr := errors.New("test error") 99 100 resp.stream = newSearchStream(p, expectedErr, ids[:2]) 101 102 var actual []oid.ID 103 err := resp.Iterate(func(id oid.ID) bool { 104 actual = append(actual, id) 105 return false 106 }) 107 require.True(t, errors.Is(err, expectedErr), "got: %v", err) 108 require.Equal(t, ids[:2], actual) 109 }) 110 } 111 112 func testListReaderResponse(t *testing.T) (*ecdsa.PrivateKey, *ObjectListReader) { 113 p, err := keys.NewPrivateKey() 114 require.NoError(t, err) 115 116 return &p.PrivateKey, &ObjectListReader{ 117 cancelCtxStream: func() {}, 118 client: &Client{}, 119 tail: nil, 120 } 121 } 122 123 func newSearchStream(key *ecdsa.PrivateKey, endError error, idList ...[]oid.ID) *singleStreamResponder { 124 return &singleStreamResponder{ 125 key: key, 126 endError: endError, 127 idList: idList, 128 } 129 } 130 131 type singleStreamResponder struct { 132 key *ecdsa.PrivateKey 133 n int 134 endError error 135 idList [][]oid.ID 136 } 137 138 func (s *singleStreamResponder) Read(resp *v2object.SearchResponse) error { 139 if s.n >= len(s.idList) { 140 if s.endError != nil { 141 return s.endError 142 } 143 panic("unexpected call to `Read`") 144 } 145 146 var body v2object.SearchResponseBody 147 148 if s.idList[s.n] != nil { 149 ids := make([]refs.ObjectID, len(s.idList[s.n])) 150 for i := range s.idList[s.n] { 151 s.idList[s.n][i].WriteToV2(&ids[i]) 152 } 153 body.SetIDList(ids) 154 } 155 resp.SetBody(&body) 156 157 err := signatureV2.SignServiceMessage(s.key, resp) 158 if err != nil { 159 panic(fmt.Errorf("error: %w", err)) 160 } 161 162 s.n++ 163 return nil 164 }