Files
wehub-resource-sync e04ed9c211
CF: Deploy Dev Docs / deploy (push) Waiting to run
Sync Labels / build (push) Waiting to run
tests / unit tests (macos-latest) (push) Waiting to run
tests / unit tests (ubuntu-latest) (push) Waiting to run
tests / unit tests (windows-latest) (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:32:45 +08:00

99 lines
4.6 KiB
Go

// Copyright 2026 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
//
// http://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 cloudstoragecommon_test
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"testing"
"cloud.google.com/go/storage"
"github.com/googleapis/mcp-toolbox/internal/tools/cloudstorage/cloudstoragecommon"
"github.com/googleapis/mcp-toolbox/internal/util"
"google.golang.org/api/googleapi"
)
func gcpErr(code int) error {
return &googleapi.Error{Code: code, Message: fmt.Sprintf("synthetic %d", code)}
}
func TestProcessGCSError_NilPassthrough(t *testing.T) {
if got := cloudstoragecommon.ProcessGCSError(nil); got != nil {
t.Fatalf("expected nil, got %v", got)
}
}
func TestProcessGCSError_Categorization(t *testing.T) {
tcs := []struct {
desc string
in error
category util.ErrorCategory
// code is checked only for server errors.
code int
}{
{desc: "bucket not exist sentinel", in: storage.ErrBucketNotExist, category: util.CategoryAgent},
{desc: "object not exist sentinel", in: storage.ErrObjectNotExist, category: util.CategoryAgent},
{desc: "wrapped object not exist", in: fmt.Errorf("wrapped: %w", storage.ErrObjectNotExist), category: util.CategoryAgent},
{desc: "read size limit exceeded", in: fmt.Errorf("big: %w", cloudstoragecommon.ErrReadSizeLimitExceeded), category: util.CategoryAgent},
{desc: "binary (non-UTF-8) content", in: fmt.Errorf("obj: %w", cloudstoragecommon.ErrBinaryContent), category: util.CategoryAgent},
{desc: "download destination exists", in: fmt.Errorf("dest: %w", cloudstoragecommon.ErrDestinationExists), category: util.CategoryAgent},
{desc: "local path not exist", in: fmt.Errorf("src: %w", os.ErrNotExist), category: util.CategoryAgent},
{desc: "400 bad request", in: gcpErr(http.StatusBadRequest), category: util.CategoryAgent},
{desc: "401 unauthorized", in: gcpErr(http.StatusUnauthorized), category: util.CategoryServer, code: http.StatusUnauthorized},
{desc: "403 forbidden", in: gcpErr(http.StatusForbidden), category: util.CategoryServer, code: http.StatusForbidden},
{desc: "404 not found", in: gcpErr(http.StatusNotFound), category: util.CategoryAgent},
{desc: "409 conflict", in: gcpErr(http.StatusConflict), category: util.CategoryAgent},
{desc: "412 precondition failed", in: gcpErr(http.StatusPreconditionFailed), category: util.CategoryAgent},
{desc: "416 range not satisfiable", in: gcpErr(http.StatusRequestedRangeNotSatisfiable), category: util.CategoryAgent},
{desc: "429 rate limited", in: gcpErr(http.StatusTooManyRequests), category: util.CategoryServer, code: http.StatusTooManyRequests},
{desc: "500 internal", in: gcpErr(http.StatusInternalServerError), category: util.CategoryServer, code: http.StatusBadGateway},
{desc: "503 unavailable", in: gcpErr(http.StatusServiceUnavailable), category: util.CategoryServer, code: http.StatusBadGateway},
{desc: "418 teapot (unmapped 4xx)", in: gcpErr(http.StatusTeapot), category: util.CategoryServer, code: http.StatusInternalServerError},
{desc: "context cancelled", in: context.Canceled, category: util.CategoryServer, code: http.StatusGatewayTimeout},
{desc: "context deadline exceeded", in: context.DeadlineExceeded, category: util.CategoryServer, code: http.StatusGatewayTimeout},
{desc: "unrecognized error", in: errors.New("kaboom"), category: util.CategoryServer, code: http.StatusInternalServerError},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := cloudstoragecommon.ProcessGCSError(tc.in)
if got == nil {
t.Fatalf("expected non-nil ToolboxError, got nil")
}
if got.Category() != tc.category {
t.Errorf("category = %v, want %v", got.Category(), tc.category)
}
if tc.category == util.CategoryServer {
cse, ok := got.(*util.ClientServerError)
if !ok {
t.Fatalf("expected *ClientServerError, got %T", got)
}
if cse.Code != tc.code {
t.Errorf("code = %d, want %d", cse.Code, tc.code)
}
} else {
if _, ok := got.(*util.AgentError); !ok {
t.Fatalf("expected *AgentError, got %T", got)
}
}
if !errors.Is(got, tc.in) {
t.Errorf("errors.Is chain broken: got %v not wrapping original %v", got, tc.in)
}
})
}
}