Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions storage/buckets/buckets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,29 @@ func TestListBuckets(t *testing.T) {
})
}

func TestListBucketsPartialSuccess(t *testing.T) {
tc := testutil.SystemTest(t)
// This test will run the listBucketsPartialSuccess function against the live service.
// However, it's highly unlikely to encounter any "unreachable" buckets
// in a normal test environment. Thus, this test mainly verifies that the call
// doesn't fail, but not the core functionality of reporting unreachable buckets.

var buf bytes.Buffer
if err := listBucketsPartialSuccess(&buf, tc.ProjectID); err != nil {
t.Fatalf("listBucketsPartialSuccess failed: %v", err)
}

got := buf.String()
if !strings.Contains(got, "Reachable buckets:") {
t.Errorf("Output missing 'Reachable buckets:' section, got:\n%s", got)
}

// In a live test, we expect no unreachable buckets.
if !strings.Contains(got, "No unreachable buckets.") {
t.Errorf("Output missing 'No unreachable buckets.' section when no buckets were unreachable, got:\n%s", got)
}
}

func TestGetBucketMetadata(t *testing.T) {
tc := testutil.SystemTest(t)
ctx := context.Background()
Expand Down
78 changes: 78 additions & 0 deletions storage/buckets/list_buckets_partial_success.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package buckets

// [START storage_list_buckets_partial_success]
import (
"context"
"fmt"
"io"
"time"

"cloud.google.com/go/storage"
"google.golang.org/api/iterator"
)

// listBucketsPartialSuccess lists buckets in the project. If ReturnPartialSuccess
// is true, the iterator will return reachable buckets and a list of
// unreachable bucket resource names.
func listBucketsPartialSuccess(w io.Writer, projectID string) error {
// projectID := "my-project-id"
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
return fmt.Errorf("storage.NewClient: %w", err)
}
defer client.Close()

ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()

it := client.Buckets(ctx, projectID)
// Enable returning unreachable buckets.
it.ReturnPartialSuccess = true

fmt.Fprintln(w, "Reachable buckets:")
for {
battrs, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
// Errors here usually indicate a problem with the overall list operation
// or connection, such as a network issue, rather than individual
// buckets being unreachable. Unreachable buckets due to issues like
// regional outages or permission issues are typically reported via the
// Unreachable() method below.
return err
}
fmt.Fprintf(w, "- %v\n", battrs.Name)
}

// Retrieve the list of buckets that were unreachable.
unreachable := it.Unreachable()
if len(unreachable) > 0 {
fmt.Fprintln(w, "\nUnreachable buckets:")
for _, r := range unreachable {
fmt.Fprintf(w, "- %v\n", r)
}
} else {
fmt.Fprintln(w, "\nNo unreachable buckets.")
}

return nil
}

// [END storage_list_buckets_partial_success]
143 changes: 143 additions & 0 deletions storage/transfer_manager/download_chunks_concurrently_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,104 @@
import (
"bytes"
"context"
<<<<<<< HEAD

Check failure on line 20 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

missing import path
<<<<<<< HEAD

Check failure on line 21 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

missing import path
=======

Check failure on line 22 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

missing import path
>>>>>>> d9e9ef31 (Improve comments and tests for download_chunks_concurrently)
"crypto/rand"

Check failure on line 24 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

expected declaration, found "crypto/rand"
"fmt"
"io"
"log"
<<<<<<< HEAD
=======
"fmt"
>>>>>>> df0f473c (Add sample download_chunks_concurrently with test.)
=======
>>>>>>> d9e9ef31 (Improve comments and tests for download_chunks_concurrently)
"os"
"strings"
"testing"

"cloud.google.com/go/storage"
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
"github.com/google/uuid"
)

const (
testPrefix = "storage-objects-test"
downloadObject = "tm-obj-download"
)

var (
tmBucketName string
storageClient *storage.Client
downloadData []byte
)

func TestMain(m *testing.M) {
ctx := context.Background()
tc, _ := testutil.ContextMain(m)

var err error

// Create fixture client & bucket to use across tests.
storageClient, err = storage.NewClient(ctx)
if err != nil {
log.Fatalf("storage.NewClient: %v", err)
}
tmBucketName = fmt.Sprintf("%s-%s", testPrefix, uuid.New().String())
bucket := storageClient.Bucket(tmBucketName)
if err := bucket.Create(ctx, tc.ProjectID, nil); err != nil {
log.Fatalf("Bucket(%q).Create: %v", tmBucketName, err)
}

// Create object fixture for download tests.
w := bucket.Object(downloadObject).NewWriter(ctx)
downloadData = make([]byte, 2*1024*1024) // 2 MiB
if _, err := rand.Read(downloadData); err != nil {
log.Fatalf("rand.Read: %v", err)
}
if _, err := io.Copy(w, bytes.NewReader(downloadData)); err != nil {
log.Fatalf("uploading object: %v", err)
}
if err := w.Close(); err != nil {
log.Fatalf("closing writer: %v", err)
}

// Run tests.
exitCode := m.Run()

// Cleanup bucket and objects.
if err := testutil.DeleteBucketIfExists(ctx, storageClient, tmBucketName); err != nil {
log.Printf("deleting bucket: %v", err)
}
os.Exit(exitCode)
}

