github.com/cs3org/reva/v2@v2.27.7/internal/grpc/services/sharesstorageprovider/sharesstorageprovider_test.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package sharesstorageprovider_test
    20  
    21  import (
    22  	"context"
    23  	"os"
    24  
    25  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    26  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    27  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    28  	collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
    29  	sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    30  	types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    31  	provider "github.com/cs3org/reva/v2/internal/grpc/services/sharesstorageprovider"
    32  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    33  	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
    34  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    35  	_ "github.com/cs3org/reva/v2/pkg/share/manager/loader"
    36  	"github.com/cs3org/reva/v2/pkg/utils"
    37  	cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
    38  	"google.golang.org/grpc"
    39  
    40  	. "github.com/onsi/ginkgo/v2"
    41  	. "github.com/onsi/gomega"
    42  	"github.com/stretchr/testify/mock"
    43  )
    44  
    45  var (
    46  	ShareJail                *sprovider.ResourceId
    47  	BaseShare                *collaboration.ReceivedShare
    48  	BaseShareTwo             *collaboration.ReceivedShare
    49  	ShareJailStatRequest     *sprovider.StatRequest
    50  	BaseStatRequest          *sprovider.StatRequest
    51  	BaseListContainerRequest *sprovider.ListContainerRequest
    52  )
    53  
    54  var _ = Describe("Sharesstorageprovider", func() {
    55  	var (
    56  		config = map[string]interface{}{
    57  			"gateway_addr":         "127.0.0.1:1234",
    58  			"driver":               "json",
    59  			"usershareprovidersvc": "any",
    60  			"drivers": map[string]map[string]interface{}{
    61  				"json": {},
    62  			},
    63  		}
    64  		ctx = ctxpkg.ContextSetUser(context.Background(), &userpb.User{
    65  			Id: &userpb.UserId{
    66  				OpaqueId: "alice",
    67  			},
    68  			Username: "alice",
    69  		})
    70  
    71  		s                            sprovider.ProviderAPIServer
    72  		gatewayClient                *cs3mocks.GatewayAPIClient
    73  		gatewaySelector              pool.Selectable[gateway.GatewayAPIClient]
    74  		sharingCollaborationClient   *cs3mocks.CollaborationAPIClient
    75  		sharingCollaborationSelector pool.Selectable[collaboration.CollaborationAPIClient]
    76  	)
    77  
    78  	BeforeEach(func() {
    79  		ShareJail = &sprovider.ResourceId{
    80  			StorageId: utils.ShareStorageProviderID,
    81  			SpaceId:   utils.ShareStorageSpaceID,
    82  			OpaqueId:  utils.ShareStorageSpaceID,
    83  		}
    84  
    85  		BaseShare = &collaboration.ReceivedShare{
    86  			State: collaboration.ShareState_SHARE_STATE_ACCEPTED,
    87  			Share: &collaboration.Share{
    88  				Id: &collaboration.ShareId{
    89  					OpaqueId: "shareid",
    90  				},
    91  				ResourceId: &sprovider.ResourceId{
    92  					StorageId: "storageid",
    93  					SpaceId:   "storageid",
    94  					OpaqueId:  "shareddir",
    95  				},
    96  				Permissions: &collaboration.SharePermissions{
    97  					Permissions: &sprovider.ResourcePermissions{
    98  						Stat:               true,
    99  						ListContainer:      true,
   100  						InitiateFileUpload: true,
   101  					},
   102  				},
   103  			},
   104  			MountPoint: &sprovider.Reference{
   105  				Path: "share1-shareddir",
   106  			},
   107  		}
   108  
   109  		BaseShareTwo = &collaboration.ReceivedShare{
   110  			State: collaboration.ShareState_SHARE_STATE_ACCEPTED,
   111  			Share: &collaboration.Share{
   112  				Id: &collaboration.ShareId{
   113  					OpaqueId: "shareid2",
   114  				},
   115  				ResourceId: &sprovider.ResourceId{
   116  					StorageId: "storageid",
   117  					SpaceId:   "storageid",
   118  					OpaqueId:  "shareddir2",
   119  				},
   120  				Permissions: &collaboration.SharePermissions{
   121  					Permissions: &sprovider.ResourcePermissions{
   122  						Stat:               true,
   123  						ListContainer:      true,
   124  						InitiateFileUpload: true,
   125  					},
   126  				},
   127  			},
   128  			MountPoint: &sprovider.Reference{
   129  				Path: "share2-shareddir2",
   130  			},
   131  		}
   132  
   133  		ShareJailStatRequest = &sprovider.StatRequest{
   134  			Ref: &sprovider.Reference{
   135  				ResourceId: &sprovider.ResourceId{
   136  					StorageId: utils.ShareStorageProviderID,
   137  					SpaceId:   utils.ShareStorageSpaceID,
   138  					OpaqueId:  utils.ShareStorageSpaceID,
   139  				},
   140  				Path: ".",
   141  			},
   142  		}
   143  
   144  		BaseStatRequest = &sprovider.StatRequest{
   145  			Ref: &sprovider.Reference{
   146  				ResourceId: &sprovider.ResourceId{
   147  					StorageId: utils.ShareStorageProviderID,
   148  					SpaceId:   utils.ShareStorageSpaceID,
   149  					OpaqueId:  "shareid",
   150  				},
   151  				Path: ".",
   152  			},
   153  		}
   154  
   155  		BaseListContainerRequest = &sprovider.ListContainerRequest{
   156  			Ref: &sprovider.Reference{
   157  				ResourceId: &sprovider.ResourceId{
   158  					StorageId: utils.ShareStorageProviderID,
   159  					SpaceId:   utils.ShareStorageSpaceID,
   160  					OpaqueId:  "shareid",
   161  				},
   162  				Path: ".",
   163  			},
   164  		}
   165  
   166  		pool.RemoveSelector("GatewaySelector" + "any")
   167  		gatewayClient = &cs3mocks.GatewayAPIClient{}
   168  		gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient](
   169  			"GatewaySelector",
   170  			"any",
   171  			func(cc grpc.ClientConnInterface) gateway.GatewayAPIClient {
   172  				return gatewayClient
   173  			},
   174  		)
   175  
   176  		pool.RemoveSelector("SharingCollaborationSelector" + "any")
   177  		sharingCollaborationClient = &cs3mocks.CollaborationAPIClient{}
   178  		sharingCollaborationSelector = pool.GetSelector[collaboration.CollaborationAPIClient](
   179  			"SharingCollaborationSelector",
   180  			"any",
   181  			func(cc grpc.ClientConnInterface) collaboration.CollaborationAPIClient {
   182  				return sharingCollaborationClient
   183  			},
   184  		)
   185  
   186  		// mock stat requests
   187  		// some-provider-id
   188  		gatewayClient.On("Stat", mock.Anything, mock.AnythingOfType("*providerv1beta1.StatRequest")).Return(
   189  			func(_ context.Context, req *sprovider.StatRequest, _ ...grpc.CallOption) *sprovider.StatResponse {
   190  				switch req.Ref.GetPath() {
   191  				case "./share1-shareddir", "./share1-shareddir (1)":
   192  					return &sprovider.StatResponse{
   193  						Status: status.NewOK(context.Background()),
   194  						Info: &sprovider.ResourceInfo{
   195  							Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
   196  							Path: "share1-shareddir",
   197  							Id: &sprovider.ResourceId{
   198  								StorageId: "share1-storageid",
   199  								SpaceId:   "share1-storageid",
   200  								OpaqueId:  "shareddir",
   201  							},
   202  							PermissionSet: &sprovider.ResourcePermissions{
   203  								Stat: true,
   204  							},
   205  							Size: 100,
   206  						},
   207  					}
   208  				case "./share1-subdir":
   209  					return &sprovider.StatResponse{
   210  						Status: status.NewOK(context.Background()),
   211  						Info: &sprovider.ResourceInfo{
   212  							Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
   213  							Path: "share1-subdir",
   214  							Id: &sprovider.ResourceId{
   215  								StorageId: "share1-storageid",
   216  								SpaceId:   "share1-storageid",
   217  								OpaqueId:  "subdir",
   218  							},
   219  							PermissionSet: &sprovider.ResourcePermissions{
   220  								Stat: true,
   221  							},
   222  							Size: 10,
   223  						},
   224  					}
   225  				case "./share1-subdir/share1-subdir-file":
   226  					return &sprovider.StatResponse{
   227  						Status: status.NewOK(context.Background()),
   228  						Info: &sprovider.ResourceInfo{
   229  							Type: sprovider.ResourceType_RESOURCE_TYPE_FILE,
   230  							Path: "share1-subdir-file",
   231  							Id: &sprovider.ResourceId{
   232  								StorageId: "share1-storageid",
   233  								SpaceId:   "share1-storageid",
   234  								OpaqueId:  "file",
   235  							},
   236  							PermissionSet: &sprovider.ResourcePermissions{
   237  								Stat: true,
   238  							},
   239  							Size: 20,
   240  						},
   241  					}
   242  				case ".":
   243  					permissionSet := &sprovider.ResourcePermissions{
   244  						Stat: true,
   245  					}
   246  					if req.Ref.ResourceId.OpaqueId == "shareddir-merged" {
   247  						permissionSet.ListContainer = true
   248  					}
   249  					return &sprovider.StatResponse{
   250  						Status: status.NewOK(context.Background()),
   251  						Info: &sprovider.ResourceInfo{
   252  							Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
   253  							Path: "share1-shareddir",
   254  							Id: &sprovider.ResourceId{
   255  								StorageId: "share1-storageid",
   256  								SpaceId:   "share1-storageid",
   257  								OpaqueId:  req.Ref.ResourceId.OpaqueId,
   258  							},
   259  							PermissionSet: permissionSet,
   260  							Size:          100,
   261  						},
   262  					}
   263  				default:
   264  					switch req.Ref.ResourceId.OpaqueId {
   265  					case "shareddir", "shareddir2":
   266  						permissionSet := &sprovider.ResourcePermissions{
   267  							Stat:          true,
   268  							ListContainer: true,
   269  						}
   270  						return &sprovider.StatResponse{
   271  							Status: status.NewOK(context.Background()),
   272  							Info: &sprovider.ResourceInfo{
   273  								Type:          sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
   274  								Path:          "share1-shareddir",
   275  								Id:            req.Ref.ResourceId,
   276  								PermissionSet: permissionSet,
   277  								Size:          100,
   278  							},
   279  						}
   280  					case "shareddir-merged":
   281  						permissionSet := &sprovider.ResourcePermissions{
   282  							Stat:          true,
   283  							ListContainer: true,
   284  						}
   285  						return &sprovider.StatResponse{
   286  							Status: status.NewOK(context.Background()),
   287  							Info: &sprovider.ResourceInfo{
   288  								Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
   289  								Path: "share1-shareddir",
   290  								Id: &sprovider.ResourceId{
   291  									StorageId: "share1-storageid",
   292  									SpaceId:   "share1-storageid",
   293  									OpaqueId:  req.Ref.ResourceId.OpaqueId,
   294  								},
   295  								PermissionSet: permissionSet,
   296  								Size:          100,
   297  							},
   298  						}
   299  					default:
   300  						return &sprovider.StatResponse{
   301  							Status: status.NewNotFound(context.Background(), "not found"),
   302  						}
   303  					}
   304  				}
   305  			},
   306  			nil)
   307  
   308  		gatewayClient.On("ListContainer", mock.Anything, mock.AnythingOfType("*providerv1beta1.ListContainerRequest")).Return(
   309  			func(_ context.Context, req *sprovider.ListContainerRequest, _ ...grpc.CallOption) *sprovider.ListContainerResponse {
   310  				switch {
   311  				case utils.ResourceIDEqual(req.Ref.ResourceId, BaseShare.Share.ResourceId):
   312  					resp := &sprovider.ListContainerResponse{
   313  						Status: status.NewOK(context.Background()),
   314  						Infos:  []*sprovider.ResourceInfo{},
   315  					}
   316  
   317  					switch req.Ref.GetPath() {
   318  					case ".":
   319  						resp.Infos = append(resp.Infos, &sprovider.ResourceInfo{
   320  							Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
   321  							Path: "share1-shareddir/share1-subdir",
   322  							Id: &sprovider.ResourceId{
   323  								StorageId: "share1-storageid",
   324  								SpaceId:   "share1-storageid",
   325  								OpaqueId:  "subdir",
   326  							},
   327  							Size: 1,
   328  						})
   329  					case "./share1-subdir":
   330  						resp.Infos = append(resp.Infos, &sprovider.ResourceInfo{
   331  							Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
   332  							Path: "share1-shareddir/share1-subdir/share1-subdir-file",
   333  							Id: &sprovider.ResourceId{
   334  								StorageId: "share1-storageid",
   335  								SpaceId:   "share1-storageid",
   336  								OpaqueId:  "file",
   337  							},
   338  							Size: 1,
   339  						})
   340  					}
   341  					return resp
   342  				case utils.ResourceIDEqual(req.Ref.ResourceId, BaseShareTwo.Share.ResourceId):
   343  					resp := &sprovider.ListContainerResponse{
   344  						Status: status.NewOK(context.Background()),
   345  						Infos:  []*sprovider.ResourceInfo{},
   346  					}
   347  					return resp
   348  				case utils.ResourceIDEqual(req.Ref.ResourceId, BaseShareTwo.Share.ResourceId):
   349  					return &sprovider.ListContainerResponse{
   350  						Status: status.NewOK(context.Background()),
   351  						Infos:  []*sprovider.ResourceInfo{},
   352  					}
   353  				default:
   354  					return &sprovider.ListContainerResponse{
   355  						Status: status.NewOK(context.Background()),
   356  						Infos:  []*sprovider.ResourceInfo{},
   357  					}
   358  				}
   359  			}, nil)
   360  
   361  	})
   362  
   363  	JustBeforeEach(func() {
   364  		p, err := provider.New(gatewaySelector, sharingCollaborationSelector, 5)
   365  		Expect(err).ToNot(HaveOccurred())
   366  		s = p.(sprovider.ProviderAPIServer)
   367  		Expect(s).ToNot(BeNil())
   368  	})
   369  
   370  	Describe("NewDefault", func() {
   371  		It("returns a new service instance", func() {
   372  			tmpfile, err := os.CreateTemp("", "eos-unit-test-shares-*.json")
   373  			Expect(err).ToNot(HaveOccurred())
   374  			defer os.Remove(tmpfile.Name())
   375  
   376  			config["drivers"] = map[string]map[string]interface{}{
   377  				"json": {
   378  					"file":     tmpfile.Name(),
   379  					"mount_id": "shareprovidermountid",
   380  				},
   381  			}
   382  			s, err := provider.NewDefault(config, nil, nil)
   383  			Expect(err).ToNot(HaveOccurred())
   384  			Expect(s).ToNot(BeNil())
   385  		})
   386  	})
   387  
   388  	Describe("ListContainer", func() {
   389  		It("lists accepted shares", func() {
   390  			sharingCollaborationClient.On("GetReceivedShare", mock.Anything, mock.Anything).Return(&collaboration.GetReceivedShareResponse{
   391  				Status: status.NewOK(context.Background()),
   392  				Share: &collaboration.ReceivedShare{
   393  					Share: BaseShare.Share,
   394  					State: collaboration.ShareState_SHARE_STATE_ACCEPTED,
   395  				},
   396  			}, nil)
   397  			res, err := s.ListContainer(ctx, BaseListContainerRequest)
   398  			Expect(err).ToNot(HaveOccurred())
   399  			Expect(res).ToNot(BeNil())
   400  			Expect(len(res.Infos)).To(Equal(1))
   401  		})
   402  		It("ignores invalid shares", func() {
   403  			sharingCollaborationClient.On("GetReceivedShare", mock.Anything, mock.Anything).Return(&collaboration.GetReceivedShareResponse{
   404  				Status: status.NewOK(context.Background()),
   405  				Share: &collaboration.ReceivedShare{
   406  					Share: &collaboration.Share{ResourceId: &sprovider.ResourceId{}},
   407  					State: collaboration.ShareState_SHARE_STATE_INVALID,
   408  				},
   409  			}, nil)
   410  			res, err := s.ListContainer(ctx, BaseListContainerRequest)
   411  			Expect(err).ToNot(HaveOccurred())
   412  			Expect(res).ToNot(BeNil())
   413  			Expect(len(res.Infos)).To(Equal(0))
   414  		})
   415  		It("ignores pending shares", func() {
   416  			sharingCollaborationClient.On("GetReceivedShare", mock.Anything, mock.Anything).Return(&collaboration.GetReceivedShareResponse{
   417  				Status: status.NewOK(context.Background()),
   418  				Share: &collaboration.ReceivedShare{
   419  					Share: &collaboration.Share{ResourceId: &sprovider.ResourceId{}},
   420  					State: collaboration.ShareState_SHARE_STATE_PENDING,
   421  				},
   422  			}, nil)
   423  			res, err := s.ListContainer(ctx, BaseListContainerRequest)
   424  			Expect(err).ToNot(HaveOccurred())
   425  			Expect(res).ToNot(BeNil())
   426  			Expect(len(res.Infos)).To(Equal(0))
   427  		})
   428  		It("ignores rejected shares", func() {
   429  			sharingCollaborationClient.On("GetReceivedShare", mock.Anything, mock.Anything).Return(&collaboration.GetReceivedShareResponse{
   430  				Status: status.NewOK(context.Background()),
   431  				Share: &collaboration.ReceivedShare{
   432  					Share: &collaboration.Share{ResourceId: &sprovider.ResourceId{}},
   433  					State: collaboration.ShareState_SHARE_STATE_REJECTED,
   434  				},
   435  			}, nil)
   436  			res, err := s.ListContainer(ctx, BaseListContainerRequest)
   437  			Expect(err).ToNot(HaveOccurred())
   438  			Expect(res).ToNot(BeNil())
   439  			Expect(len(res.Infos)).To(Equal(0))
   440  		})
   441  	})
   442  
   443  	Context("with two accepted shares", func() {
   444  		BeforeEach(func() {
   445  			sharingCollaborationClient.On("ListReceivedShares", mock.Anything, mock.Anything).Return(&collaboration.ListReceivedSharesResponse{
   446  				Status: status.NewOK(context.Background()),
   447  				Shares: []*collaboration.ReceivedShare{BaseShare, BaseShareTwo},
   448  			}, nil)
   449  			sharingCollaborationClient.On("GetReceivedShare", mock.Anything, mock.Anything).Return(
   450  				func(_ context.Context, req *collaboration.GetReceivedShareRequest, _ ...grpc.CallOption) *collaboration.GetReceivedShareResponse {
   451  					switch req.Ref.GetId().GetOpaqueId() {
   452  					case BaseShare.Share.Id.OpaqueId:
   453  						return &collaboration.GetReceivedShareResponse{
   454  							Status: status.NewOK(context.Background()),
   455  							Share:  BaseShare,
   456  						}
   457  					case BaseShareTwo.Share.Id.OpaqueId:
   458  						return &collaboration.GetReceivedShareResponse{
   459  							Status: status.NewOK(context.Background()),
   460  							Share:  BaseShareTwo,
   461  						}
   462  					default:
   463  						return &collaboration.GetReceivedShareResponse{
   464  							Status: status.NewNotFound(context.Background(), "not found"),
   465  						}
   466  					}
   467  				}, nil)
   468  		})
   469  
   470  		Describe("Stat", func() {
   471  			It("stats the first share folder", func() {
   472  				req := ShareJailStatRequest
   473  				req.Ref.Path = "./share1-shareddir"
   474  				res, err := s.Stat(ctx, req)
   475  				Expect(err).ToNot(HaveOccurred())
   476  				Expect(res).ToNot(BeNil())
   477  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   478  				Expect(res.Info).ToNot(BeNil())
   479  				Expect(res.Info.Type).To(Equal(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER))
   480  				Expect(res.Info.Path).To(Equal("share1-shareddir"))
   481  				// Expect(res.Info.Size).To(Equal(uint64(300))) TODO: Why 300?
   482  				Expect(res.Info.Size).To(Equal(uint64(100)))
   483  			})
   484  
   485  			It("stats the correct share in the share jail", func() {
   486  				BaseShare.MountPoint.Path = "share1-shareddir"
   487  				BaseShareTwo.MountPoint.Path = "share1-shareddir (1)"
   488  				statReq := ShareJailStatRequest
   489  				statReq.Ref.Path = "./share1-shareddir (1)"
   490  				res, err := s.Stat(ctx, statReq)
   491  				Expect(err).ToNot(HaveOccurred())
   492  				Expect(res).ToNot(BeNil())
   493  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   494  				Expect(res.Info).ToNot(BeNil())
   495  				Expect(res.Info.Type).To(Equal(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER))
   496  				Expect(res.Info.Path).To(Equal("share1-shareddir"))
   497  				Expect(res.Info.Size).To(Equal(uint64(100)))
   498  			})
   499  
   500  			It("stats a shares folder", func() {
   501  				statReq := BaseStatRequest
   502  				res, err := s.Stat(ctx, statReq)
   503  				Expect(err).ToNot(HaveOccurred())
   504  				Expect(res).ToNot(BeNil())
   505  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   506  				Expect(res.Info).ToNot(BeNil())
   507  				Expect(res.Info.Type).To(Equal(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER))
   508  				Expect(res.Info.Path).To(Equal("share1-shareddir"))
   509  				Expect(res.Info.Size).To(Equal(uint64(100)))
   510  			})
   511  
   512  			It("stats a subfolder in a share", func() {
   513  				statReq := BaseStatRequest
   514  				statReq.Ref.Path = "./share1-subdir"
   515  				res, err := s.Stat(ctx, statReq)
   516  				Expect(err).ToNot(HaveOccurred())
   517  				Expect(res).ToNot(BeNil())
   518  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   519  				Expect(res.Info).ToNot(BeNil())
   520  				Expect(res.Info.Type).To(Equal(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER))
   521  				Expect(res.Info.Path).To(Equal("share1-subdir"))
   522  				Expect(res.Info.Size).To(Equal(uint64(10)))
   523  			})
   524  
   525  			It("stats a shared file", func() {
   526  				statReq := BaseStatRequest
   527  				statReq.Ref.Path = "./share1-subdir/share1-subdir-file"
   528  				res, err := s.Stat(ctx, statReq)
   529  				Expect(err).ToNot(HaveOccurred())
   530  				Expect(res).ToNot(BeNil())
   531  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   532  				Expect(res.Info).ToNot(BeNil())
   533  				Expect(res.Info.Type).To(Equal(sprovider.ResourceType_RESOURCE_TYPE_FILE))
   534  				Expect(res.Info.Path).To(Equal("share1-subdir-file"))
   535  				Expect(res.Info.Size).To(Equal(uint64(20)))
   536  			})
   537  		})
   538  
   539  		Describe("ListContainer", func() {
   540  			It("traverses into specific shares", func() {
   541  				req := BaseListContainerRequest
   542  				res, err := s.ListContainer(ctx, req)
   543  				Expect(err).ToNot(HaveOccurred())
   544  				Expect(res).ToNot(BeNil())
   545  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   546  				Expect(len(res.Infos)).To(Equal(1))
   547  
   548  				entry := res.Infos[0]
   549  				Expect(entry.Path).To(Equal("share1-shareddir/share1-subdir"))
   550  			})
   551  
   552  			It("traverses into subfolders of specific shares", func() {
   553  				req := BaseListContainerRequest
   554  				req.Ref.Path = "./share1-subdir"
   555  				res, err := s.ListContainer(ctx, req)
   556  				Expect(err).ToNot(HaveOccurred())
   557  				Expect(res).ToNot(BeNil())
   558  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   559  				Expect(len(res.Infos)).To(Equal(1))
   560  
   561  				entry := res.Infos[0]
   562  				Expect(entry.Path).To(Equal("share1-shareddir/share1-subdir/share1-subdir-file"))
   563  			})
   564  		})
   565  
   566  		Describe("InitiateFileDownload", func() {
   567  			It("returns not found when not found", func() {
   568  				gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{
   569  					Status: status.NewNotFound(ctx, "gateway: file not found"),
   570  				}, nil)
   571  
   572  				req := &sprovider.InitiateFileDownloadRequest{
   573  					Ref: &sprovider.Reference{
   574  						ResourceId: ShareJail,
   575  						Path:       "./share1-shareddir/does-not-exist",
   576  					},
   577  				}
   578  				res, err := s.InitiateFileDownload(ctx, req)
   579  				Expect(err).ToNot(HaveOccurred())
   580  				Expect(res).ToNot(BeNil())
   581  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_NOT_FOUND))
   582  			})
   583  
   584  			It("initiates the download of an existing file", func() {
   585  				gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{
   586  					Status: status.NewOK(ctx),
   587  					Protocols: []*gateway.FileDownloadProtocol{
   588  						{
   589  							Opaque:           &types.Opaque{},
   590  							Protocol:         "simple",
   591  							DownloadEndpoint: "https://localhost:9200/data",
   592  							Token:            "thetoken",
   593  						},
   594  					},
   595  				}, nil)
   596  				req := &sprovider.InitiateFileDownloadRequest{
   597  					Ref: &sprovider.Reference{
   598  						ResourceId: ShareJail,
   599  						Path:       "./share1-shareddir/share1-subdir/share1-subdir-file",
   600  					},
   601  				}
   602  				res, err := s.InitiateFileDownload(ctx, req)
   603  				Expect(err).ToNot(HaveOccurred())
   604  				Expect(res).ToNot(BeNil())
   605  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   606  				Expect(res.Protocols[0].Protocol).To(Equal("simple"))
   607  				Expect(res.Protocols[0].DownloadEndpoint).To(Equal("https://localhost:9200/data/thetoken"))
   608  			})
   609  		})
   610  
   611  		Describe("CreateContainer", func() {
   612  			BeforeEach(func() {
   613  				gatewayClient.On("CreateContainer", mock.Anything, mock.Anything).Return(&sprovider.CreateContainerResponse{
   614  					Status: status.NewOK(ctx),
   615  				}, nil)
   616  			})
   617  
   618  			It("refuses to create a top-level container which doesn't belong to a share", func() {
   619  				req := &sprovider.CreateContainerRequest{
   620  					Ref: &sprovider.Reference{
   621  						Path: "/shares/invalid-top-level-subdir",
   622  					},
   623  				}
   624  				res, err := s.CreateContainer(ctx, req)
   625  				gatewayClient.AssertNotCalled(GinkgoT(), "CreateContainer", mock.Anything, mock.Anything)
   626  				Expect(err).ToNot(HaveOccurred())
   627  				Expect(res).ToNot(BeNil())
   628  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT))
   629  			})
   630  
   631  			It("creates a directory", func() {
   632  				req := &sprovider.CreateContainerRequest{
   633  					Ref: &sprovider.Reference{
   634  						ResourceId: ShareJail,
   635  						Path:       "./share1-shareddir/share1-newsubdir",
   636  					},
   637  				}
   638  				res, err := s.CreateContainer(ctx, req)
   639  				gatewayClient.AssertCalled(GinkgoT(), "CreateContainer", mock.Anything, mock.Anything)
   640  				Expect(err).ToNot(HaveOccurred())
   641  				Expect(res).ToNot(BeNil())
   642  			})
   643  		})
   644  
   645  		Describe("TouchFile", func() {
   646  			BeforeEach(func() {
   647  				gatewayClient.On("TouchFile", mock.Anything, mock.Anything).Return(
   648  					&sprovider.TouchFileResponse{Status: status.NewOK(ctx)}, nil)
   649  			})
   650  
   651  			It("touches a file", func() {
   652  				req := &sprovider.TouchFileRequest{
   653  					Ref: &sprovider.Reference{
   654  						ResourceId: ShareJail,
   655  						Path:       "./share1-shareddir/newfile.txt",
   656  					},
   657  				}
   658  				res, err := s.TouchFile(ctx, req)
   659  				gatewayClient.AssertCalled(GinkgoT(), "TouchFile", mock.Anything, mock.Anything)
   660  				Expect(err).ToNot(HaveOccurred())
   661  				Expect(res).ToNot(BeNil())
   662  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   663  			})
   664  		})
   665  
   666  		Describe("Delete", func() {
   667  			BeforeEach(func() {
   668  				gatewayClient.On("Delete", mock.Anything, mock.Anything).Return(
   669  					&sprovider.DeleteResponse{Status: status.NewOK(ctx)}, nil)
   670  			})
   671  
   672  			It("rejects the share when deleting a share", func() {
   673  				sharingCollaborationClient.On("UpdateReceivedShare", mock.Anything, mock.Anything).Return(
   674  					&collaboration.UpdateReceivedShareResponse{Status: status.NewOK(ctx)}, nil)
   675  				req := &sprovider.DeleteRequest{
   676  					Ref: &sprovider.Reference{
   677  						ResourceId: &sprovider.ResourceId{
   678  							StorageId: utils.ShareStorageProviderID,
   679  							SpaceId:   utils.ShareStorageSpaceID,
   680  							OpaqueId:  BaseShare.Share.Id.OpaqueId,
   681  						},
   682  						Path: ".",
   683  					},
   684  				}
   685  				res, err := s.Delete(ctx, req)
   686  				gatewayClient.AssertNotCalled(GinkgoT(), "Delete", mock.Anything, mock.Anything)
   687  				Expect(err).ToNot(HaveOccurred())
   688  				Expect(res).ToNot(BeNil())
   689  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   690  
   691  				sharingCollaborationClient.AssertCalled(GinkgoT(), "UpdateReceivedShare", mock.Anything, mock.Anything)
   692  			})
   693  
   694  			It("deletes a file", func() {
   695  				req := &sprovider.DeleteRequest{
   696  					Ref: &sprovider.Reference{
   697  						ResourceId: ShareJail,
   698  						Path:       "./share1-shareddir/share1-subdir/share1-subdir-file",
   699  					},
   700  				}
   701  				res, err := s.Delete(ctx, req)
   702  				gatewayClient.AssertCalled(GinkgoT(), "Delete", mock.Anything, mock.Anything)
   703  				Expect(err).ToNot(HaveOccurred())
   704  				Expect(res).ToNot(BeNil())
   705  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   706  			})
   707  		})
   708  
   709  		Describe("Move", func() {
   710  			BeforeEach(func() {
   711  				gatewayClient.On("Move", mock.Anything, mock.Anything).Return(&sprovider.MoveResponse{
   712  					Status: status.NewOK(ctx),
   713  				}, nil)
   714  			})
   715  
   716  			It("renames a share", func() {
   717  				sharingCollaborationClient.On("UpdateReceivedShare", mock.Anything, mock.Anything).Return(nil, nil)
   718  
   719  				req := &sprovider.MoveRequest{
   720  					Source: &sprovider.Reference{
   721  						ResourceId: &sprovider.ResourceId{
   722  							StorageId: utils.ShareStorageProviderID,
   723  							SpaceId:   utils.ShareStorageSpaceID,
   724  							OpaqueId:  BaseShare.Share.Id.OpaqueId,
   725  						},
   726  						Path: ".",
   727  					},
   728  					Destination: &sprovider.Reference{
   729  						ResourceId: ShareJail,
   730  						Path:       "./newname",
   731  					},
   732  				}
   733  				res, err := s.Move(ctx, req)
   734  				gatewayClient.AssertNotCalled(GinkgoT(), "Move", mock.Anything, mock.Anything)
   735  				Expect(err).ToNot(HaveOccurred())
   736  				Expect(res).ToNot(BeNil())
   737  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   738  				sharingCollaborationClient.AssertCalled(GinkgoT(), "UpdateReceivedShare", mock.Anything, mock.Anything)
   739  			})
   740  
   741  			It("renames a sharejail entry", func() {
   742  				sharingCollaborationClient.On("UpdateReceivedShare", mock.Anything, mock.Anything).Return(nil, nil)
   743  
   744  				req := &sprovider.MoveRequest{
   745  					Source: &sprovider.Reference{
   746  						ResourceId: ShareJail,
   747  						Path:       "./share1-shareddir",
   748  					},
   749  					Destination: &sprovider.Reference{
   750  						ResourceId: ShareJail,
   751  						Path:       "./share1-shareddir-renamed",
   752  					},
   753  				}
   754  				res, err := s.Move(ctx, req)
   755  				gatewayClient.AssertNotCalled(GinkgoT(), "Move", mock.Anything, mock.Anything)
   756  				Expect(err).ToNot(HaveOccurred())
   757  				Expect(res).ToNot(BeNil())
   758  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   759  				sharingCollaborationClient.AssertCalled(GinkgoT(), "UpdateReceivedShare", mock.Anything, mock.Anything)
   760  			})
   761  
   762  			It("refuses to move a file between shares", func() {
   763  				req := &sprovider.MoveRequest{
   764  					Source: &sprovider.Reference{
   765  						ResourceId: ShareJail,
   766  						Path:       "./share1-shareddir/share1-shareddir-file",
   767  					},
   768  					Destination: &sprovider.Reference{
   769  						ResourceId: ShareJail,
   770  						Path:       "./share2-shareddir2/share1-shareddir-file",
   771  					},
   772  				}
   773  				res, err := s.Move(ctx, req)
   774  				gatewayClient.AssertNotCalled(GinkgoT(), "Move", mock.Anything, mock.Anything)
   775  				Expect(err).ToNot(HaveOccurred())
   776  				Expect(res).ToNot(BeNil())
   777  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_UNIMPLEMENTED))
   778  			})
   779  
   780  			It("refuses to move a file between shares resolving to the same space", func() {
   781  				req := &sprovider.MoveRequest{
   782  					Source: &sprovider.Reference{
   783  						ResourceId: ShareJail,
   784  						Path:       "./share1-shareddir/share1-shareddir-file",
   785  					},
   786  					Destination: &sprovider.Reference{
   787  						ResourceId: ShareJail,
   788  						Path:       "./share2-shareddir2/share1-shareddir-file",
   789  					},
   790  				}
   791  				res, err := s.Move(ctx, req)
   792  				gatewayClient.AssertNotCalled(GinkgoT(), "Move", mock.Anything, mock.Anything)
   793  				Expect(err).ToNot(HaveOccurred())
   794  				Expect(res).ToNot(BeNil())
   795  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_UNIMPLEMENTED))
   796  			})
   797  
   798  			It("moves a file", func() {
   799  				req := &sprovider.MoveRequest{
   800  					Source: &sprovider.Reference{
   801  						ResourceId: ShareJail,
   802  						Path:       "./share1-shareddir/share1-shareddir-file",
   803  					},
   804  					Destination: &sprovider.Reference{
   805  						ResourceId: ShareJail,
   806  						Path:       "./share1-shareddir/share1-shareddir-filenew",
   807  					},
   808  				}
   809  				res, err := s.Move(ctx, req)
   810  				gatewayClient.AssertCalled(GinkgoT(), "Move", mock.Anything, mock.Anything)
   811  				Expect(err).ToNot(HaveOccurred())
   812  				Expect(res).ToNot(BeNil())
   813  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   814  			})
   815  		})
   816  
   817  		Describe("ListFileVersions", func() {
   818  			BeforeEach(func() {
   819  				gatewayClient.On("ListFileVersions", mock.Anything, mock.Anything).Return(
   820  					&sprovider.ListFileVersionsResponse{
   821  						Status: status.NewOK(ctx),
   822  						Versions: []*sprovider.FileVersion{
   823  							{
   824  								Size:  10,
   825  								Mtime: 1,
   826  								Etag:  "1",
   827  								Key:   "1",
   828  							},
   829  							{
   830  								Size:  20,
   831  								Mtime: 2,
   832  								Etag:  "2",
   833  								Key:   "2",
   834  							},
   835  						},
   836  					}, nil)
   837  			})
   838  
   839  			It("does not try to list versions of shares or the top-level dir", func() {
   840  				req := &sprovider.ListFileVersionsRequest{
   841  					Ref: &sprovider.Reference{
   842  						Path: "/shares",
   843  					},
   844  				}
   845  				res, err := s.ListFileVersions(ctx, req)
   846  				gatewayClient.AssertNotCalled(GinkgoT(), "ListFileVersions", mock.Anything, mock.Anything)
   847  				Expect(err).ToNot(HaveOccurred())
   848  				Expect(res).ToNot(BeNil())
   849  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT))
   850  
   851  				req = &sprovider.ListFileVersionsRequest{
   852  					Ref: &sprovider.Reference{
   853  						Path: "/shares/share1-shareddir/",
   854  					},
   855  				}
   856  				res, err = s.ListFileVersions(ctx, req)
   857  				gatewayClient.AssertNotCalled(GinkgoT(), "ListFileVersions", mock.Anything, mock.Anything)
   858  				Expect(err).ToNot(HaveOccurred())
   859  				Expect(res).ToNot(BeNil())
   860  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_INVALID_ARGUMENT))
   861  			})
   862  
   863  			It("lists versions", func() {
   864  				req := &sprovider.ListFileVersionsRequest{
   865  					Ref: &sprovider.Reference{
   866  						ResourceId: ShareJail,
   867  						Path:       "./share1-shareddir/share1-shareddir-file",
   868  					},
   869  				}
   870  				res, err := s.ListFileVersions(ctx, req)
   871  				gatewayClient.AssertCalled(GinkgoT(), "ListFileVersions", mock.Anything, mock.Anything)
   872  				Expect(err).ToNot(HaveOccurred())
   873  				Expect(res).ToNot(BeNil())
   874  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   875  				Expect(len(res.Versions)).To(Equal(2))
   876  				version := res.Versions[0]
   877  				Expect(version.Key).To(Equal("1"))
   878  				Expect(version.Etag).To(Equal("1"))
   879  				Expect(version.Mtime).To(Equal(uint64(1)))
   880  				Expect(version.Size).To(Equal(uint64(10)))
   881  			})
   882  		})
   883  
   884  		Describe("RestoreFileVersion", func() {
   885  			BeforeEach(func() {
   886  				gatewayClient.On("RestoreFileVersion", mock.Anything, mock.Anything).Return(
   887  					&sprovider.RestoreFileVersionResponse{
   888  						Status: status.NewOK(ctx),
   889  					}, nil)
   890  			})
   891  
   892  			It("restores a file version", func() {
   893  				req := &sprovider.RestoreFileVersionRequest{
   894  					Ref: &sprovider.Reference{
   895  						ResourceId: ShareJail,
   896  						Path:       "./share1-shareddir/share1-shareddir-file",
   897  					},
   898  					Key: "1",
   899  				}
   900  				res, err := s.RestoreFileVersion(ctx, req)
   901  				gatewayClient.AssertCalled(GinkgoT(), "RestoreFileVersion", mock.Anything, mock.Anything)
   902  				Expect(err).ToNot(HaveOccurred())
   903  				Expect(res).ToNot(BeNil())
   904  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   905  			})
   906  		})
   907  
   908  		Describe("InitiateFileUpload", func() {
   909  			BeforeEach(func() {
   910  				gatewayClient.On("InitiateFileUpload", mock.Anything, mock.Anything).Return(
   911  					&gateway.InitiateFileUploadResponse{
   912  						Status: status.NewOK(ctx),
   913  						Protocols: []*gateway.FileUploadProtocol{
   914  							{
   915  								Opaque:         &types.Opaque{},
   916  								Protocol:       "simple",
   917  								UploadEndpoint: "https://localhost:9200/data",
   918  								Token:          "thetoken",
   919  							},
   920  						},
   921  					}, nil)
   922  			})
   923  
   924  			It("initiates a file upload", func() {
   925  				req := &sprovider.InitiateFileUploadRequest{
   926  					Ref: &sprovider.Reference{
   927  						ResourceId: ShareJail,
   928  						Path:       "./share1-shareddir/share1-shareddir-file",
   929  					},
   930  				}
   931  				res, err := s.InitiateFileUpload(ctx, req)
   932  				gatewayClient.AssertCalled(GinkgoT(), "InitiateFileUpload", mock.Anything, mock.Anything)
   933  				Expect(err).ToNot(HaveOccurred())
   934  				Expect(res).ToNot(BeNil())
   935  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   936  				Expect(len(res.Protocols)).To(Equal(1))
   937  				Expect(res.Protocols[0].Protocol).To(Equal("simple"))
   938  				Expect(res.Protocols[0].UploadEndpoint).To(Equal("https://localhost:9200/data/thetoken"))
   939  			})
   940  		})
   941  
   942  		Describe("SetArbitraryMetadata", func() {
   943  			BeforeEach(func() {
   944  				gatewayClient.On("SetArbitraryMetadata", mock.Anything, mock.Anything).Return(&sprovider.SetArbitraryMetadataResponse{
   945  					Status: status.NewOK(ctx),
   946  				}, nil)
   947  			})
   948  
   949  			It("sets the metadata", func() {
   950  				req := &sprovider.SetArbitraryMetadataRequest{
   951  					Ref: &sprovider.Reference{
   952  						ResourceId: ShareJail,
   953  						Path:       "./share1-shareddir/share1-subdir/share1-subdir-file",
   954  					},
   955  					ArbitraryMetadata: &sprovider.ArbitraryMetadata{
   956  						Metadata: map[string]string{
   957  							"foo": "bar",
   958  						},
   959  					},
   960  				}
   961  				res, err := s.SetArbitraryMetadata(ctx, req)
   962  				gatewayClient.AssertCalled(GinkgoT(), "SetArbitraryMetadata", mock.Anything, mock.Anything)
   963  				Expect(err).ToNot(HaveOccurred())
   964  				Expect(res).ToNot(BeNil())
   965  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
   966  			})
   967  		})
   968  	})
   969  	Context("with two accepted shares for the same resource", func() {
   970  		BeforeEach(func() {
   971  			BaseShare.Share.Id.OpaqueId = "multishare1"
   972  			BaseShare.Share.ResourceId = &sprovider.ResourceId{
   973  				StorageId: "share1-storageid",
   974  				SpaceId:   "share1-storageid",
   975  				OpaqueId:  "shareddir-merged",
   976  			}
   977  			BaseShare.Share.Permissions = &collaboration.SharePermissions{
   978  				Permissions: &sprovider.ResourcePermissions{
   979  					Stat: true,
   980  				},
   981  			}
   982  			BaseShare.Share.Ctime = utils.TSNow()
   983  			BaseShare.MountPoint = &sprovider.Reference{Path: "share1-shareddir"}
   984  			BaseShareTwo.Share.Id.OpaqueId = "multishare2"
   985  			BaseShareTwo.Share.ResourceId = BaseShare.Share.ResourceId
   986  			BaseShareTwo.Share.Permissions = &collaboration.SharePermissions{
   987  				Permissions: &sprovider.ResourcePermissions{
   988  					ListContainer: true,
   989  				},
   990  			}
   991  			BaseShareTwo.MountPoint = BaseShare.MountPoint
   992  			BaseShareTwo.Share.Ctime = utils.TSNow()
   993  
   994  			sharingCollaborationClient.On("ListReceivedShares", mock.Anything, mock.Anything).Return(&collaboration.ListReceivedSharesResponse{
   995  				Status: status.NewOK(context.Background()),
   996  				Shares: []*collaboration.ReceivedShare{BaseShare, BaseShareTwo},
   997  			}, nil)
   998  			sharingCollaborationClient.On("GetReceivedShare", mock.Anything, mock.Anything).Return(
   999  				func(_ context.Context, req *collaboration.GetReceivedShareRequest, _ ...grpc.CallOption) *collaboration.GetReceivedShareResponse {
  1000  					switch req.Ref.GetId().GetOpaqueId() {
  1001  					case BaseShare.Share.Id.OpaqueId:
  1002  						return &collaboration.GetReceivedShareResponse{
  1003  							Status: status.NewOK(context.Background()),
  1004  							Share:  BaseShare,
  1005  						}
  1006  					case BaseShareTwo.Share.Id.OpaqueId:
  1007  						return &collaboration.GetReceivedShareResponse{
  1008  							Status: status.NewOK(context.Background()),
  1009  							Share:  BaseShareTwo,
  1010  						}
  1011  					default:
  1012  						return &collaboration.GetReceivedShareResponse{
  1013  							Status: status.NewNotFound(context.Background(), "not found"),
  1014  						}
  1015  					}
  1016  				}, nil)
  1017  		})
  1018  		Describe("Stat", func() {
  1019  			It("merges permissions from multiple shares", func() {
  1020  				BaseStatRequest.Ref.ResourceId.OpaqueId = "multishare2"
  1021  				res, err := s.Stat(ctx, BaseStatRequest)
  1022  				Expect(err).ToNot(HaveOccurred())
  1023  				Expect(res).ToNot(BeNil())
  1024  				Expect(res.Info).ToNot(BeNil())
  1025  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
  1026  				Expect(res.Info.Type).To(Equal(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER))
  1027  				Expect(res.Info.Path).To(Equal("share1-shareddir"))
  1028  				Expect(res.Info.PermissionSet.Stat).To(BeTrue())
  1029  				Expect(res.Info.PermissionSet.ListContainer).To(BeTrue())
  1030  			})
  1031  		})
  1032  		Describe("ListContainer", func() {
  1033  			It("Returns just one item", func() {
  1034  				req := BaseListContainerRequest
  1035  				req.Ref.ResourceId.OpaqueId = utils.ShareStorageSpaceID
  1036  				req.Ref.Path = ""
  1037  				res, err := s.ListContainer(ctx, req)
  1038  				Expect(err).ToNot(HaveOccurred())
  1039  				Expect(res).ToNot(BeNil())
  1040  				Expect(res.Status.Code).To(Equal(rpc.Code_CODE_OK))
  1041  				Expect(len(res.Infos)).To(Equal(1))
  1042  
  1043  				entry := res.Infos[0]
  1044  				Expect(entry.Path).To(Equal("share1-shareddir"))
  1045  			})
  1046  		})
  1047  
  1048  	})
  1049  })