1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
12 "git.curoverse.com/arvados.git/sdk/go/arvados"
13 "git.curoverse.com/arvados.git/sdk/go/arvadostest"
14 check "gopkg.in/check.v1"
17 var _ = check.Suite(&CollectionListSuite{})
19 type collectionLister struct {
21 ItemsToReturn []arvados.Collection
25 func (cl *collectionLister) matchFilters(c arvados.Collection, filters []arvados.Filter) bool {
27 for _, f := range filters {
28 if f.Attr == "uuid" && f.Operator == "=" {
29 s, ok := f.Operand.(string)
30 if ok && s == c.UUID {
33 } else if f.Attr == "uuid" && f.Operator == "in" {
34 if operand, ok := f.Operand.([]string); ok {
35 for _, s := range operand {
40 } else if operand, ok := f.Operand.([]interface{}); ok {
41 for _, s := range operand {
42 if s, ok := s.(string); ok && s == c.UUID {
53 func (cl *collectionLister) CollectionList(ctx context.Context, options arvados.ListOptions) (resp arvados.CollectionList, _ error) {
54 cl.APIStub.CollectionList(ctx, options)
55 for _, c := range cl.ItemsToReturn {
56 if cl.MaxPageSize > 0 && len(resp.Items) >= cl.MaxPageSize {
59 if options.Limit >= 0 && len(resp.Items) >= options.Limit {
62 if cl.matchFilters(c, options.Filters) {
63 resp.Items = append(resp.Items, c)
69 type CollectionListSuite struct {
71 ids []string // aaaaa, bbbbb, ccccc
72 uuids [][]string // [[aa-*, aa-*, aa-*], [bb-*, bb-*, ...], ...]
73 backends []*collectionLister
76 func (s *CollectionListSuite) SetUpTest(c *check.C) {
77 s.FederationSuite.SetUpTest(c)
82 for i, id := range []string{"aaaaa", "bbbbb", "ccccc"} {
83 cl := &collectionLister{}
84 s.ids = append(s.ids, id)
85 s.uuids = append(s.uuids, nil)
86 for j := 0; j < 5; j++ {
87 uuid := fmt.Sprintf("%s-4zz18-%s%010d", id, id, j)
88 s.uuids[i] = append(s.uuids[i], uuid)
89 cl.ItemsToReturn = append(cl.ItemsToReturn, arvados.Collection{
93 s.backends = append(s.backends, cl)
97 // call some backends directly via API
98 s.addDirectRemote(c, id, cl)
100 // call some backends through rpc->router->API
101 // to ensure nothing is lost in translation
102 s.addHTTPRemote(c, id, cl)
107 type listTrial struct {
112 filters []arvados.Filter
114 expectCalls []int // number of API calls to backends
118 func (s *CollectionListSuite) TestCollectionListNoUUIDFilters(c *check.C) {
122 expectUUIDs: []string{s.uuids[0][0]},
123 expectCalls: []int{1, 0, 0},
127 func (s *CollectionListSuite) TestCollectionListOneLocal(c *check.C) {
131 filters: []arvados.Filter{{"uuid", "=", s.uuids[0][0]}},
132 expectUUIDs: []string{s.uuids[0][0]},
133 expectCalls: []int{1, 0, 0},
137 func (s *CollectionListSuite) TestCollectionListOneRemote(c *check.C) {
141 filters: []arvados.Filter{{"uuid", "=", s.uuids[1][0]}},
142 expectUUIDs: []string{s.uuids[1][0]},
143 expectCalls: []int{0, 1, 0},
147 func (s *CollectionListSuite) TestCollectionListOneLocalUsingInOperator(c *check.C) {
151 filters: []arvados.Filter{{"uuid", "in", []string{s.uuids[0][0]}}},
152 expectUUIDs: []string{s.uuids[0][0]},
153 expectCalls: []int{1, 0, 0},
157 func (s *CollectionListSuite) TestCollectionListOneRemoteUsingInOperator(c *check.C) {
161 filters: []arvados.Filter{{"uuid", "in", []string{s.uuids[1][1]}}},
162 expectUUIDs: []string{s.uuids[1][1]},
163 expectCalls: []int{0, 1, 0},
167 func (s *CollectionListSuite) TestCollectionListOneLocalOneRemote(c *check.C) {
171 filters: []arvados.Filter{{"uuid", "in", []string{s.uuids[0][0], s.uuids[1][0]}}},
172 expectUUIDs: []string{s.uuids[0][0], s.uuids[1][0]},
173 expectCalls: []int{1, 1, 0},
177 func (s *CollectionListSuite) TestCollectionListTwoRemotes(c *check.C) {
181 filters: []arvados.Filter{{"uuid", "in", []string{s.uuids[2][0], s.uuids[1][0]}}},
182 expectUUIDs: []string{s.uuids[1][0], s.uuids[2][0]},
183 expectCalls: []int{0, 1, 1},
187 func (s *CollectionListSuite) TestCollectionListSatisfyAllFilters(c *check.C) {
188 s.cluster.API.MaxItemsPerResponse = 2
192 filters: []arvados.Filter{
193 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][1], s.uuids[2][0], s.uuids[2][1], s.uuids[2][2]}},
194 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][2], s.uuids[2][1]}},
196 expectUUIDs: []string{s.uuids[0][0], s.uuids[2][1]},
197 expectCalls: []int{1, 0, 1},
201 func (s *CollectionListSuite) TestCollectionListEmptySet(c *check.C) {
205 filters: []arvados.Filter{{"uuid", "in", []string{}}},
206 expectUUIDs: []string{},
207 expectCalls: []int{0, 0, 0},
211 func (s *CollectionListSuite) TestCollectionListUnmatchableUUID(c *check.C) {
215 filters: []arvados.Filter{
216 {"uuid", "in", []string{s.uuids[0][0], "abcdefg"}},
217 {"uuid", "in", []string{s.uuids[0][0], "bbbbb-4zz18-bogus"}},
218 {"uuid", "in", []string{s.uuids[0][0], "bogus-4zz18-bogus"}},
220 expectUUIDs: []string{s.uuids[0][0]},
221 expectCalls: []int{1, 0, 0},
225 func (s *CollectionListSuite) TestCollectionListMultiPage(c *check.C) {
226 for i := range s.backends {
227 s.uuids[i] = s.uuids[i][:3]
228 s.backends[i].ItemsToReturn = s.backends[i].ItemsToReturn[:3]
230 s.cluster.API.MaxItemsPerResponse = 9
231 for _, stub := range s.backends {
234 allUUIDs := append(append(append([]string(nil), s.uuids[0]...), s.uuids[1]...), s.uuids[2]...)
238 filters: []arvados.Filter{{"uuid", "in", append([]string(nil), allUUIDs...)}},
239 expectUUIDs: allUUIDs,
240 expectCalls: []int{2, 2, 2},
244 func (s *CollectionListSuite) TestCollectionListMultiSiteExtraFilters(c *check.C) {
245 // not [yet] supported
249 filters: []arvados.Filter{
250 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][0]}},
251 {"uuid", "is_a", "teapot"},
253 expectCalls: []int{0, 0, 0},
254 expectStatus: http.StatusBadRequest,
258 func (s *CollectionListSuite) TestCollectionListMultiSiteWithCount(c *check.C) {
259 for _, count := range []string{"", "exact"} {
263 filters: []arvados.Filter{
264 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][0]}},
265 {"uuid", "is_a", "teapot"},
267 expectCalls: []int{0, 0, 0},
268 expectStatus: http.StatusBadRequest,
273 func (s *CollectionListSuite) TestCollectionListMultiSiteWithLimit(c *check.C) {
274 for _, limit := range []int{0, 1, 2} {
278 filters: []arvados.Filter{
279 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][0]}},
280 {"uuid", "is_a", "teapot"},
282 expectCalls: []int{0, 0, 0},
283 expectStatus: http.StatusBadRequest,
288 func (s *CollectionListSuite) TestCollectionListMultiSiteWithOffset(c *check.C) {
293 filters: []arvados.Filter{
294 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][0]}},
295 {"uuid", "is_a", "teapot"},
297 expectCalls: []int{0, 0, 0},
298 expectStatus: http.StatusBadRequest,
302 func (s *CollectionListSuite) TestCollectionListMultiSiteWithOrder(c *check.C) {
306 order: []string{"uuid desc"},
307 filters: []arvados.Filter{
308 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][0]}},
309 {"uuid", "is_a", "teapot"},
311 expectCalls: []int{0, 0, 0},
312 expectStatus: http.StatusBadRequest,
316 func (s *CollectionListSuite) TestCollectionListInvalidFilters(c *check.C) {
320 filters: []arvados.Filter{
321 {"uuid", "in", "teapot"},
323 expectCalls: []int{0, 0, 0},
324 expectStatus: http.StatusBadRequest,
328 func (s *CollectionListSuite) TestCollectionListRemoteUnknown(c *check.C) {
332 filters: []arvados.Filter{
333 {"uuid", "in", []string{s.uuids[0][0], "bogus-4zz18-000001111122222"}},
335 expectStatus: http.StatusNotFound,
339 func (s *CollectionListSuite) TestCollectionListRemoteError(c *check.C) {
340 s.addDirectRemote(c, "bbbbb", &arvadostest.APIStub{})
344 filters: []arvados.Filter{
345 {"uuid", "in", []string{s.uuids[0][0], s.uuids[1][0]}},
347 expectStatus: http.StatusBadGateway,
351 func (s *CollectionListSuite) test(c *check.C, trial listTrial) {
352 resp, err := s.fed.CollectionList(s.ctx, arvados.ListOptions{
355 Offset: trial.offset,
357 Filters: trial.filters,
359 if trial.expectStatus != 0 {
360 c.Assert(err, check.NotNil)
361 err, _ := err.(interface{ HTTPStatus() int })
362 c.Assert(err, check.NotNil) // err must implement HTTPStatus()
363 c.Check(err.HTTPStatus(), check.Equals, trial.expectStatus)
364 c.Logf("returned error is %#v", err)
365 c.Logf("returned error string is %q", err)
367 c.Check(err, check.IsNil)
368 var expectItems []arvados.Collection
369 for _, uuid := range trial.expectUUIDs {
370 expectItems = append(expectItems, arvados.Collection{UUID: uuid})
372 c.Check(resp, check.DeepEquals, arvados.CollectionList{
377 for i, stub := range s.backends {
378 if i >= len(trial.expectCalls) {
381 calls := stub.Calls(nil)
382 c.Check(calls, check.HasLen, trial.expectCalls[i])
386 opts := calls[0].Options.(arvados.ListOptions)
387 c.Check(opts.Limit, check.Equals, trial.limit)