github.com/cs3org/reva/v2@v2.27.7/tests/integration/grpc/gateway_storageprovider_static_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  
    26  	"google.golang.org/grpc/metadata"
    27  
    28  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    29  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    30  	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    31  	storagep "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    32  	"github.com/cs3org/reva/v2/pkg/auth/scope"
    33  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    34  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    35  	jwt "github.com/cs3org/reva/v2/pkg/token/manager/jwt"
    36  
    37  	. "github.com/onsi/ginkgo/v2"
    38  	. "github.com/onsi/gomega"
    39  )
    40  
    41  // This test suite tests the gprc gateway interface
    42  //
    43  // It uses the `startRevads` helper to spawn the according reva daemon and
    44  // other dependencies like a userprovider if needed.
    45  // It also sets up an authenticated context and a service client to the storage
    46  // provider to be used in the assertion functions.
    47  var _ = PDescribe("gateway using a static registry and a shard setup", func() {
    48  	// TODO: Static registry relies on gateway being not dumb  at the moment. So these won't work anymore
    49  	// FIXME: Bring me back please!
    50  	var (
    51  		dependencies = []RevadConfig{}
    52  		revads       = map[string]*Revad{}
    53  
    54  		einsteinCtx   context.Context
    55  		marieCtx      context.Context
    56  		variables     map[string]string
    57  		serviceClient gateway.GatewayAPIClient
    58  		marie         = &userpb.User{
    59  			Id: &userpb.UserId{
    60  				Idp:      "0.0.0.0:39000",
    61  				OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c",
    62  				Type:     userpb.UserType_USER_TYPE_PRIMARY,
    63  			},
    64  			Username: "marie",
    65  		}
    66  		einstein = &userpb.User{
    67  			Id: &userpb.UserId{
    68  				Idp:      "0.0.0.0:39000",
    69  				OpaqueId: "e4fb0282-fabf-4cff-b1ee-90bdc01c4eef",
    70  				Type:     userpb.UserType_USER_TYPE_PRIMARY,
    71  			},
    72  			Username: "einstein",
    73  		}
    74  		homeRef = &storagep.Reference{Path: "."}
    75  	)
    76  
    77  	BeforeEach(func() {
    78  		dependencies = []RevadConfig{
    79  			{Name: "gateway", Config: "gateway-static.toml"},
    80  			{Name: "users", Config: "userprovider-json.toml"},
    81  			{Name: "storage", Config: "storageprovider-local.toml"},
    82  			{Name: "storage2", Config: "storageprovider-local.toml"},
    83  		}
    84  		redisAddress := os.Getenv("REDIS_ADDRESS")
    85  		if redisAddress == "" {
    86  			Fail("REDIS_ADDRESS not set")
    87  		}
    88  		variables = map[string]string{
    89  			"redis_address": redisAddress,
    90  		}
    91  	})
    92  
    93  	JustBeforeEach(func() {
    94  		var err error
    95  		einsteinCtx = context.Background()
    96  		marieCtx = context.Background()
    97  
    98  		// Add auth token
    99  		tokenManager, err := jwt.New(map[string]interface{}{"secret": "changemeplease"})
   100  		Expect(err).ToNot(HaveOccurred())
   101  		scope, err := scope.AddOwnerScope(nil)
   102  		Expect(err).ToNot(HaveOccurred())
   103  
   104  		t, err := tokenManager.MintToken(marieCtx, marie, scope)
   105  		Expect(err).ToNot(HaveOccurred())
   106  		marieCtx = ctxpkg.ContextSetToken(marieCtx, t)
   107  		marieCtx = metadata.AppendToOutgoingContext(marieCtx, ctxpkg.TokenHeader, t)
   108  		marieCtx = ctxpkg.ContextSetUser(marieCtx, marie)
   109  
   110  		t, err = tokenManager.MintToken(einsteinCtx, einstein, scope)
   111  		Expect(err).ToNot(HaveOccurred())
   112  		einsteinCtx = ctxpkg.ContextSetToken(einsteinCtx, t)
   113  		einsteinCtx = metadata.AppendToOutgoingContext(einsteinCtx, ctxpkg.TokenHeader, t)
   114  		einsteinCtx = ctxpkg.ContextSetUser(einsteinCtx, einstein)
   115  
   116  		revads, err = startRevads(dependencies, variables)
   117  		Expect(err).ToNot(HaveOccurred())
   118  		Expect(revads["gateway"]).ToNot(BeNil())
   119  		serviceClient, err = pool.GetGatewayServiceClient(revads["gateway"].GrpcAddress)
   120  		Expect(err).ToNot(HaveOccurred())
   121  	})
   122  
   123  	AfterEach(func() {
   124  		for _, r := range revads {
   125  			Expect(r.Cleanup(CurrentGinkgoTestDescription().Failed)).To(Succeed())
   126  		}
   127  	})
   128  
   129  	Context("with a mapping based home jail", func() {
   130  		BeforeEach(func() {
   131  			variables["disable_home"] = "false"
   132  		})
   133  
   134  		It("creates a home directory on the correct provider", func() {
   135  			By("creating marie's home")
   136  			statRes, err := serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: homeRef})
   137  			Expect(err).ToNot(HaveOccurred())
   138  			Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   139  
   140  			res, err := serviceClient.CreateHome(marieCtx, &storagep.CreateHomeRequest{})
   141  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   142  			Expect(err).ToNot(HaveOccurred())
   143  
   144  			statRes, err = serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: homeRef})
   145  			Expect(err).ToNot(HaveOccurred())
   146  			Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   147  
   148  			// the mapping considers the opaque id: f... -> storage2
   149  			fi, err := os.Stat(path.Join(revads["storage2"].StorageRoot, "data/f", marie.Id.OpaqueId))
   150  			Expect(err).ToNot(HaveOccurred())
   151  			Expect(fi.IsDir()).To(BeTrue())
   152  			_, err = os.Stat(path.Join(revads["storage"].StorageRoot, "data/f", marie.Id.OpaqueId))
   153  			Expect(err).To(HaveOccurred())
   154  
   155  			ghRes, err := serviceClient.GetHome(marieCtx, &storagep.GetHomeRequest{})
   156  			Expect(err).ToNot(HaveOccurred())
   157  			Expect(ghRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   158  
   159  			By("creating einstein's home")
   160  			statRes, err = serviceClient.Stat(einsteinCtx, &storagep.StatRequest{Ref: homeRef})
   161  			Expect(err).ToNot(HaveOccurred())
   162  			Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   163  
   164  			res, err = serviceClient.CreateHome(einsteinCtx, &storagep.CreateHomeRequest{})
   165  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   166  			Expect(err).ToNot(HaveOccurred())
   167  
   168  			statRes, err = serviceClient.Stat(einsteinCtx, &storagep.StatRequest{Ref: homeRef})
   169  			Expect(err).ToNot(HaveOccurred())
   170  			Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   171  
   172  			// the mapping considers the opaque id: e... -> storage
   173  			fi, err = os.Stat(path.Join(revads["storage"].StorageRoot, "data/e", einstein.Id.OpaqueId))
   174  			Expect(err).ToNot(HaveOccurred())
   175  			Expect(fi.IsDir()).To(BeTrue())
   176  			_, err = os.Stat(path.Join(revads["storage2"].StorageRoot, "data/e", einstein.Id.OpaqueId))
   177  			Expect(err).To(HaveOccurred())
   178  
   179  			ghRes, err = serviceClient.GetHome(einsteinCtx, &storagep.GetHomeRequest{})
   180  			Expect(err).ToNot(HaveOccurred())
   181  			Expect(ghRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   182  		})
   183  
   184  		Context("with a home directory", func() {
   185  			JustBeforeEach(func() {
   186  				res, err := serviceClient.CreateHome(marieCtx, &storagep.CreateHomeRequest{})
   187  				Expect(err).ToNot(HaveOccurred())
   188  				Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   189  			})
   190  
   191  			It("creates and lists a new directory", func() {
   192  				newRef := &storagep.Reference{Path: "/home/newdir"}
   193  
   194  				listRes, err := serviceClient.ListContainer(marieCtx, &storagep.ListContainerRequest{Ref: homeRef})
   195  				Expect(err).ToNot(HaveOccurred())
   196  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   197  				Expect(len(listRes.Infos)).To(Equal(1))
   198  				Expect(listRes.Infos[0].Path).To(Equal("/home/MyShares"))
   199  
   200  				statRes, err := serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: newRef})
   201  				Expect(err).ToNot(HaveOccurred())
   202  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   203  
   204  				res, err := serviceClient.CreateContainer(marieCtx, &storagep.CreateContainerRequest{Ref: newRef})
   205  				Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   206  				Expect(err).ToNot(HaveOccurred())
   207  
   208  				statRes, err = serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: newRef})
   209  				Expect(err).ToNot(HaveOccurred())
   210  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   211  
   212  				listRes, err = serviceClient.ListContainer(marieCtx, &storagep.ListContainerRequest{Ref: homeRef})
   213  				Expect(err).ToNot(HaveOccurred())
   214  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   215  				Expect(len(listRes.Infos)).To(Equal(2))
   216  				paths := []string{}
   217  				for _, i := range listRes.Infos {
   218  					paths = append(paths, i.Path)
   219  				}
   220  				Expect(paths).To(ConsistOf("/home/MyShares", newRef.Path))
   221  
   222  				listRes, err = serviceClient.ListContainer(marieCtx, &storagep.ListContainerRequest{Ref: newRef})
   223  				Expect(err).ToNot(HaveOccurred())
   224  				Expect(listRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   225  			})
   226  
   227  			Context("and a subdirectory", func() {
   228  				var (
   229  					subdirRef = &storagep.Reference{Path: "/home/subdir"}
   230  				)
   231  
   232  				JustBeforeEach(func() {
   233  					createRes, err := serviceClient.CreateContainer(marieCtx, &storagep.CreateContainerRequest{Ref: subdirRef})
   234  					Expect(createRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   235  					Expect(err).ToNot(HaveOccurred())
   236  				})
   237  
   238  				It("gets the path to an ID", func() {
   239  					statRes, err := serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: subdirRef})
   240  					Expect(err).ToNot(HaveOccurred())
   241  					Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   242  
   243  					getPathRes, err := serviceClient.GetPath(marieCtx, &storagep.GetPathRequest{ResourceId: statRes.Info.Id})
   244  					Expect(err).ToNot(HaveOccurred())
   245  					Expect(getPathRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   246  				})
   247  
   248  				It("stats by path and by ID", func() {
   249  					statRes, err := serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: subdirRef})
   250  					Expect(err).ToNot(HaveOccurred())
   251  					Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   252  
   253  					idRef := &storagep.Reference{ResourceId: &storagep.ResourceId{StorageId: revads["storage2"].ID, OpaqueId: statRes.Info.Id.OpaqueId}}
   254  					statRes, err = serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: idRef})
   255  					Expect(err).ToNot(HaveOccurred())
   256  					Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   257  				})
   258  
   259  				It("moves and deletes a directory", func() {
   260  					newRef2 := &storagep.Reference{Path: "/home/newdir2"}
   261  
   262  					moveRes, err := serviceClient.Move(marieCtx, &storagep.MoveRequest{Source: subdirRef, Destination: newRef2})
   263  					Expect(err).ToNot(HaveOccurred())
   264  					Expect(moveRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   265  
   266  					statRes, err := serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: newRef2})
   267  					Expect(err).ToNot(HaveOccurred())
   268  					Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   269  
   270  					deleteRes, err := serviceClient.Delete(marieCtx, &storagep.DeleteRequest{Ref: newRef2})
   271  					Expect(deleteRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   272  					Expect(err).ToNot(HaveOccurred())
   273  
   274  					statRes, err = serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: newRef2})
   275  					Expect(err).ToNot(HaveOccurred())
   276  					Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   277  				})
   278  			})
   279  		})
   280  	})
   281  
   282  	Context("with a sharded /users mount", func() {
   283  		var (
   284  			homePath  = "/users/f/f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"
   285  			rootRef   = &storagep.Reference{Path: path.Join("/users")}
   286  			baseRef   = &storagep.Reference{Path: path.Join("/users/f")}
   287  			homeRef   = &storagep.Reference{Path: homePath}
   288  			subdirRef = &storagep.Reference{Path: path.Join(homePath, "subdir")}
   289  		)
   290  
   291  		BeforeEach(func() {
   292  			variables["disable_home"] = "true"
   293  		})
   294  
   295  		It("merges the results of both /users providers", func() {
   296  			lRes, err := serviceClient.ListContainer(marieCtx, &storagep.ListContainerRequest{Ref: &storagep.Reference{Path: "/users"}})
   297  			Expect(err).ToNot(HaveOccurred())
   298  			Expect(lRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   299  			Expect(len(lRes.Infos)).To(Equal(36))
   300  
   301  			lRes, err = serviceClient.ListContainer(marieCtx, &storagep.ListContainerRequest{Ref: &storagep.Reference{Path: "/users/f"}})
   302  			Expect(err).ToNot(HaveOccurred())
   303  			Expect(lRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   304  			Expect(len(lRes.Infos)).To(Equal(0))
   305  
   306  			res, err := serviceClient.CreateContainer(einsteinCtx, &storagep.CreateContainerRequest{
   307  				Ref: &storagep.Reference{
   308  					Path: path.Join("/users/e"),
   309  				},
   310  			})
   311  			Expect(err).ToNot(HaveOccurred())
   312  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   313  
   314  			res, err = serviceClient.CreateContainer(einsteinCtx, &storagep.CreateContainerRequest{
   315  				Ref: &storagep.Reference{
   316  					Path: path.Join("/users/e", einstein.Id.OpaqueId),
   317  				},
   318  			})
   319  			Expect(err).ToNot(HaveOccurred())
   320  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   321  
   322  			lRes, err = serviceClient.ListContainer(einsteinCtx, &storagep.ListContainerRequest{Ref: &storagep.Reference{Path: "/users/e"}})
   323  			Expect(err).ToNot(HaveOccurred())
   324  			Expect(lRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   325  			Expect(len(lRes.Infos)).To(Equal(1))
   326  			Expect(lRes.Infos[0].Path).To(Equal("/users/e/e4fb0282-fabf-4cff-b1ee-90bdc01c4eef"))
   327  
   328  			lRes, err = serviceClient.ListContainer(einsteinCtx, &storagep.ListContainerRequest{Ref: &storagep.Reference{Path: "/users/d"}})
   329  			Expect(err).ToNot(HaveOccurred())
   330  			Expect(lRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   331  			Expect(len(lRes.Infos)).To(Equal(0))
   332  
   333  			res, err = serviceClient.CreateContainer(einsteinCtx, &storagep.CreateContainerRequest{
   334  				Ref: &storagep.Reference{
   335  					Path: path.Join("/users/f"),
   336  				},
   337  			})
   338  			Expect(err).ToNot(HaveOccurred())
   339  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   340  
   341  			res, err = serviceClient.CreateContainer(marieCtx, &storagep.CreateContainerRequest{
   342  				Ref: &storagep.Reference{
   343  					Path: path.Join("/users/f", marie.Id.OpaqueId),
   344  				},
   345  			})
   346  			Expect(err).ToNot(HaveOccurred())
   347  			Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   348  
   349  			lRes, err = serviceClient.ListContainer(marieCtx, &storagep.ListContainerRequest{Ref: &storagep.Reference{Path: "/users/f"}})
   350  			Expect(err).ToNot(HaveOccurred())
   351  			Expect(lRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   352  			Expect(len(lRes.Infos)).To(Equal(1))
   353  			Expect(lRes.Infos[0].Path).To(Equal("/users/f/f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"))
   354  		})
   355  
   356  		Context("with a user home", func() {
   357  			JustBeforeEach(func() {
   358  				res, err := serviceClient.CreateContainer(marieCtx, &storagep.CreateContainerRequest{
   359  					Ref: &storagep.Reference{
   360  						Path: path.Join("/users/f"),
   361  					},
   362  				})
   363  				Expect(err).ToNot(HaveOccurred())
   364  				Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   365  				res, err = serviceClient.CreateContainer(marieCtx, &storagep.CreateContainerRequest{
   366  					Ref: &storagep.Reference{
   367  						Path: path.Join("/users/f", marie.Id.OpaqueId),
   368  					},
   369  				})
   370  				Expect(err).ToNot(HaveOccurred())
   371  				Expect(res.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   372  			})
   373  
   374  			It("provides access to the user home", func() {
   375  				newRef := &storagep.Reference{Path: path.Join(homePath, "newName")}
   376  
   377  				createRes, err := serviceClient.CreateContainer(marieCtx, &storagep.CreateContainerRequest{Ref: subdirRef})
   378  				Expect(createRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   379  				Expect(err).ToNot(HaveOccurred())
   380  
   381  				statRes, err := serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: homeRef})
   382  				Expect(err).ToNot(HaveOccurred())
   383  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   384  				Expect(statRes.Info.Path).To(Equal(homePath))
   385  
   386  				lRes, err := serviceClient.ListContainer(marieCtx, &storagep.ListContainerRequest{Ref: homeRef})
   387  				Expect(err).ToNot(HaveOccurred())
   388  				Expect(lRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   389  				Expect(len(lRes.Infos)).To(Equal(1))
   390  				Expect(lRes.Infos[0].Path).To(Equal(subdirRef.Path))
   391  
   392  				mRes, err := serviceClient.Move(marieCtx, &storagep.MoveRequest{Source: subdirRef, Destination: newRef})
   393  				Expect(mRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   394  				Expect(err).ToNot(HaveOccurred())
   395  
   396  				dRes, err := serviceClient.Delete(marieCtx, &storagep.DeleteRequest{Ref: newRef})
   397  				Expect(dRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   398  				Expect(err).ToNot(HaveOccurred())
   399  
   400  				statRes, err = serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: newRef})
   401  				Expect(err).ToNot(HaveOccurred())
   402  				Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_NOT_FOUND))
   403  			})
   404  
   405  			It("propagates the etag to the root", func() {
   406  				getEtag := func(r *storagep.Reference) string {
   407  					statRes, err := serviceClient.Stat(marieCtx, &storagep.StatRequest{Ref: r})
   408  					Expect(err).ToNot(HaveOccurred())
   409  					Expect(statRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   410  					return statRes.Info.Etag
   411  				}
   412  
   413  				rootEtag := getEtag(rootRef)
   414  				baseEtag := getEtag(baseRef)
   415  				userEtag := getEtag(homeRef)
   416  
   417  				createRes, err := serviceClient.CreateContainer(marieCtx, &storagep.CreateContainerRequest{Ref: subdirRef})
   418  				Expect(err).ToNot(HaveOccurred())
   419  				Expect(createRes.Status.Code).To(Equal(rpcv1beta1.Code_CODE_OK))
   420  
   421  				Expect(getEtag(homeRef)).ToNot(Equal(userEtag))
   422  				Expect(getEtag(baseRef)).ToNot(Equal(baseEtag))
   423  				Expect(getEtag(rootRef)).ToNot(Equal(rootEtag))
   424  			})
   425  		})
   426  	})
   427  })