func TestDownloadChunksConcurrently(t *testing.T) {
bucketName := tmBucketName
blobName := downloadObject

// Create a temporary file to download to, ensuring we have permissions
// and the file is cleaned up.
f, err := os.CreateTemp("", "tm-file-test-")
if err != nil {
t.Fatalf("os.CreateTemp: %v", err)
}
fileName := f.Name()
f.Close() // Close the file so the download can write to it.
defer os.Remove(fileName)
=======

Check failure on line 110 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

expected statement, found '=='
"google.golang.org/api/iterator"
=======
>>>>>>> 2ed49f9f (Improve test and comments.)

Check failure on line 113 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

exponent has no digits
=======
"github.com/google/uuid"
>>>>>>> d9e9ef31 (Improve comments and tests for download_chunks_concurrently)

Check failure on line 116 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

expected '{', found ')'
)

Check failure on line 117 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

expected statement, found ')'

const (
testPrefix = "storage-objects-test"
Expand All @@ -41,7 +127,7 @@
downloadData []byte
)

func TestMain(m *testing.M) {

Check failure on line 130 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

expected '(', found TestMain
ctx := context.Background()
tc, _ := testutil.ContextMain(m)

Expand Down Expand Up @@ -70,6 +156,7 @@
if err := w.Close(); err != nil {
log.Fatalf("closing writer: %v", err)
}
>>>>>>> df0f473c (Add sample download_chunks_concurrently with test.)

Check failure on line 159 in storage/transfer_manager/download_chunks_concurrently_test.go

View workflow job for this annotation

GitHub Actions / Lint

expected statement, found '>>'

// Run tests.
exitCode := m.Run()
Expand Down Expand Up @@ -99,10 +186,32 @@
if err := downloadChunksConcurrently(&buf, bucketName, blobName, fileName); err != nil {
t.Errorf("downloadChunksConcurrently: %v", err)
}
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
=======
defer os.Remove(fileName)
>>>>>>> df0f473c (Add sample download_chunks_concurrently with test.)
=======
// Clean up the file at the end of the test.
defer func() {
if err := os.Remove(fileName); err != nil {
t.Logf("os.Remove: %v", err)
}
}()
>>>>>>> e5956298 (Improve cleanup in download_chunks_concurrently_test.go)
=======
>>>>>>> d9e9ef31 (Improve comments and tests for download_chunks_concurrently)

if got, want := buf.String(), fmt.Sprintf("Downloaded %v to %v", blobName, fileName); !strings.Contains(got, want) {
t.Errorf("got %q, want to contain %q", got, want)
}
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> d9e9ef31 (Improve comments and tests for download_chunks_concurrently)

// Verify that the downloaded data is the same as the uploaded data.
downloadedBytes, err := os.ReadFile(fileName)
Expand All @@ -112,5 +221,39 @@

if !bytes.Equal(downloadedBytes, downloadData) {
t.Errorf("downloaded data does not match uploaded data. got %d bytes, want %d bytes", len(downloadedBytes), len(downloadData))
<<<<<<< HEAD
=======
}

func deleteBucket(ctx context.Context, t *testing.T, bucket *storage.BucketHandle) {
t.Helper()
it := bucket.Objects(ctx, nil)
for {
attrs, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
t.Logf("Bucket(%v).Objects: %v", bucket, err)
break
}
if err := bucket.Object(attrs.Name).Delete(ctx); err != nil {
t.Logf("Bucket(%v).Object(%q).Delete: %v", bucket, attrs.Name, err)
}
}
if err := bucket.Delete(ctx); err != nil {
t.Logf("Bucket(%v).Delete: %v", bucket, err)
>>>>>>> df0f473c (Add sample download_chunks_concurrently with test.)
=======

// Clean up.
if err := testutil.DeleteBucketIfExists(ctx, client, bucketName); err != nil {
t.Fatalf("testutil.DeleteBucketIfExists: %v", err)
>>>>>>> 2ed49f9f (Improve test and comments.)
}
=======
>>>>>>> e5956298 (Improve cleanup in download_chunks_concurrently_test.go)
=======
}
>>>>>>> d9e9ef31 (Improve comments and tests for download_chunks_concurrently)
}
Loading