cloud.google.com/go/storage@v1.40.0/client_test.go (about)

     1  // Copyright 2022 Google LLC
     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  package storage
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"log"
    21  	"os"
    22  	"strconv"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"cloud.google.com/go/iam/apiv1/iampb"
    28  	"github.com/google/go-cmp/cmp"
    29  	"google.golang.org/api/iterator"
    30  )
    31  
    32  var emulatorClients map[string]storageClient
    33  var veneerClient *Client
    34  
    35  func TestCreateBucketEmulated(t *testing.T) {
    36  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
    37  		want := &BucketAttrs{
    38  			Name: bucket,
    39  			Logging: &BucketLogging{
    40  				LogBucket: bucket,
    41  			},
    42  		}
    43  		got, err := client.CreateBucket(context.Background(), project, want.Name, want, nil)
    44  		if err != nil {
    45  			t.Fatal(err)
    46  		}
    47  		want.Location = "US"
    48  		if diff := cmp.Diff(got.Name, want.Name); diff != "" {
    49  			t.Errorf("Name got(-),want(+):\n%s", diff)
    50  		}
    51  		if diff := cmp.Diff(got.Location, want.Location); diff != "" {
    52  			t.Errorf("Location got(-),want(+):\n%s", diff)
    53  		}
    54  		if diff := cmp.Diff(got.Logging.LogBucket, want.Logging.LogBucket); diff != "" {
    55  			t.Errorf("LogBucket got(-),want(+):\n%s", diff)
    56  		}
    57  	})
    58  }
    59  
    60  func TestDeleteBucketEmulated(t *testing.T) {
    61  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
    62  		b := &BucketAttrs{
    63  			Name: bucket,
    64  		}
    65  		// Create the bucket that will be deleted.
    66  		_, err := client.CreateBucket(context.Background(), project, b.Name, b, nil)
    67  		if err != nil {
    68  			t.Fatalf("client.CreateBucket: %v", err)
    69  		}
    70  		// Delete the bucket that was just created.
    71  		err = client.DeleteBucket(context.Background(), b.Name, nil)
    72  		if err != nil {
    73  			t.Fatalf("client.DeleteBucket: %v", err)
    74  		}
    75  	})
    76  }
    77  
    78  func TestGetBucketEmulated(t *testing.T) {
    79  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
    80  		want := &BucketAttrs{
    81  			Name: bucket,
    82  		}
    83  		// Create the bucket that will be retrieved.
    84  		_, err := client.CreateBucket(context.Background(), project, want.Name, want, nil)
    85  		if err != nil {
    86  			t.Fatalf("client.CreateBucket: %v", err)
    87  		}
    88  		got, err := client.GetBucket(context.Background(), want.Name, &BucketConditions{MetagenerationMatch: 1})
    89  		if err != nil {
    90  			t.Fatal(err)
    91  		}
    92  		if diff := cmp.Diff(got.Name, want.Name); diff != "" {
    93  			t.Errorf("got(-),want(+):\n%s", diff)
    94  		}
    95  	})
    96  }
    97  
    98  func TestUpdateBucketEmulated(t *testing.T) {
    99  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   100  		bkt := &BucketAttrs{
   101  			Name: bucket,
   102  		}
   103  		// Create the bucket that will be updated.
   104  		_, err := client.CreateBucket(context.Background(), project, bkt.Name, bkt, nil)
   105  		if err != nil {
   106  			t.Fatalf("client.CreateBucket: %v", err)
   107  		}
   108  
   109  		ua := &BucketAttrsToUpdate{
   110  			VersioningEnabled:     false,
   111  			RequesterPays:         false,
   112  			DefaultEventBasedHold: false,
   113  			Encryption:            &BucketEncryption{DefaultKMSKeyName: "key2"},
   114  			Lifecycle: &Lifecycle{
   115  				Rules: []LifecycleRule{
   116  					{
   117  						Action:    LifecycleAction{Type: "Delete"},
   118  						Condition: LifecycleCondition{AgeInDays: 30},
   119  					},
   120  				},
   121  			},
   122  			Logging:      &BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
   123  			Website:      &BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
   124  			StorageClass: "NEARLINE",
   125  			RPO:          RPOAsyncTurbo,
   126  		}
   127  		want := &BucketAttrs{
   128  			Name:                  bucket,
   129  			VersioningEnabled:     false,
   130  			RequesterPays:         false,
   131  			DefaultEventBasedHold: false,
   132  			Encryption:            &BucketEncryption{DefaultKMSKeyName: "key2"},
   133  			Lifecycle: Lifecycle{
   134  				Rules: []LifecycleRule{
   135  					{
   136  						Action:    LifecycleAction{Type: "Delete"},
   137  						Condition: LifecycleCondition{AgeInDays: 30},
   138  					},
   139  				},
   140  			},
   141  			Logging:      &BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
   142  			Website:      &BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
   143  			StorageClass: "NEARLINE",
   144  			RPO:          RPOAsyncTurbo,
   145  		}
   146  
   147  		got, err := client.UpdateBucket(context.Background(), bucket, ua, &BucketConditions{MetagenerationMatch: 1})
   148  		if err != nil {
   149  			t.Fatal(err)
   150  		}
   151  		if diff := cmp.Diff(got.Name, want.Name); diff != "" {
   152  			t.Errorf("Name: got(-),want(+):\n%s", diff)
   153  		}
   154  		if diff := cmp.Diff(got.VersioningEnabled, want.VersioningEnabled); diff != "" {
   155  			t.Errorf("VersioningEnabled: got(-),want(+):\n%s", diff)
   156  		}
   157  		if diff := cmp.Diff(got.RequesterPays, want.RequesterPays); diff != "" {
   158  			t.Errorf("RequesterPays: got(-),want(+):\n%s", diff)
   159  		}
   160  		if diff := cmp.Diff(got.DefaultEventBasedHold, want.DefaultEventBasedHold); diff != "" {
   161  			t.Errorf("DefaultEventBasedHold: got(-),want(+):\n%s", diff)
   162  		}
   163  		if diff := cmp.Diff(got.Encryption, want.Encryption); diff != "" {
   164  			t.Errorf("Encryption: got(-),want(+):\n%s", diff)
   165  		}
   166  		if diff := cmp.Diff(got.Lifecycle, want.Lifecycle); diff != "" {
   167  			t.Errorf("Lifecycle: got(-),want(+):\n%s", diff)
   168  		}
   169  		if diff := cmp.Diff(got.Logging, want.Logging); diff != "" {
   170  			t.Errorf("Logging: got(-),want(+):\n%s", diff)
   171  		}
   172  		if diff := cmp.Diff(got.Website, want.Website); diff != "" {
   173  			t.Errorf("Website: got(-),want(+):\n%s", diff)
   174  		}
   175  		if diff := cmp.Diff(got.RPO, want.RPO); diff != "" {
   176  			t.Errorf("RPO: got(-),want(+):\n%s", diff)
   177  		}
   178  		if diff := cmp.Diff(got.StorageClass, want.StorageClass); diff != "" {
   179  			t.Errorf("StorageClass: got(-),want(+):\n%s", diff)
   180  		}
   181  	})
   182  }
   183  
   184  func TestGetServiceAccountEmulated(t *testing.T) {
   185  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   186  		_, err := client.GetServiceAccount(context.Background(), project)
   187  		if err != nil {
   188  			t.Fatalf("client.GetServiceAccount: %v", err)
   189  		}
   190  	})
   191  }
   192  
   193  func TestGetSetTestIamPolicyEmulated(t *testing.T) {
   194  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   195  		battrs, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   196  			Name: bucket,
   197  		}, nil)
   198  		if err != nil {
   199  			t.Fatalf("client.CreateBucket: %v", err)
   200  		}
   201  		got, err := client.GetIamPolicy(context.Background(), battrs.Name, 0)
   202  		if err != nil {
   203  			t.Fatalf("client.GetIamPolicy: %v", err)
   204  		}
   205  		err = client.SetIamPolicy(context.Background(), battrs.Name, &iampb.Policy{
   206  			Etag:     got.GetEtag(),
   207  			Bindings: []*iampb.Binding{{Role: "roles/viewer", Members: []string{"allUsers"}}},
   208  		})
   209  		if err != nil {
   210  			t.Fatalf("client.SetIamPolicy: %v", err)
   211  		}
   212  		want := []string{"storage.foo", "storage.bar"}
   213  		perms, err := client.TestIamPermissions(context.Background(), battrs.Name, want)
   214  		if err != nil {
   215  			t.Fatalf("client.TestIamPermissions: %v", err)
   216  		}
   217  		if diff := cmp.Diff(perms, want); diff != "" {
   218  			t.Errorf("got(-),want(+):\n%s", diff)
   219  		}
   220  	})
   221  }
   222  
   223  func TestDeleteObjectEmulated(t *testing.T) {
   224  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   225  		// Populate test object that will be deleted.
   226  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   227  			Name: bucket,
   228  		}, nil)
   229  		if err != nil {
   230  			t.Fatalf("client.CreateBucket: %v", err)
   231  		}
   232  		want := ObjectAttrs{
   233  			Bucket: bucket,
   234  			Name:   fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
   235  		}
   236  		w := veneerClient.Bucket(bucket).Object(want.Name).NewWriter(context.Background())
   237  		if _, err := w.Write(randomBytesToWrite); err != nil {
   238  			t.Fatalf("failed to populate test object: %v", err)
   239  		}
   240  		if err := w.Close(); err != nil {
   241  			t.Fatalf("closing object: %v", err)
   242  		}
   243  		err = client.DeleteObject(context.Background(), bucket, want.Name, defaultGen, nil)
   244  		if err != nil {
   245  			t.Fatalf("client.DeleteBucket: %v", err)
   246  		}
   247  	})
   248  }
   249  
   250  func TestGetObjectEmulated(t *testing.T) {
   251  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   252  		// Populate test object.
   253  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   254  			Name: bucket,
   255  		}, nil)
   256  		if err != nil {
   257  			t.Fatalf("client.CreateBucket: %v", err)
   258  		}
   259  		want := ObjectAttrs{
   260  			Bucket: bucket,
   261  			Name:   fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
   262  		}
   263  		w := veneerClient.Bucket(bucket).Object(want.Name).NewWriter(context.Background())
   264  		if _, err := w.Write(randomBytesToWrite); err != nil {
   265  			t.Fatalf("failed to populate test object: %v", err)
   266  		}
   267  		if err := w.Close(); err != nil {
   268  			t.Fatalf("closing object: %v", err)
   269  		}
   270  		got, err := client.GetObject(context.Background(), bucket, want.Name, defaultGen, nil, nil)
   271  		if err != nil {
   272  			t.Fatal(err)
   273  		}
   274  		if diff := cmp.Diff(got.Name, want.Name); diff != "" {
   275  			t.Errorf("got(-),want(+):\n%s", diff)
   276  		}
   277  	})
   278  }
   279  
   280  func TestRewriteObjectEmulated(t *testing.T) {
   281  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   282  		// Populate test object.
   283  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   284  			Name: bucket,
   285  		}, nil)
   286  		if err != nil {
   287  			t.Fatalf("client.CreateBucket: %v", err)
   288  		}
   289  		src := ObjectAttrs{
   290  			Bucket: bucket,
   291  			Name:   fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
   292  		}
   293  		w := veneerClient.Bucket(bucket).Object(src.Name).NewWriter(context.Background())
   294  		if _, err := w.Write(randomBytesToWrite); err != nil {
   295  			t.Fatalf("failed to populate test object: %v", err)
   296  		}
   297  		if err := w.Close(); err != nil {
   298  			t.Fatalf("closing object: %v", err)
   299  		}
   300  		req := &rewriteObjectRequest{
   301  			dstObject: destinationObject{
   302  				bucket: bucket,
   303  				name:   fmt.Sprintf("copy-of-%s", src.Name),
   304  				attrs:  &ObjectAttrs{},
   305  			},
   306  			srcObject: sourceObject{
   307  				bucket: bucket,
   308  				name:   src.Name,
   309  				gen:    defaultGen,
   310  			},
   311  		}
   312  		got, err := client.RewriteObject(context.Background(), req)
   313  		if err != nil {
   314  			t.Fatal(err)
   315  		}
   316  		if !got.done {
   317  			t.Fatal("didn't finish writing!")
   318  		}
   319  		if want := int64(len(randomBytesToWrite)); got.written != want {
   320  			t.Errorf("Bytes written: got %d, want %d", got.written, want)
   321  		}
   322  	})
   323  }
   324  
   325  func TestUpdateObjectEmulated(t *testing.T) {
   326  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   327  		// Populate test object.
   328  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   329  			Name: bucket,
   330  		}, nil)
   331  		if err != nil {
   332  			t.Fatalf("client.CreateBucket: %v", err)
   333  		}
   334  		ct := time.Date(2022, 5, 25, 12, 12, 12, 0, time.UTC)
   335  		o := ObjectAttrs{
   336  			Bucket:     bucket,
   337  			Name:       fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
   338  			CustomTime: ct,
   339  		}
   340  		w := veneerClient.Bucket(bucket).Object(o.Name).NewWriter(context.Background())
   341  		if _, err := w.Write(randomBytesToWrite); err != nil {
   342  			t.Fatalf("failed to populate test object: %v", err)
   343  		}
   344  		if err := w.Close(); err != nil {
   345  			t.Fatalf("closing object: %v", err)
   346  		}
   347  		want := &ObjectAttrsToUpdate{
   348  			EventBasedHold:     false,
   349  			TemporaryHold:      false,
   350  			ContentType:        "text/html",
   351  			ContentLanguage:    "en",
   352  			ContentEncoding:    "gzip",
   353  			ContentDisposition: "",
   354  			CacheControl:       "",
   355  			CustomTime:         ct.Add(10 * time.Hour),
   356  		}
   357  
   358  		params := &updateObjectParams{bucket: bucket, object: o.Name, uattrs: want, gen: defaultGen, conds: &Conditions{MetagenerationMatch: 1}}
   359  		got, err := client.UpdateObject(context.Background(), params)
   360  		if err != nil {
   361  			t.Fatalf("client.UpdateObject: %v", err)
   362  		}
   363  		if diff := cmp.Diff(got.Name, o.Name); diff != "" {
   364  			t.Errorf("Name: got(-),want(+):\n%s", diff)
   365  		}
   366  		if diff := cmp.Diff(got.EventBasedHold, want.EventBasedHold); diff != "" {
   367  			t.Errorf("EventBasedHold: got(-),want(+):\n%s", diff)
   368  		}
   369  		if diff := cmp.Diff(got.TemporaryHold, want.TemporaryHold); diff != "" {
   370  			t.Errorf("TemporaryHold: got(-),want(+):\n%s", diff)
   371  		}
   372  		if diff := cmp.Diff(got.ContentType, want.ContentType); diff != "" {
   373  			t.Errorf("ContentType: got(-),want(+):\n%s", diff)
   374  		}
   375  		if diff := cmp.Diff(got.ContentLanguage, want.ContentLanguage); diff != "" {
   376  			t.Errorf("ContentLanguage: got(-),want(+):\n%s", diff)
   377  		}
   378  		if diff := cmp.Diff(got.ContentEncoding, want.ContentEncoding); diff != "" {
   379  			t.Errorf("ContentEncoding: got(-),want(+):\n%s", diff)
   380  		}
   381  		if diff := cmp.Diff(got.ContentDisposition, want.ContentDisposition); diff != "" {
   382  			t.Errorf("ContentDisposition: got(-),want(+):\n%s", diff)
   383  		}
   384  		if diff := cmp.Diff(got.CacheControl, want.CacheControl); diff != "" {
   385  			t.Errorf("CacheControl: got(-),want(+):\n%s", diff)
   386  		}
   387  		if diff := cmp.Diff(got.CustomTime, want.CustomTime); diff != "" {
   388  			t.Errorf("CustomTime: got(-),want(+):\n%s", diff)
   389  		}
   390  	})
   391  }
   392  
   393  func TestListObjectsEmulated(t *testing.T) {
   394  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   395  		// Populate test data.
   396  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   397  			Name: bucket,
   398  		}, nil)
   399  		if err != nil {
   400  			t.Fatalf("client.CreateBucket: %v", err)
   401  		}
   402  		prefix := time.Now().Nanosecond()
   403  		want := []*ObjectAttrs{
   404  			{
   405  				Bucket: bucket,
   406  				Name:   fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
   407  			},
   408  			{
   409  				Bucket: bucket,
   410  				Name:   fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
   411  			},
   412  			{
   413  				Bucket: bucket,
   414  				Name:   fmt.Sprintf("object-%d", time.Now().Nanosecond()),
   415  			},
   416  		}
   417  		for _, obj := range want {
   418  			w := veneerClient.Bucket(bucket).Object(obj.Name).NewWriter(context.Background())
   419  			if _, err := w.Write(randomBytesToWrite); err != nil {
   420  				t.Fatalf("failed to populate test data: %v", err)
   421  			}
   422  			if err := w.Close(); err != nil {
   423  				t.Fatalf("closing object: %v", err)
   424  			}
   425  		}
   426  
   427  		// Simple list, no query.
   428  		it := client.ListObjects(context.Background(), bucket, nil)
   429  		var o *ObjectAttrs
   430  		var got int
   431  		for i := 0; err == nil && i <= len(want); i++ {
   432  			o, err = it.Next()
   433  			if err != nil {
   434  				break
   435  			}
   436  			got++
   437  			if diff := cmp.Diff(o.Name, want[i].Name); diff != "" {
   438  				t.Errorf("got(-),want(+):\n%s", diff)
   439  			}
   440  		}
   441  		if err != iterator.Done {
   442  			t.Fatalf("expected %q but got %q", iterator.Done, err)
   443  		}
   444  		expected := len(want)
   445  		if got != expected {
   446  			t.Errorf("expected to get %d objects, but got %d", expected, got)
   447  		}
   448  	})
   449  }
   450  
   451  func TestListObjectsWithPrefixEmulated(t *testing.T) {
   452  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   453  		// Populate test data.
   454  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   455  			Name: bucket,
   456  		}, nil)
   457  		if err != nil {
   458  			t.Fatalf("client.CreateBucket: %v", err)
   459  		}
   460  		prefix := time.Now().Nanosecond()
   461  		want := []*ObjectAttrs{
   462  			{
   463  				Bucket: bucket,
   464  				Name:   fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
   465  			},
   466  			{
   467  				Bucket: bucket,
   468  				Name:   fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
   469  			},
   470  			{
   471  				Bucket: bucket,
   472  				Name:   fmt.Sprintf("object-%d", time.Now().Nanosecond()),
   473  			},
   474  		}
   475  		for _, obj := range want {
   476  			w := veneerClient.Bucket(bucket).Object(obj.Name).NewWriter(context.Background())
   477  			if _, err := w.Write(randomBytesToWrite); err != nil {
   478  				t.Fatalf("failed to populate test data: %v", err)
   479  			}
   480  			if err := w.Close(); err != nil {
   481  				t.Fatalf("closing object: %v", err)
   482  			}
   483  		}
   484  
   485  		// Query with Prefix.
   486  		it := client.ListObjects(context.Background(), bucket, &Query{Prefix: strconv.Itoa(prefix)})
   487  		var o *ObjectAttrs
   488  		var got int
   489  		want = want[:2]
   490  		for i := 0; i <= len(want); i++ {
   491  			o, err = it.Next()
   492  			if err != nil {
   493  				break
   494  			}
   495  			got++
   496  			if diff := cmp.Diff(o.Name, want[i].Name); diff != "" {
   497  				t.Errorf("got(-),want(+):\n%s", diff)
   498  			}
   499  		}
   500  		if err != iterator.Done {
   501  			t.Fatalf("expected %q but got %q", iterator.Done, err)
   502  		}
   503  		expected := len(want)
   504  		if got != expected {
   505  			t.Errorf("expected to get %d objects, but got %d", expected, got)
   506  		}
   507  	})
   508  }
   509  
   510  func TestListBucketsEmulated(t *testing.T) {
   511  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   512  		prefix := time.Now().Nanosecond()
   513  		want := []*BucketAttrs{
   514  			{Name: fmt.Sprintf("%d-%s-%d", prefix, bucket, time.Now().Nanosecond())},
   515  			{Name: fmt.Sprintf("%d-%s-%d", prefix, bucket, time.Now().Nanosecond())},
   516  			{Name: fmt.Sprintf("%s-%d", bucket, time.Now().Nanosecond())},
   517  		}
   518  		// Create the buckets that will be listed.
   519  		for _, b := range want {
   520  			_, err := client.CreateBucket(context.Background(), project, b.Name, b, nil)
   521  			if err != nil {
   522  				t.Fatalf("client.CreateBucket: %v", err)
   523  			}
   524  		}
   525  
   526  		it := client.ListBuckets(context.Background(), project)
   527  		it.Prefix = strconv.Itoa(prefix)
   528  		// Drop the non-prefixed bucket from the expected results.
   529  		want = want[:2]
   530  		var err error
   531  		var b *BucketAttrs
   532  		var got int
   533  		for i := 0; err == nil && i <= len(want); i++ {
   534  			b, err = it.Next()
   535  			if err != nil {
   536  				break
   537  			}
   538  			got++
   539  			if diff := cmp.Diff(b.Name, want[i].Name); diff != "" {
   540  				t.Errorf("got(-),want(+):\n%s", diff)
   541  			}
   542  		}
   543  		if err != iterator.Done {
   544  			t.Fatalf("expected %q but got %q", iterator.Done, err)
   545  		}
   546  		expected := len(want)
   547  		if got != expected {
   548  			t.Errorf("expected to get %d buckets, but got %d", expected, got)
   549  		}
   550  	})
   551  }
   552  
   553  func TestListBucketACLsEmulated(t *testing.T) {
   554  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   555  		ctx := context.Background()
   556  		attrs := &BucketAttrs{
   557  			Name:          bucket,
   558  			PredefinedACL: "publicRead",
   559  		}
   560  		// Create the bucket that will be retrieved.
   561  		if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
   562  			t.Fatalf("client.CreateBucket: %v", err)
   563  		}
   564  
   565  		acls, err := client.ListBucketACLs(ctx, bucket)
   566  		if err != nil {
   567  			t.Fatalf("client.ListBucketACLs: %v", err)
   568  		}
   569  		if want, got := len(acls), 2; want != got {
   570  			t.Errorf("ListBucketACLs: got %v, want %v items", acls, want)
   571  		}
   572  	})
   573  }
   574  
   575  func TestUpdateBucketACLEmulated(t *testing.T) {
   576  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   577  		ctx := context.Background()
   578  		attrs := &BucketAttrs{
   579  			Name:          bucket,
   580  			PredefinedACL: "authenticatedRead",
   581  		}
   582  		// Create the bucket that will be retrieved.
   583  		if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
   584  			t.Fatalf("client.CreateBucket: %v", err)
   585  		}
   586  		var listAcls []ACLRule
   587  		var err error
   588  		// Assert bucket has two BucketACL entities, including project owner and predefinedACL.
   589  		if listAcls, err = client.ListBucketACLs(ctx, bucket); err != nil {
   590  			t.Fatalf("client.ListBucketACLs: %v", err)
   591  		}
   592  		if got, want := len(listAcls), 2; got != want {
   593  			t.Errorf("ListBucketACLs: got %v, want %v items", listAcls, want)
   594  		}
   595  		entity := AllUsers
   596  		role := RoleReader
   597  		err = client.UpdateBucketACL(ctx, bucket, entity, role)
   598  		if err != nil {
   599  			t.Fatalf("client.UpdateBucketACL: %v", err)
   600  		}
   601  		// Assert bucket now has three BucketACL entities, including existing ACLs.
   602  		if listAcls, err = client.ListBucketACLs(ctx, bucket); err != nil {
   603  			t.Fatalf("client.ListBucketACLs: %v", err)
   604  		}
   605  		if got, want := len(listAcls), 3; got != want {
   606  			t.Errorf("ListBucketACLs: got %v, want %v items", listAcls, want)
   607  		}
   608  	})
   609  }
   610  
   611  func TestDeleteBucketACLEmulated(t *testing.T) {
   612  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   613  		ctx := context.Background()
   614  		attrs := &BucketAttrs{
   615  			Name:          bucket,
   616  			PredefinedACL: "publicRead",
   617  		}
   618  		// Create the bucket that will be retrieved.
   619  		if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
   620  			t.Fatalf("client.CreateBucket: %v", err)
   621  		}
   622  		// Assert bucket has two BucketACL entities, including project owner and predefinedACL.
   623  		acls, err := client.ListBucketACLs(ctx, bucket)
   624  		if err != nil {
   625  			t.Fatalf("client.ListBucketACLs: %v", err)
   626  		}
   627  		if got, want := len(acls), 2; got != want {
   628  			t.Errorf("ListBucketACLs: got %v, want %v items", acls, want)
   629  		}
   630  		// Delete one BucketACL with AllUsers entity.
   631  		if err := client.DeleteBucketACL(ctx, bucket, AllUsers); err != nil {
   632  			t.Fatalf("client.DeleteBucketACL: %v", err)
   633  		}
   634  		// Assert bucket has one BucketACL.
   635  		acls, err = client.ListBucketACLs(ctx, bucket)
   636  		if err != nil {
   637  			t.Fatalf("client.ListBucketACLs: %v", err)
   638  		}
   639  		if got, want := len(acls), 1; got != want {
   640  			t.Errorf("ListBucketACLs: got %v, want %v items", acls, want)
   641  		}
   642  	})
   643  }
   644  
   645  func TestDefaultObjectACLCRUDEmulated(t *testing.T) {
   646  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   647  		ctx := context.Background()
   648  		attrs := &BucketAttrs{
   649  			Name:                       bucket,
   650  			PredefinedDefaultObjectACL: "publicRead",
   651  		}
   652  		// Create the bucket that will be retrieved.
   653  		if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
   654  			t.Fatalf("client.CreateBucket: %v", err)
   655  		}
   656  		// Assert bucket has 2 DefaultObjectACL entities, including project owner and PredefinedDefaultObjectACL.
   657  		acls, err := client.ListDefaultObjectACLs(ctx, bucket)
   658  		if err != nil {
   659  			t.Fatalf("client.ListDefaultObjectACLs: %v", err)
   660  		}
   661  		if got, want := len(acls), 2; got != want {
   662  			t.Errorf("ListDefaultObjectACLs: got %v, want %v items", acls, want)
   663  		}
   664  		entity := AllAuthenticatedUsers
   665  		role := RoleOwner
   666  		err = client.UpdateDefaultObjectACL(ctx, bucket, entity, role)
   667  		if err != nil {
   668  			t.Fatalf("UpdateDefaultObjectCL: %v", err)
   669  		}
   670  		// Assert there are now 3 DefaultObjectACL entities, including existing DefaultObjectACLs.
   671  		acls, err = client.ListDefaultObjectACLs(ctx, bucket)
   672  		if err != nil {
   673  			t.Fatalf("client.ListDefaultObjectACLs: %v", err)
   674  		}
   675  		if got, want := len(acls), 3; got != want {
   676  			t.Errorf("ListDefaultObjectACLs: %v got %v, want %v items", len(acls), acls, want)
   677  		}
   678  		// Delete 1 DefaultObjectACL with AllUsers entity.
   679  		if err := client.DeleteDefaultObjectACL(ctx, bucket, AllUsers); err != nil {
   680  			t.Fatalf("client.DeleteDefaultObjectACL: %v", err)
   681  		}
   682  		// Assert bucket has 2 DefaultObjectACL entities.
   683  		acls, err = client.ListDefaultObjectACLs(ctx, bucket)
   684  		if err != nil {
   685  			t.Fatalf("client.ListDefaultObjectACLs: %v", err)
   686  		}
   687  		if got, want := len(acls), 2; got != want {
   688  			t.Errorf("ListDefaultObjectACLs: %v got %v, want %v items", len(acls), acls, want)
   689  		}
   690  	})
   691  }
   692  
   693  func TestObjectACLCRUDEmulated(t *testing.T) {
   694  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   695  		// Populate test object.
   696  		ctx := context.Background()
   697  		_, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
   698  			Name: bucket,
   699  		}, nil)
   700  		if err != nil {
   701  			t.Fatalf("CreateBucket: %v", err)
   702  		}
   703  		o := ObjectAttrs{
   704  			Bucket: bucket,
   705  			Name:   fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
   706  		}
   707  		w := veneerClient.Bucket(bucket).Object(o.Name).NewWriter(context.Background())
   708  		if _, err := w.Write(randomBytesToWrite); err != nil {
   709  			t.Fatalf("failed to populate test object: %v", err)
   710  		}
   711  		if err := w.Close(); err != nil {
   712  			t.Fatalf("closing object: %v", err)
   713  		}
   714  		var listAcls []ACLRule
   715  		// Assert there are 4 ObjectACL entities, including object owner and project owners/editors/viewers.
   716  		if listAcls, err = client.ListObjectACLs(ctx, bucket, o.Name); err != nil {
   717  			t.Fatalf("ListObjectACLs: %v", err)
   718  		}
   719  		if got, want := len(listAcls), 4; got != want {
   720  			t.Errorf("ListObjectACLs: got %v, want %v items", listAcls, want)
   721  		}
   722  		entity := AllUsers
   723  		role := RoleReader
   724  		err = client.UpdateObjectACL(ctx, bucket, o.Name, entity, role)
   725  		if err != nil {
   726  			t.Fatalf("UpdateObjectCL: %v", err)
   727  		}
   728  		// Assert there are now 5 ObjectACL entities, including existing ACLs.
   729  		if listAcls, err = client.ListObjectACLs(ctx, bucket, o.Name); err != nil {
   730  			t.Fatalf("ListObjectACLs: %v", err)
   731  		}
   732  		if got, want := len(listAcls), 5; got != want {
   733  			t.Errorf("ListObjectACLs: got %v, want %v items", listAcls, want)
   734  		}
   735  		if err = client.DeleteObjectACL(ctx, bucket, o.Name, AllUsers); err != nil {
   736  			t.Fatalf("client.DeleteObjectACL: %v", err)
   737  		}
   738  		// Assert there are now 4 ObjectACL entities after deletion.
   739  		if listAcls, err = client.ListObjectACLs(ctx, bucket, o.Name); err != nil {
   740  			t.Fatalf("ListObjectACLs: %v", err)
   741  		}
   742  		if got, want := len(listAcls), 4; got != want {
   743  			t.Errorf("ListObjectACLs: got %v, want %v items", listAcls, want)
   744  		}
   745  	})
   746  }
   747  
   748  func TestOpenReaderEmulated(t *testing.T) {
   749  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   750  		// Populate test data.
   751  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   752  			Name: bucket,
   753  		}, nil)
   754  		if err != nil {
   755  			t.Fatalf("client.CreateBucket: %v", err)
   756  		}
   757  		prefix := time.Now().Nanosecond()
   758  		want := &ObjectAttrs{
   759  			Bucket: bucket,
   760  			Name:   fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
   761  		}
   762  		w := veneerClient.Bucket(bucket).Object(want.Name).NewWriter(context.Background())
   763  		if _, err := w.Write(randomBytesToWrite); err != nil {
   764  			t.Fatalf("failed to populate test data: %v", err)
   765  		}
   766  		if err := w.Close(); err != nil {
   767  			t.Fatalf("closing object: %v", err)
   768  		}
   769  
   770  		params := &newRangeReaderParams{
   771  			bucket: bucket,
   772  			object: want.Name,
   773  			gen:    defaultGen,
   774  			offset: 0,
   775  			length: -1,
   776  		}
   777  		r, err := client.NewRangeReader(context.Background(), params)
   778  		if err != nil {
   779  			t.Fatalf("opening reading: %v", err)
   780  		}
   781  		wantLen := len(randomBytesToWrite)
   782  		got := make([]byte, wantLen)
   783  		n, err := r.Read(got)
   784  		if n != wantLen {
   785  			t.Fatalf("expected to read %d bytes, but got %d", wantLen, n)
   786  		}
   787  		if diff := cmp.Diff(got, randomBytesToWrite); diff != "" {
   788  			t.Fatalf("Read: got(-),want(+):\n%s", diff)
   789  		}
   790  	})
   791  }
   792  
   793  func TestOpenWriterEmulated(t *testing.T) {
   794  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   795  		if strings.Contains(project, "grpc") {
   796  			t.Skip("Implementation in testbench pending: https://github.com/googleapis/storage-testbench/issues/568")
   797  		}
   798  
   799  		// Populate test data.
   800  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
   801  			Name: bucket,
   802  		}, nil)
   803  		if err != nil {
   804  			t.Fatalf("client.CreateBucket: %v", err)
   805  		}
   806  		prefix := time.Now().Nanosecond()
   807  		want := &ObjectAttrs{
   808  			Bucket:     bucket,
   809  			Name:       fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
   810  			Generation: defaultGen,
   811  		}
   812  
   813  		var gotAttrs *ObjectAttrs
   814  		params := &openWriterParams{
   815  			attrs:    want,
   816  			bucket:   bucket,
   817  			ctx:      context.Background(),
   818  			donec:    make(chan struct{}),
   819  			setError: func(_ error) {}, // no-op
   820  			progress: func(_ int64) {}, // no-op
   821  			setObj:   func(o *ObjectAttrs) { gotAttrs = o },
   822  		}
   823  		pw, err := client.OpenWriter(params)
   824  		if err != nil {
   825  			t.Fatalf("failed to open writer: %v", err)
   826  		}
   827  		if _, err := pw.Write(randomBytesToWrite); err != nil {
   828  			t.Fatalf("failed to populate test data: %v", err)
   829  		}
   830  		if err := pw.Close(); err != nil {
   831  			t.Fatalf("closing object: %v", err)
   832  		}
   833  		select {
   834  		case <-params.donec:
   835  		}
   836  		if gotAttrs == nil {
   837  			t.Fatalf("Writer finished, but resulting object wasn't set")
   838  		}
   839  		if diff := cmp.Diff(gotAttrs.Name, want.Name); diff != "" {
   840  			t.Fatalf("Resulting object name: got(-),want(+):\n%s", diff)
   841  		}
   842  
   843  		r, err := veneerClient.Bucket(bucket).Object(want.Name).NewReader(context.Background())
   844  		if err != nil {
   845  			t.Fatalf("opening reading: %v", err)
   846  		}
   847  		wantLen := len(randomBytesToWrite)
   848  		got := make([]byte, wantLen)
   849  		n, err := r.Read(got)
   850  		if n != wantLen {
   851  			t.Fatalf("expected to read %d bytes, but got %d", wantLen, n)
   852  		}
   853  		if diff := cmp.Diff(got, randomBytesToWrite); diff != "" {
   854  			t.Fatalf("checking written content: got(-),want(+):\n%s", diff)
   855  		}
   856  	})
   857  }
   858  
   859  func TestListNotificationsEmulated(t *testing.T) {
   860  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   861  		// Populate test object.
   862  		ctx := context.Background()
   863  		_, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
   864  			Name: bucket,
   865  		}, nil)
   866  		if err != nil {
   867  			t.Fatalf("client.CreateBucket: %v", err)
   868  		}
   869  		_, err = client.CreateNotification(ctx, bucket, &Notification{
   870  			TopicProjectID: project,
   871  			TopicID:        "go-storage-notification-test",
   872  			PayloadFormat:  "JSON_API_V1",
   873  		})
   874  		if err != nil {
   875  			t.Fatalf("client.CreateNotification: %v", err)
   876  		}
   877  		n, err := client.ListNotifications(ctx, bucket)
   878  		if err != nil {
   879  			t.Fatalf("client.ListNotifications: %v", err)
   880  		}
   881  		if want, got := 1, len(n); want != got {
   882  			t.Errorf("ListNotifications: got %v, want %v items", n, want)
   883  		}
   884  	})
   885  }
   886  
   887  func TestCreateNotificationEmulated(t *testing.T) {
   888  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   889  		// Populate test object.
   890  		ctx := context.Background()
   891  		_, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
   892  			Name: bucket,
   893  		}, nil)
   894  		if err != nil {
   895  			t.Fatalf("client.CreateBucket: %v", err)
   896  		}
   897  
   898  		want := &Notification{
   899  			TopicProjectID: project,
   900  			TopicID:        "go-storage-notification-test",
   901  			PayloadFormat:  "JSON_API_V1",
   902  		}
   903  		got, err := client.CreateNotification(ctx, bucket, want)
   904  		if err != nil {
   905  			t.Fatalf("client.CreateNotification: %v", err)
   906  		}
   907  		if diff := cmp.Diff(got.TopicID, want.TopicID); diff != "" {
   908  			t.Errorf("CreateNotification topic: got(-),want(+):\n%s", diff)
   909  		}
   910  	})
   911  }
   912  
   913  func TestDeleteNotificationEmulated(t *testing.T) {
   914  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   915  		// Populate test object.
   916  		ctx := context.Background()
   917  		_, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
   918  			Name: bucket,
   919  		}, nil)
   920  		if err != nil {
   921  			t.Fatalf("client.CreateBucket: %v", err)
   922  		}
   923  		var n *Notification
   924  		n, err = client.CreateNotification(ctx, bucket, &Notification{
   925  			TopicProjectID: project,
   926  			TopicID:        "go-storage-notification-test",
   927  			PayloadFormat:  "JSON_API_V1",
   928  		})
   929  		if err != nil {
   930  			t.Fatalf("client.CreateNotification: %v", err)
   931  		}
   932  		err = client.DeleteNotification(ctx, bucket, n.ID)
   933  		if err != nil {
   934  			t.Fatalf("client.DeleteNotification: %v", err)
   935  		}
   936  	})
   937  }
   938  
   939  func initEmulatorClients() func() error {
   940  	noopCloser := func() error { return nil }
   941  	if !isEmulatorEnvironmentSet() {
   942  		return noopCloser
   943  	}
   944  	ctx := context.Background()
   945  
   946  	grpcClient, err := newGRPCStorageClient(ctx)
   947  	if err != nil {
   948  		log.Fatalf("Error setting up gRPC client for emulator tests: %v", err)
   949  		return noopCloser
   950  	}
   951  	httpClient, err := newHTTPStorageClient(ctx)
   952  	if err != nil {
   953  		log.Fatalf("Error setting up HTTP client for emulator tests: %v", err)
   954  		return noopCloser
   955  	}
   956  
   957  	emulatorClients = map[string]storageClient{
   958  		"http": httpClient,
   959  		"grpc": grpcClient,
   960  	}
   961  
   962  	veneerClient, err = NewClient(ctx)
   963  	if err != nil {
   964  		log.Fatalf("Error setting up Veneer client for emulator tests: %v", err)
   965  		return noopCloser
   966  	}
   967  
   968  	return func() error {
   969  		gerr := grpcClient.Close()
   970  		herr := httpClient.Close()
   971  		verr := veneerClient.Close()
   972  
   973  		if gerr != nil {
   974  			return gerr
   975  		} else if herr != nil {
   976  			return herr
   977  		}
   978  		return verr
   979  	}
   980  }
   981  
   982  func TestLockBucketRetentionPolicyEmulated(t *testing.T) {
   983  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
   984  		b := &BucketAttrs{
   985  			Name: bucket,
   986  			RetentionPolicy: &RetentionPolicy{
   987  				RetentionPeriod: time.Minute,
   988  			},
   989  		}
   990  		// Create the bucket that will be locked.
   991  		_, err := client.CreateBucket(context.Background(), project, b.Name, b, nil)
   992  		if err != nil {
   993  			t.Fatalf("client.CreateBucket: %v", err)
   994  		}
   995  		// Lock the bucket's retention policy.
   996  		err = client.LockBucketRetentionPolicy(context.Background(), b.Name, &BucketConditions{MetagenerationMatch: 1})
   997  		if err != nil {
   998  			t.Fatalf("client.LockBucketRetentionPolicy: %v", err)
   999  		}
  1000  		got, err := client.GetBucket(context.Background(), bucket, nil)
  1001  		if err != nil {
  1002  			t.Fatalf("client.GetBucket: %v", err)
  1003  		}
  1004  		if !got.RetentionPolicy.IsLocked {
  1005  			t.Error("Expected bucket retention policy to be locked, but was not.")
  1006  		}
  1007  	})
  1008  }
  1009  
  1010  func TestComposeEmulated(t *testing.T) {
  1011  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
  1012  		ctx := context.Background()
  1013  
  1014  		// Populate test data.
  1015  		_, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
  1016  			Name: bucket,
  1017  		}, nil)
  1018  		if err != nil {
  1019  			t.Fatalf("client.CreateBucket: %v", err)
  1020  		}
  1021  		prefix := time.Now().Nanosecond()
  1022  		srcNames := []string{
  1023  			fmt.Sprintf("%d-object1", prefix),
  1024  			fmt.Sprintf("%d-object2", prefix),
  1025  		}
  1026  
  1027  		for _, n := range srcNames {
  1028  			w := veneerClient.Bucket(bucket).Object(n).NewWriter(ctx)
  1029  			if _, err := w.Write(randomBytesToWrite); err != nil {
  1030  				t.Fatalf("failed to populate test data: %v", err)
  1031  			}
  1032  			if err := w.Close(); err != nil {
  1033  				t.Fatalf("closing object: %v", err)
  1034  			}
  1035  		}
  1036  
  1037  		dstName := fmt.Sprintf("%d-object3", prefix)
  1038  		req := composeObjectRequest{
  1039  			dstBucket: bucket,
  1040  			dstObject: destinationObject{
  1041  				name:  dstName,
  1042  				attrs: &ObjectAttrs{StorageClass: "COLDLINE"},
  1043  			},
  1044  			srcs: []sourceObject{
  1045  				{name: srcNames[0]},
  1046  				{name: srcNames[1]},
  1047  			},
  1048  		}
  1049  		attrs, err := client.ComposeObject(ctx, &req)
  1050  		if err != nil {
  1051  			t.Fatalf("client.ComposeObject(): %v", err)
  1052  		}
  1053  		if got := attrs.Name; got != dstName {
  1054  			t.Errorf("attrs.Name: got %v, want %v", got, dstName)
  1055  		}
  1056  		// Check that the destination object size is equal to the sum of its
  1057  		// sources.
  1058  		if got, want := attrs.Size, 2*len(randomBytesToWrite); got != int64(want) {
  1059  			t.Errorf("attrs.Size: got %v, want %v", got, want)
  1060  		}
  1061  		// Check that destination attrs set via object attrs are preserved.
  1062  		if got, want := attrs.StorageClass, "COLDLINE"; got != want {
  1063  			t.Errorf("attrs.StorageClass: got %v, want %v", got, want)
  1064  		}
  1065  	})
  1066  }
  1067  
  1068  func TestHMACKeyCRUDEmulated(t *testing.T) {
  1069  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
  1070  		ctx := context.Background()
  1071  		serviceAccountEmail := "test@test-project.iam.gserviceaccount.com"
  1072  		want, err := client.CreateHMACKey(ctx, project, serviceAccountEmail)
  1073  		if err != nil {
  1074  			t.Fatalf("CreateHMACKey: %v", err)
  1075  		}
  1076  		if want == nil {
  1077  			t.Fatal("CreateHMACKey: Unexpectedly got back a nil HMAC key")
  1078  		}
  1079  		if want.State != Active {
  1080  			t.Fatalf("CreateHMACKey: Unexpected state %q, expected %q", want.State, Active)
  1081  		}
  1082  		got, err := client.GetHMACKey(ctx, project, want.AccessID)
  1083  		if err != nil {
  1084  			t.Fatalf("GetHMACKey: %v", err)
  1085  		}
  1086  		if diff := cmp.Diff(got.ID, want.ID); diff != "" {
  1087  			t.Errorf("GetHMACKey ID:got(-),want(+):\n%s", diff)
  1088  		}
  1089  		if diff := cmp.Diff(got.UpdatedTime, want.UpdatedTime); diff != "" {
  1090  			t.Errorf("GetHMACKey UpdatedTime: got(-),want(+):\n%s", diff)
  1091  		}
  1092  		attr := &HMACKeyAttrsToUpdate{
  1093  			State: Inactive,
  1094  		}
  1095  		got, err = client.UpdateHMACKey(ctx, project, serviceAccountEmail, want.AccessID, attr)
  1096  		if err != nil {
  1097  			t.Fatalf("UpdateHMACKey: %v", err)
  1098  		}
  1099  		if got.State != attr.State {
  1100  			t.Errorf("UpdateHMACKey State: got %v, want %v", got.State, attr.State)
  1101  		}
  1102  		showDeletedKeys := false
  1103  		it := client.ListHMACKeys(ctx, project, serviceAccountEmail, showDeletedKeys)
  1104  		var count int
  1105  		var e error
  1106  		for ; ; count++ {
  1107  			_, e = it.Next()
  1108  			if e != nil {
  1109  				break
  1110  			}
  1111  		}
  1112  		if e != iterator.Done {
  1113  			t.Fatalf("ListHMACKeys: expected %q but got %q", iterator.Done, err)
  1114  		}
  1115  		if expected := 1; count != expected {
  1116  			t.Errorf("ListHMACKeys: expected to get %d hmacKeys, but got %d", expected, count)
  1117  		}
  1118  		err = client.DeleteHMACKey(ctx, project, want.AccessID)
  1119  		if err != nil {
  1120  			t.Fatalf("DeleteHMACKey: %v", err)
  1121  		}
  1122  		got, err = client.GetHMACKey(ctx, project, want.AccessID)
  1123  		if err == nil {
  1124  			t.Fatalf("GetHMACKey unexcepted error: wanted 404")
  1125  		}
  1126  	})
  1127  }
  1128  
  1129  func TestBucketConditionsEmulated(t *testing.T) {
  1130  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
  1131  		ctx := context.Background()
  1132  		cases := []struct {
  1133  			name string
  1134  			call func(bucket string, metaGen int64) error
  1135  		}{
  1136  			{
  1137  				name: "get",
  1138  				call: func(bucket string, metaGen int64) error {
  1139  					_, err := client.GetBucket(ctx, bucket, &BucketConditions{MetagenerationMatch: metaGen})
  1140  					return err
  1141  				},
  1142  			},
  1143  			{
  1144  				name: "update",
  1145  				call: func(bucket string, metaGen int64) error {
  1146  					_, err := client.UpdateBucket(ctx, bucket, &BucketAttrsToUpdate{StorageClass: "ARCHIVE"}, &BucketConditions{MetagenerationMatch: metaGen})
  1147  					return err
  1148  				},
  1149  			},
  1150  			{
  1151  				name: "delete",
  1152  				call: func(bucket string, metaGen int64) error {
  1153  					return client.DeleteBucket(ctx, bucket, &BucketConditions{MetagenerationMatch: metaGen})
  1154  				},
  1155  			},
  1156  			{
  1157  				name: "lockRetentionPolicy",
  1158  				call: func(bucket string, metaGen int64) error {
  1159  					return client.LockBucketRetentionPolicy(ctx, bucket, &BucketConditions{MetagenerationMatch: metaGen})
  1160  				},
  1161  			},
  1162  		}
  1163  		for _, c := range cases {
  1164  			t.Run(c.name, func(r *testing.T) {
  1165  				bucket, metaGen, err := createBucket(ctx, project)
  1166  				if err != nil {
  1167  					r.Fatalf("creating bucket: %v", err)
  1168  				}
  1169  				if err := c.call(bucket, metaGen); err != nil {
  1170  					r.Errorf("error: %v", err)
  1171  				}
  1172  			})
  1173  		}
  1174  	})
  1175  }
  1176  
  1177  func TestObjectConditionsEmulated(t *testing.T) {
  1178  	transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
  1179  		ctx := context.Background()
  1180  
  1181  		// Create test bucket
  1182  		if _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{Name: bucket}, nil); err != nil {
  1183  			t.Fatalf("client.CreateBucket: %v", err)
  1184  		}
  1185  
  1186  		cases := []struct {
  1187  			name string
  1188  			call func() error
  1189  		}{
  1190  			{
  1191  				name: "update generation",
  1192  				call: func() error {
  1193  					objName, gen, _, err := createObject(ctx, bucket)
  1194  					if err != nil {
  1195  						return fmt.Errorf("creating object: %w", err)
  1196  					}
  1197  					uattrs := &ObjectAttrsToUpdate{CustomTime: time.Now()}
  1198  					_, err = client.UpdateObject(ctx, &updateObjectParams{bucket: bucket, object: objName, uattrs: uattrs, gen: gen})
  1199  					return err
  1200  				},
  1201  			},
  1202  			{
  1203  				name: "update ifMetagenerationMatch",
  1204  				call: func() error {
  1205  					objName, gen, metaGen, err := createObject(ctx, bucket)
  1206  					if err != nil {
  1207  						return fmt.Errorf("creating object: %w", err)
  1208  					}
  1209  					uattrs := &ObjectAttrsToUpdate{CustomTime: time.Now()}
  1210  					conds := &Conditions{
  1211  						GenerationMatch:     gen,
  1212  						MetagenerationMatch: metaGen,
  1213  					}
  1214  					_, err = client.UpdateObject(ctx, &updateObjectParams{bucket: bucket, object: objName, uattrs: uattrs, gen: gen, conds: conds})
  1215  					return err
  1216  				},
  1217  			},
  1218  			{
  1219  				name: "write ifGenerationMatch",
  1220  				call: func() error {
  1221  					var err error
  1222  					_, err = client.OpenWriter(&openWriterParams{
  1223  						ctx:                ctx,
  1224  						chunkSize:          256 * 1024,
  1225  						chunkRetryDeadline: 0,
  1226  						bucket:             bucket,
  1227  						attrs:              &ObjectAttrs{},
  1228  						conds:              &Conditions{DoesNotExist: true},
  1229  						encryptionKey:      nil,
  1230  						sendCRC32C:         false,
  1231  						donec:              nil,
  1232  						setError: func(e error) {
  1233  							if e != nil {
  1234  								err = e
  1235  							}
  1236  						},
  1237  						progress: nil,
  1238  						setObj:   nil,
  1239  					})
  1240  					return err
  1241  				},
  1242  			},
  1243  			{
  1244  				name: "rewrite ifMetagenerationMatch",
  1245  				call: func() error {
  1246  					objName, gen, metaGen, err := createObject(ctx, bucket)
  1247  					if err != nil {
  1248  						return fmt.Errorf("creating object: %w", err)
  1249  					}
  1250  					_, err = client.RewriteObject(ctx, &rewriteObjectRequest{
  1251  						srcObject: sourceObject{
  1252  							name:   objName,
  1253  							bucket: bucket,
  1254  							gen:    gen,
  1255  							conds: &Conditions{
  1256  								GenerationMatch:     gen,
  1257  								MetagenerationMatch: metaGen,
  1258  							},
  1259  						},
  1260  						dstObject: destinationObject{
  1261  							name:   fmt.Sprintf("%d-object", time.Now().Nanosecond()),
  1262  							bucket: bucket,
  1263  							conds: &Conditions{
  1264  								DoesNotExist: true,
  1265  							},
  1266  							attrs: &ObjectAttrs{},
  1267  						},
  1268  					})
  1269  					return err
  1270  				},
  1271  			},
  1272  			{
  1273  				name: "compose ifGenerationMatch",
  1274  				call: func() error {
  1275  					obj1, obj1Gen, _, err := createObject(ctx, bucket)
  1276  					if err != nil {
  1277  						return fmt.Errorf("creating object: %w", err)
  1278  					}
  1279  					obj2, obj2Gen, _, err := createObject(ctx, bucket)
  1280  					if err != nil {
  1281  						return fmt.Errorf("creating object: %w", err)
  1282  					}
  1283  					_, err = client.ComposeObject(ctx, &composeObjectRequest{
  1284  						dstBucket: bucket,
  1285  						dstObject: destinationObject{
  1286  							name:   fmt.Sprintf("%d-object", time.Now().Nanosecond()),
  1287  							bucket: bucket,
  1288  							conds:  &Conditions{DoesNotExist: true},
  1289  							attrs:  &ObjectAttrs{},
  1290  						},
  1291  						srcs: []sourceObject{
  1292  							{
  1293  								name:   obj1,
  1294  								bucket: bucket,
  1295  								gen:    obj1Gen,
  1296  								conds: &Conditions{
  1297  									GenerationMatch: obj1Gen,
  1298  								},
  1299  							},
  1300  							{
  1301  								name:   obj2,
  1302  								bucket: bucket,
  1303  								conds: &Conditions{
  1304  									GenerationMatch: obj2Gen,
  1305  								},
  1306  							},
  1307  						},
  1308  					})
  1309  					return err
  1310  				},
  1311  			},
  1312  			{
  1313  				name: "delete ifGenerationMatch",
  1314  				call: func() error {
  1315  					objName, gen, _, err := createObject(ctx, bucket)
  1316  					if err != nil {
  1317  						return fmt.Errorf("creating object: %w", err)
  1318  					}
  1319  					err = client.DeleteObject(ctx, bucket, objName, gen, &Conditions{GenerationMatch: gen})
  1320  					return err
  1321  				},
  1322  			},
  1323  			{
  1324  				name: "get ifMetagenerationMatch",
  1325  				call: func() error {
  1326  					objName, gen, metaGen, err := createObject(ctx, bucket)
  1327  					if err != nil {
  1328  						return fmt.Errorf("creating object: %w", err)
  1329  					}
  1330  					_, err = client.GetObject(ctx, bucket, objName, gen, nil, &Conditions{GenerationMatch: gen, MetagenerationMatch: metaGen})
  1331  					return err
  1332  				},
  1333  			},
  1334  		}
  1335  		for _, c := range cases {
  1336  			t.Run(c.name, func(r *testing.T) {
  1337  				if err := c.call(); err != nil {
  1338  					r.Errorf("error: %v", err)
  1339  				}
  1340  			})
  1341  		}
  1342  	})
  1343  }
  1344  
  1345  // createObject creates an object in the emulator and returns its name, generation, and
  1346  // metageneration.
  1347  func createObject(ctx context.Context, bucket string) (string, int64, int64, error) {
  1348  	prefix := time.Now().Nanosecond()
  1349  	objName := fmt.Sprintf("%d-object", prefix)
  1350  
  1351  	w := veneerClient.Bucket(bucket).Object(objName).NewWriter(ctx)
  1352  	if _, err := w.Write(randomBytesToWrite); err != nil {
  1353  		return "", 0, 0, fmt.Errorf("failed to populate test data: %w", err)
  1354  	}
  1355  	if err := w.Close(); err != nil {
  1356  		return "", 0, 0, fmt.Errorf("closing object: %w", err)
  1357  	}
  1358  	attrs, err := veneerClient.Bucket(bucket).Object(objName).Attrs(ctx)
  1359  	if err != nil {
  1360  		return "", 0, 0, fmt.Errorf("get object: %w", err)
  1361  	}
  1362  	return objName, attrs.Generation, attrs.Metageneration, nil
  1363  }
  1364  
  1365  // createBucket creates a new bucket in the emulator and returns its name and
  1366  // metageneration.
  1367  func createBucket(ctx context.Context, projectID string) (string, int64, error) {
  1368  	prefix := time.Now().Nanosecond()
  1369  	bucket := fmt.Sprintf("%d-bucket", prefix)
  1370  
  1371  	if err := veneerClient.Bucket(bucket).Create(ctx, projectID, nil); err != nil {
  1372  		return "", 0, fmt.Errorf("Bucket.Create: %w", err)
  1373  	}
  1374  	attrs, err := veneerClient.Bucket(bucket).Attrs(ctx)
  1375  	if err != nil {
  1376  		return "", 0, fmt.Errorf("Bucket.Attrs: %w", err)
  1377  	}
  1378  	return bucket, attrs.MetaGeneration, nil
  1379  }
  1380  
  1381  // transportClienttest executes the given function with a sub-test, a project name
  1382  // based on the transport, a unique bucket name also based on the transport, and
  1383  // the transport-specific client to run the test with. It also checks the environment
  1384  // to ensure it is suitable for emulator-based tests, or skips.
  1385  func transportClientTest(t *testing.T, test func(*testing.T, string, string, storageClient)) {
  1386  	checkEmulatorEnvironment(t)
  1387  
  1388  	for transport, client := range emulatorClients {
  1389  		t.Run(transport, func(t *testing.T) {
  1390  			project := fmt.Sprintf("%s-project", transport)
  1391  			bucket := fmt.Sprintf("%s-bucket-%d", transport, time.Now().Nanosecond())
  1392  			test(t, project, bucket, client)
  1393  		})
  1394  	}
  1395  }
  1396  
  1397  // checkEmulatorEnvironment skips the test if the emulator environment variables
  1398  // are not set.
  1399  func checkEmulatorEnvironment(t *testing.T) {
  1400  	if !isEmulatorEnvironmentSet() {
  1401  		t.Skip("Emulator tests skipped without emulator environment variables set")
  1402  	}
  1403  }
  1404  
  1405  // isEmulatorEnvironmentSet checks if the emulator environment variables are set.
  1406  func isEmulatorEnvironmentSet() bool {
  1407  	return os.Getenv("STORAGE_EMULATOR_HOST_GRPC") != "" && os.Getenv("STORAGE_EMULATOR_HOST") != ""
  1408  }