github.com/cs3org/reva/v2@v2.27.7/tests/integration/grpc/gateway_storageprovider_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 grpc_test
    20  
    21  import (
    22  	"context"
    23  	"os"
    24  	"path"
    25  	"time"
    26  
    27  	"github.com/cs3org/reva/v2/pkg/storagespace"
    28  	"github.com/rs/zerolog"
    29  	"google.golang.org/grpc/metadata"
    30  
    31  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    32  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    33  	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    34  	storagep "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    35  	"github.com/cs3org/reva/v2/pkg/auth/scope"
    36  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    37  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    38  	"github.com/cs3org/reva/v2/pkg/storage"
    39  	"github.com/cs3org/reva/v2/pkg/storage/fs/ocis"
    40  	jwt "github.com/cs3org/reva/v2/pkg/token/manager/jwt"
    41  	"github.com/cs3org/reva/v2/pkg/utils"
    42  	"github.com/cs3org/reva/v2/tests/helpers"
    43  
    44  	. "github.com/onsi/ginkgo/v2"
    45  	. "github.com/onsi/gomega"
    46  )
    47  
    48  // This test suite tests the gprc gateway interface
    49  //
    50  // It uses the `startRevads` helper to spawn the according reva daemon and
    51  // other dependencies like a userprovider if needed.
    52  // It also sets up an authenticated context and a service client to the storage
    53  // provider to be used in the assertion functions.
    54  var _ = Describe("gateway", func() {
    55  	var (
    56  		dependencies = []RevadConfig{}
    57  		variables    = map[string]string{}
    58  		revads       = map[string]*Revad{}
    59  
    60  		ctx           context.Context
    61  		serviceClient gateway.GatewayAPIClient
    62  		user          = &userpb.User{
    63  			Id: &userpb.UserId{
    64  				Idp:      "0.0.0.0:39000",
    65  				OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c",
    66  				Type:     userpb.UserType_USER_TYPE_PRIMARY,
    67  			},
    68  			Username: "einstein",
    69  		}
    70  		homeRef = &storagep.Reference{
    71  			ResourceId: &storagep.ResourceId{
    72  				SpaceId:  user.Id.OpaqueId,
    73  				OpaqueId: user.Id.OpaqueId,
    74  			},
    75  			Path: ".",
    76  		}
    77  
    78  		infos2Etags = func(infos []*storagep.ResourceInfo) map[string]string {
    79  			etags := map[string]string{}
    80  			for _, info := range infos {
    81  				etags[info.Path] = info.Etag
    82  			}
    83  			return etags
    84  		}
    85  		infos2Paths = func(infos []*storagep.ResourceInfo) []string {
    86  			paths := []string{}
    87  			for _, info := range infos {
    88  				paths = append(paths, info.Path)
    89  			}
    90  			return paths
    91  		}
    92  	)
    93  
    94  	BeforeEach(func() {
    95  		dependencies = []RevadConfig{
    96  			{
    97  				Name:   "gateway",
    98  				Config: "gateway.toml"},
    99  			{
   100  				Name:   "users",
   101  				Config: "userprovider-json.toml"},
   102  			{
   103  				Name:   "storage",
   104  				Config: "storageprovider-ocis.toml"},
   105  			{
   106  				Name:   "storage2",
   107  				Config: "storageprovider-ocis.toml"},
   108  			{
   109  				Name:   "permissions",
   110  				Config: "permissions-ocis-ci.toml"},
   111  		}
   112  	})
   113  
   114  	JustBeforeEach(func() {
   115  		var err error
   116  		ctx = context.Background()
   117  
   118  		// Add auth token
   119  		tokenManager, err := jwt.New(map[string]interface{}{"secret": "changemeplease"})
   120  		Expect(err).ToNot(HaveOccurred())
   121  		scope, err := scope.AddOwnerScope(nil)
   122  		Expect(err).ToNot(HaveOccurred())
   123  		t, err := tokenManager.MintToken(ctx, user, scope)
   124  		Expect(err).ToNot(HaveOccurred())
   125  		ctx = ctxpkg.ContextSetToken(ctx, t)
   126  		ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, t)
   127  		ctx = ctxpkg.ContextSetUser(ctx, user)
   128  
   129  		revads, err = startRevads(dependencies, variables)
   130  		Expect(err).ToNot(HaveOccurred())
   131  		Expect(revads["gateway"]).ToNot(BeNil())
   132  		serviceClient, err = pool.GetGatewayServiceClient(revads["gateway"].GrpcAddress)
   133  		Expect(err).ToNot(HaveOccurred())
   134  	})
   135  
   136  	AfterEach(func() {
   137  		for _, r := range revads {
   138  			Expect(r.Cleanup(CurrentSpecReport().Failed())).To(Succeed())
   139  		}
   140  	})
   141  
   142  	It("creates a home directory", func() {
   143  		statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   144  		Expect(err).ToNot(HaveOccurred())
   145  		Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   146  
   147  		res, err := serviceClient.CreateHome(ctx, &storagep.CreateHomeRequest{})
   148  		Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   149  		Expect(err).ToNot(HaveOccurred())
   150  
   151  		statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   152  		Expect(err).ToNot(HaveOccurred())
   153  		Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   154  
   155  		ghRes, err := serviceClient.GetHome(ctx, &storagep.GetHomeRequest{})
   156  		Expect(err).ToNot(HaveOccurred())
   157  		Expect(ghRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   158  	})
   159  
   160  	Context("with a sharded projects directory", func() {
   161  		var (
   162  			shard1Fs    storage.FS
   163  			shard1Space *storagep.StorageSpace
   164  			shard2Fs    storage.FS
   165  			projectsRef = &storagep.Reference{Path: "/projects"}
   166  
   167  			getProjectsEtag = func() string {
   168  				listRes, err := serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: &storagep.Reference{Path: "/"}})
   169  				Expect(err).ToNot(HaveOccurred())
   170  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   171  				Expect(len(listRes.Infos)).To(Equal(1))
   172  				return listRes.Infos[0].Etag
   173  			}
   174  		)
   175  
   176  		BeforeEach(func() {
   177  			dependencies = []RevadConfig{
   178  				{Name: "gateway", Config: "gateway-sharded.toml"},
   179  				{Name: "users", Config: "userprovider-json.toml"},
   180  				{Name: "homestorage", Config: "storageprovider-ocis.toml"},
   181  				{Name: "storage", Config: "storageprovider-ocis.toml"},
   182  				{Name: "storage2", Config: "storageprovider-ocis.toml"},
   183  				{Name: "permissions", Config: "permissions-ocis-ci.toml"},
   184  			}
   185  		})
   186  
   187  		JustBeforeEach(func() {
   188  			var err error
   189  			shard1Fs, err = ocis.New(map[string]interface{}{
   190  				"root":                revads["storage"].StorageRoot,
   191  				"userprovidersvc":     revads["users"].GrpcAddress,
   192  				"permissionssvc":      revads["permissions"].GrpcAddress,
   193  				"treesize_accounting": true,
   194  				"treetime_accounting": true,
   195  			}, nil, &zerolog.Logger{})
   196  			Expect(err).ToNot(HaveOccurred())
   197  			res, err := shard1Fs.CreateStorageSpace(ctx, &storagep.CreateStorageSpaceRequest{
   198  				Type:  "project",
   199  				Name:  "a - project",
   200  				Owner: user,
   201  			})
   202  			Expect(err).ToNot(HaveOccurred())
   203  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   204  			shard1Space = res.StorageSpace
   205  
   206  			ssid, err := storagespace.ParseID(shard1Space.Id.OpaqueId)
   207  			Expect(err).ToNot(HaveOccurred())
   208  			err = helpers.Upload(ctx,
   209  				shard1Fs,
   210  				&storagep.Reference{ResourceId: &ssid, Path: "/file.txt"},
   211  				[]byte("1"),
   212  			)
   213  			Expect(err).ToNot(HaveOccurred())
   214  
   215  			shard2Fs, err = ocis.New(map[string]interface{}{
   216  				"root":                revads["storage"].StorageRoot,
   217  				"userprovidersvc":     revads["users"].GrpcAddress,
   218  				"permissionssvc":      revads["permissions"].GrpcAddress,
   219  				"treesize_accounting": true,
   220  				"treetime_accounting": true,
   221  			}, nil, &zerolog.Logger{})
   222  			Expect(err).ToNot(HaveOccurred())
   223  			res, err = shard2Fs.CreateStorageSpace(ctx, &storagep.CreateStorageSpaceRequest{
   224  				Type:  "project",
   225  				Name:  "z - project",
   226  				Owner: user,
   227  			})
   228  			Expect(err).ToNot(HaveOccurred())
   229  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   230  		})
   231  
   232  		Describe("ListContainer", func() {
   233  			// Note: The Gateway doesn't merge any lists any more. This needs to be done by the client
   234  			// TODO: Move the tests to a place where they can actually test something
   235  			PIt("merges the lists of both shards", func() {
   236  				listRes, err := serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: projectsRef})
   237  				Expect(err).ToNot(HaveOccurred())
   238  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   239  
   240  				Expect(infos2Paths(listRes.Infos)).To(ConsistOf([]string{"/projects/a - project", "/projects/z - project"}))
   241  			})
   242  
   243  			PIt("propagates the etags from both shards", func() {
   244  				rootEtag := getProjectsEtag()
   245  
   246  				listRes, err := serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: projectsRef})
   247  				Expect(err).ToNot(HaveOccurred())
   248  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   249  
   250  				etags := infos2Etags(listRes.Infos)
   251  				Expect(etags["/projects/a - project"]).ToNot(BeNil())
   252  				Expect(etags["/projects/z - project"]).ToNot(BeNil())
   253  
   254  				By("creating a new file")
   255  				err = helpers.Upload(ctx, shard1Fs, &storagep.Reference{ResourceId: &storagep.ResourceId{StorageId: shard1Space.Id.OpaqueId}, Path: "/newfile.txt"}, []byte("1234567890"))
   256  				Expect(err).ToNot(HaveOccurred())
   257  
   258  				time.Sleep(time.Second) // cache must expire
   259  				listRes, err = serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: projectsRef})
   260  				Expect(err).ToNot(HaveOccurred())
   261  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   262  				etags2 := infos2Etags(listRes.Infos)
   263  				Expect(etags2["/projects/a - project"]).ToNot(Equal(etags["/projects/a - project"]))
   264  				Expect(etags2["/projects/z - project"]).To(Equal(etags["/projects/z - project"]))
   265  
   266  				rootEtag2 := getProjectsEtag()
   267  				Expect(rootEtag2).ToNot(Equal(rootEtag))
   268  
   269  				By("updating an existing file")
   270  				err = helpers.Upload(ctx, shard1Fs, &storagep.Reference{ResourceId: &storagep.ResourceId{StorageId: shard1Space.Id.OpaqueId}, Path: "/newfile.txt"}, []byte("12345678901"))
   271  				Expect(err).ToNot(HaveOccurred())
   272  
   273  				time.Sleep(time.Second) // cache must expire
   274  				listRes, err = serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: projectsRef})
   275  				Expect(err).ToNot(HaveOccurred())
   276  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   277  				etags3 := infos2Etags(listRes.Infos)
   278  				Expect(etags3["/projects/a - project"]).ToNot(Equal(etags2["/projects/a - project"]))
   279  				Expect(etags3["/projects/z - project"]).To(Equal(etags2["/projects/z - project"]))
   280  
   281  				rootEtag3 := getProjectsEtag()
   282  				Expect(rootEtag3).ToNot(Equal(rootEtag2))
   283  
   284  				By("creating a directory")
   285  				err = shard1Fs.CreateDir(ctx, &storagep.Reference{ResourceId: &storagep.ResourceId{StorageId: shard1Space.Id.OpaqueId}, Path: "/newdirectory"})
   286  				Expect(err).ToNot(HaveOccurred())
   287  
   288  				time.Sleep(time.Second) // cache must expire
   289  				listRes, err = serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: projectsRef})
   290  				Expect(err).ToNot(HaveOccurred())
   291  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   292  				etags4 := infos2Etags(listRes.Infos)
   293  				Expect(etags4["/projects/a - project"]).ToNot(Equal(etags3["/projects/a - project"]))
   294  				Expect(etags4["/projects/z - project"]).To(Equal(etags3["/projects/z - project"]))
   295  
   296  				rootEtag4 := getProjectsEtag()
   297  				Expect(rootEtag4).ToNot(Equal(rootEtag3))
   298  			})
   299  
   300  			It("places new spaces in the correct shard", func() {
   301  				createRes, err := serviceClient.CreateStorageSpace(ctx, &storagep.CreateStorageSpaceRequest{
   302  					Owner: user,
   303  					Type:  "project",
   304  					Name:  "o - project",
   305  				})
   306  				Expect(err).ToNot(HaveOccurred())
   307  				Expect(createRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   308  				space := createRes.StorageSpace
   309  
   310  				ref := &storagep.Reference{
   311  					ResourceId: space.Root,
   312  					Path:       ".",
   313  				}
   314  
   315  				listRes, err := serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: ref})
   316  				Expect(err).ToNot(HaveOccurred())
   317  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   318  
   319  				ssid, err := storagespace.ParseID(space.Id.OpaqueId)
   320  				Expect(err).ToNot(HaveOccurred())
   321  				mpk1, err := os.ReadFile(path.Join(revads["storage"].StorageRoot, "/indexes/by-type/project.mpk"))
   322  				Expect(err).ToNot(HaveOccurred())
   323  				Expect(string(mpk1)).ToNot(ContainSubstring(ssid.OpaqueId))
   324  				mpk2, err := os.ReadFile(path.Join(revads["storage2"].StorageRoot, "/indexes/by-type/project.mpk"))
   325  				Expect(err).ToNot(HaveOccurred())
   326  				Expect(string(mpk2)).To(ContainSubstring(ssid.OpaqueId))
   327  			})
   328  
   329  			PIt("deletes spaces", func() {})
   330  
   331  			It("lists individual project spaces", func() {
   332  				By("trying to list a non-existent space")
   333  				listRes, err := serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: &storagep.Reference{
   334  					ResourceId: &storagep.ResourceId{
   335  						StorageId: "does-not-exist",
   336  						OpaqueId:  "neither-supposed-to-exist",
   337  					},
   338  					Path: ".",
   339  				}})
   340  				Expect(err).ToNot(HaveOccurred())
   341  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   342  
   343  				By("listing an existing space")
   344  				listRes, err = serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: &storagep.Reference{ResourceId: shard1Space.Root, Path: "."}})
   345  				Expect(err).ToNot(HaveOccurred())
   346  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   347  				Expect(len(listRes.Infos)).To(Equal(1))
   348  				paths := []string{}
   349  				for _, i := range listRes.Infos {
   350  					paths = append(paths, i.Path)
   351  				}
   352  				Expect(paths).To(ConsistOf([]string{"file.txt"}))
   353  			})
   354  
   355  		})
   356  	})
   357  
   358  	Context("with a basic user storage", func() {
   359  		var (
   360  			fs            storage.FS
   361  			embeddedFs    storage.FS
   362  			homeSpace     *storagep.StorageSpace
   363  			embeddedSpace *storagep.StorageSpace
   364  			embeddedRef   *storagep.Reference
   365  		)
   366  
   367  		BeforeEach(func() {
   368  			dependencies = []RevadConfig{
   369  				{Name: "gateway", Config: "gateway.toml"},
   370  				{Name: "users", Config: "userprovider-json.toml"},
   371  				{Name: "storage", Config: "storageprovider-ocis.toml"},
   372  				{Name: "storage2", Config: "storageprovider-ocis.toml"},
   373  				{Name: "permissions", Config: "permissions-ocis-ci.toml"},
   374  			}
   375  		})
   376  
   377  		JustBeforeEach(func() {
   378  			var err error
   379  			fs, err = ocis.New(map[string]interface{}{
   380  				"root":                revads["storage"].StorageRoot,
   381  				"permissionssvc":      revads["permissions"].GrpcAddress,
   382  				"treesize_accounting": true,
   383  				"treetime_accounting": true,
   384  			}, nil, &zerolog.Logger{})
   385  			Expect(err).ToNot(HaveOccurred())
   386  
   387  			r, err := serviceClient.CreateHome(ctx, &storagep.CreateHomeRequest{})
   388  			Expect(err).ToNot(HaveOccurred())
   389  			Expect(r.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   390  
   391  			spaces, err := fs.ListStorageSpaces(ctx, []*storagep.ListStorageSpacesRequest_Filter{}, false)
   392  			Expect(err).ToNot(HaveOccurred())
   393  			homeSpace = spaces[0]
   394  			ssid, err := storagespace.ParseID(homeSpace.Id.OpaqueId)
   395  			Expect(err).ToNot(HaveOccurred())
   396  			err = helpers.Upload(ctx,
   397  				fs,
   398  				&storagep.Reference{ResourceId: &ssid, Path: "/file.txt"},
   399  				[]byte("1"),
   400  			)
   401  			Expect(err).ToNot(HaveOccurred())
   402  
   403  			embeddedFs, err = ocis.New(map[string]interface{}{
   404  				"root":                revads["storage2"].StorageRoot,
   405  				"userprovidersvc":     revads["users"].GrpcAddress,
   406  				"permissionssvc":      revads["permissions"].GrpcAddress,
   407  				"treesize_accounting": true,
   408  				"treetime_accounting": true,
   409  			}, nil, &zerolog.Logger{})
   410  			Expect(err).ToNot(HaveOccurred())
   411  			res, err := serviceClient.CreateStorageSpace(ctx, &storagep.CreateStorageSpaceRequest{
   412  				Type:  "project",
   413  				Name:  "embedded project",
   414  				Owner: user,
   415  			})
   416  			Expect(err).ToNot(HaveOccurred())
   417  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   418  			embeddedSpace = res.StorageSpace
   419  			essid, err := storagespace.ParseID(embeddedSpace.Id.OpaqueId)
   420  			Expect(err).ToNot(HaveOccurred())
   421  			embeddedRef = &storagep.Reference{
   422  				ResourceId: &essid,
   423  				Path:       ".", //  path.Join(homeRef.Path, "Projects", embeddedSpace.Id.OpaqueId),
   424  			}
   425  			err = helpers.Upload(ctx,
   426  				embeddedFs,
   427  				&storagep.Reference{ResourceId: &essid, Path: "/embedded.txt"},
   428  				[]byte("22"),
   429  			)
   430  			Expect(err).ToNot(HaveOccurred())
   431  		})
   432  
   433  		Describe("ListContainer", func() {
   434  			It("lists the root", func() {
   435  				listRes, err := serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: homeRef})
   436  				Expect(err).ToNot(HaveOccurred())
   437  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   438  				Expect(len(listRes.Infos)).To(Equal(1))
   439  
   440  				var fileInfo *storagep.ResourceInfo
   441  				// var embeddedInfo *storagep.ResourceInfo
   442  				for _, i := range listRes.Infos {
   443  					if i.Path == "file.txt" {
   444  						fileInfo = i
   445  					} // else if i.Path == "Projects" {
   446  					// embeddedInfo = i
   447  					// }
   448  
   449  				}
   450  				Expect(fileInfo).ToNot(BeNil())
   451  				Expect(fileInfo.Owner.OpaqueId).To(Equal(user.Id.OpaqueId))
   452  				Expect(fileInfo.Path).To(Equal("file.txt"))
   453  				Expect(fileInfo.Size).To(Equal(uint64(1)))
   454  
   455  				// Expect(embeddedInfo).ToNot(BeNil())
   456  				// Expect(embeddedInfo.Owner.OpaqueId).To(Equal(user.Id.OpaqueId))
   457  				// Expect(embeddedInfo.Path).To(Equal("Projects"))
   458  				// Expect(embeddedInfo.Size).To(Equal(uint64(2)))
   459  			})
   460  
   461  			PIt("lists the embedded project space", func() {
   462  				listRes, err := serviceClient.ListContainer(ctx, &storagep.ListContainerRequest{Ref: embeddedRef})
   463  				Expect(err).ToNot(HaveOccurred())
   464  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   465  				Expect(len(listRes.Infos)).To(Equal(2))
   466  
   467  				var embeddedInfo *storagep.ResourceInfo
   468  				for _, i := range listRes.Infos {
   469  					if i.Path == path.Join(embeddedRef.Path, "embedded.txt") {
   470  						embeddedInfo = i
   471  					}
   472  
   473  				}
   474  				Expect(embeddedInfo).ToNot(BeNil())
   475  				Expect(embeddedInfo.Owner.OpaqueId).To(Equal(user.Id.OpaqueId))
   476  				Expect(embeddedInfo.Path).To(Equal(path.Join(embeddedRef.Path, "embedded.txt")))
   477  				Expect(embeddedInfo.Size).To(Equal(uint64(2)))
   478  			})
   479  		})
   480  
   481  		Describe("Stat", func() {
   482  			It("stats the root", func() {
   483  				statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   484  				Expect(err).ToNot(HaveOccurred())
   485  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   486  
   487  				info := statRes.Info
   488  				Expect(info.Type).To(Equal(storagep.ResourceType_RESOURCE_TYPE_CONTAINER))
   489  				Expect(utils.ResourceIDEqual(info.Id, homeRef.ResourceId)).To(BeTrue())
   490  				Expect(info.Path).To(Equal(".")) // path of a root node of a space is always "."
   491  				Expect(info.Owner.OpaqueId).To(Equal(user.Id.OpaqueId))
   492  
   493  				// TODO: size aggregating is done by the client now - so no chance testing that here
   494  				// Expect(info.Size).To(Equal(uint64(3))) // home: 1, embedded: 2
   495  			})
   496  
   497  			It("stats the root of embedded space", func() {
   498  				statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: embeddedRef})
   499  				Expect(err).ToNot(HaveOccurred())
   500  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   501  
   502  				info := statRes.Info
   503  				Expect(info.Type).To(Equal(storagep.ResourceType_RESOURCE_TYPE_CONTAINER))
   504  				Expect(utils.ResourceIDEqual(info.Id, embeddedRef.ResourceId)).To(BeTrue())
   505  				Expect(info.Path).To(Equal(".")) // path of a root node of a space is always "."
   506  				Expect(info.Size).To(Equal(uint64(2)))
   507  			})
   508  
   509  			PIt("propagates Sizes from within the root space", func() {
   510  				// TODO: this cannot work atm as the propagation is not done by the gateway anymore
   511  				statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   512  				Expect(err).ToNot(HaveOccurred())
   513  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   514  				Expect(statRes.Info.Size).To(Equal(uint64(3)))
   515  
   516  				By("Uploading a new file")
   517  				rid, err := storagespace.ParseID(homeSpace.Id.OpaqueId)
   518  				Expect(err).ToNot(HaveOccurred())
   519  				err = helpers.Upload(ctx, fs, &storagep.Reference{ResourceId: &rid, Path: "/newfile.txt"}, []byte("1234567890"))
   520  				Expect(err).ToNot(HaveOccurred())
   521  
   522  				time.Sleep(time.Second) // cache must expire
   523  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   524  				Expect(err).ToNot(HaveOccurred())
   525  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   526  				Expect(statRes.Info.Size).To(Equal(uint64(13)))
   527  
   528  				By("Uploading a new file into a subdir")
   529  				err = fs.CreateDir(ctx, &storagep.Reference{ResourceId: &rid, Path: "/newdir"})
   530  				Expect(err).ToNot(HaveOccurred())
   531  				err = helpers.Upload(ctx, fs, &storagep.Reference{ResourceId: &rid, Path: "/newdir/newfile.txt"}, []byte("1234567890"))
   532  				Expect(err).ToNot(HaveOccurred())
   533  
   534  				time.Sleep(time.Second) // cache must expire
   535  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   536  				Expect(err).ToNot(HaveOccurred())
   537  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   538  				Expect(statRes.Info.Size).To(Equal(uint64(23)))
   539  
   540  				By("Updating an existing file")
   541  				err = helpers.Upload(ctx, fs, &storagep.Reference{ResourceId: &rid, Path: "/newdir/newfile.txt"}, []byte("12345678901234567890"))
   542  				Expect(err).ToNot(HaveOccurred())
   543  
   544  				time.Sleep(time.Second) // cache must expire
   545  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   546  				Expect(err).ToNot(HaveOccurred())
   547  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   548  				Expect(statRes.Info.Size).To(Equal(uint64(33)))
   549  			})
   550  
   551  			PIt("propagates Sizes from within the embedded space", func() {
   552  				statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   553  				Expect(err).ToNot(HaveOccurred())
   554  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   555  				Expect(statRes.Info.Size).To(Equal(uint64(3)))
   556  
   557  				By("Uploading a new file")
   558  				rid, err := storagespace.ParseID(embeddedSpace.Id.OpaqueId)
   559  				Expect(err).ToNot(HaveOccurred())
   560  				err = helpers.Upload(ctx, embeddedFs, &storagep.Reference{ResourceId: &rid, Path: "/newfile.txt"}, []byte("1234567890"))
   561  				Expect(err).ToNot(HaveOccurred())
   562  
   563  				time.Sleep(time.Second) // cache must expire
   564  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   565  				Expect(err).ToNot(HaveOccurred())
   566  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   567  				Expect(statRes.Info.Size).To(Equal(uint64(13)))
   568  
   569  				By("Uploading a new file into a subdir")
   570  				err = embeddedFs.CreateDir(ctx, &storagep.Reference{ResourceId: &rid, Path: "/newdir"})
   571  				Expect(err).ToNot(HaveOccurred())
   572  				err = helpers.Upload(ctx, embeddedFs, &storagep.Reference{ResourceId: &rid, Path: "/newdir/newfile.txt"}, []byte("1234567890"))
   573  				Expect(err).ToNot(HaveOccurred())
   574  
   575  				time.Sleep(time.Second) // cache must expire
   576  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   577  				Expect(err).ToNot(HaveOccurred())
   578  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   579  				Expect(statRes.Info.Size).To(Equal(uint64(23)))
   580  
   581  				By("Updating an existing file")
   582  				err = helpers.Upload(ctx, embeddedFs, &storagep.Reference{ResourceId: &rid, Path: "/newdir/newfile.txt"}, []byte("12345678901234567890"))
   583  				Expect(err).ToNot(HaveOccurred())
   584  
   585  				time.Sleep(time.Second) // cache must expire
   586  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   587  				Expect(err).ToNot(HaveOccurred())
   588  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   589  				Expect(statRes.Info.Size).To(Equal(uint64(33)))
   590  			})
   591  
   592  			It("propagates Etags from within the root space", func() {
   593  				statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   594  				Expect(err).ToNot(HaveOccurred())
   595  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   596  				etag := statRes.Info.Etag
   597  				ssid, err := storagespace.ParseID(homeSpace.Id.OpaqueId)
   598  				Expect(err).ToNot(HaveOccurred())
   599  				By("Uploading a new file")
   600  				err = helpers.Upload(ctx, fs, &storagep.Reference{ResourceId: &ssid, Path: "/newfile.txt"}, []byte("1"))
   601  				Expect(err).ToNot(HaveOccurred())
   602  
   603  				time.Sleep(time.Second) // cache must expire
   604  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   605  				Expect(err).ToNot(HaveOccurred())
   606  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   607  				newEtag := statRes.Info.Etag
   608  
   609  				Expect(newEtag).ToNot(Equal(etag))
   610  
   611  				By("Creating a new dir")
   612  				err = fs.CreateDir(ctx, &storagep.Reference{ResourceId: &ssid, Path: "/newdir"})
   613  				Expect(err).ToNot(HaveOccurred())
   614  
   615  				time.Sleep(time.Second) // cache must expire
   616  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   617  				Expect(err).ToNot(HaveOccurred())
   618  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   619  				newEtag2 := statRes.Info.Etag
   620  
   621  				Expect(newEtag2).ToNot(Equal(newEtag))
   622  
   623  				By("Updating an existing file")
   624  				err = helpers.Upload(ctx, fs, &storagep.Reference{ResourceId: &ssid, Path: "/file.txt"}, []byte("2"))
   625  				Expect(err).ToNot(HaveOccurred())
   626  
   627  				time.Sleep(time.Second) // cache must expire
   628  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   629  				Expect(err).ToNot(HaveOccurred())
   630  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   631  				newEtag3 := statRes.Info.Etag
   632  
   633  				Expect(newEtag3).ToNot(Equal(newEtag2))
   634  			})
   635  
   636  			PIt("propagates Etags from within the embedded space", func() {
   637  				statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   638  				Expect(err).ToNot(HaveOccurred())
   639  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   640  				etag := statRes.Info.Etag
   641  				essid, err := storagespace.ParseID(embeddedSpace.Id.OpaqueId)
   642  				Expect(err).ToNot(HaveOccurred())
   643  				By("Uploading a new file")
   644  				err = helpers.Upload(ctx, embeddedFs, &storagep.Reference{ResourceId: &essid, Path: "/newfile.txt"}, []byte("1"))
   645  				Expect(err).ToNot(HaveOccurred())
   646  
   647  				time.Sleep(time.Second) // cache must expire
   648  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   649  				Expect(err).ToNot(HaveOccurred())
   650  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   651  				newEtag := statRes.Info.Etag
   652  
   653  				Expect(newEtag).ToNot(Equal(etag))
   654  
   655  				By("Creating a new dir")
   656  				err = embeddedFs.CreateDir(ctx, &storagep.Reference{ResourceId: &essid, Path: "/newdir"})
   657  				Expect(err).ToNot(HaveOccurred())
   658  
   659  				time.Sleep(time.Second) // cache must expire
   660  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   661  				Expect(err).ToNot(HaveOccurred())
   662  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   663  				newEtag2 := statRes.Info.Etag
   664  
   665  				Expect(newEtag2).ToNot(Equal(newEtag))
   666  
   667  				By("Updating an existing file")
   668  				err = helpers.Upload(ctx, embeddedFs, &storagep.Reference{ResourceId: &essid, Path: "/newfile.txt"}, []byte("1"))
   669  				Expect(err).ToNot(HaveOccurred())
   670  
   671  				time.Sleep(time.Second) // cache must expire
   672  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: homeRef})
   673  				Expect(err).ToNot(HaveOccurred())
   674  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   675  				newEtag3 := statRes.Info.Etag
   676  
   677  				Expect(newEtag3).ToNot(Equal(newEtag2))
   678  			})
   679  		})
   680  
   681  		Describe("Move", func() {
   682  			It("moves a directory", func() {
   683  				hssid, err := storagespace.ParseID(homeSpace.Id.OpaqueId)
   684  				Expect(err).ToNot(HaveOccurred())
   685  				sourceRef := &storagep.Reference{ResourceId: &hssid, Path: "./source"}
   686  				targetRef := &storagep.Reference{ResourceId: &hssid, Path: "./destination"}
   687  				dstRef := &storagep.Reference{ResourceId: &hssid, Path: "./destination/source"}
   688  
   689  				err = fs.CreateDir(ctx, sourceRef)
   690  				Expect(err).ToNot(HaveOccurred())
   691  				err = fs.CreateDir(ctx, targetRef)
   692  				Expect(err).ToNot(HaveOccurred())
   693  
   694  				mvRes, err := serviceClient.Move(ctx, &storagep.MoveRequest{Source: sourceRef, Destination: dstRef})
   695  				Expect(err).ToNot(HaveOccurred())
   696  				Expect(mvRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   697  
   698  				statRes, err := serviceClient.Stat(ctx, &storagep.StatRequest{Ref: sourceRef})
   699  				Expect(err).ToNot(HaveOccurred())
   700  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   701  				statRes, err = serviceClient.Stat(ctx, &storagep.StatRequest{Ref: dstRef})
   702  				Expect(err).ToNot(HaveOccurred())
   703  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   704  			})
   705  		})
   706  	})
   707  })