package provider import ( "errors" "fmt" "testing" ) // sdkError simulates an underlying provider SDK error type, so tests can // assert errors.As reaches through the wrapper to it. type sdkError struct{ code int } func (e *sdkError) Error() string { return fmt.Sprintf("sdk error %d", e.code) } func TestError_IsAndAs(t *testing.T) { t.Parallel() underlying := &sdkError{code: 429} err := Wrap(ErrQuotaExceeded, "create", "gcp-eu", "zones/z/instances/x", underlying) if !errors.Is(err, ErrQuotaExceeded) { t.Error("errors.Is(err, ErrQuotaExceeded) = false, want true") } if errors.Is(err, ErrNotFound) { t.Error("errors.Is(err, ErrNotFound) = true, want false") } var sdk *sdkError if !errors.As(err, &sdk) { t.Fatal("errors.As(err, &sdk) = false, want true") } if sdk.code != 429 { t.Errorf("recovered sdkError.code = %d, want 429", sdk.code) } } func TestError_WrapWithNilUnderlying(t *testing.T) { t.Parallel() err := Wrap(ErrPermanent, "get", "mock", "id", nil) if !errors.Is(err, ErrPermanent) { t.Error("errors.Is(err, ErrPermanent) = false, want true") } if err.Error() == "" { t.Error("Error() returned an empty string") } } func TestClass(t *testing.T) { t.Parallel() tests := []struct { name string err error want error }{ {"nil returns nil", nil, nil}, {"wrapped not found", Wrap(ErrNotFound, "get", "mock", "id", nil), ErrNotFound}, {"wrapped quota", Wrap(ErrQuotaExceeded, "create", "gcp", "id", nil), ErrQuotaExceeded}, {"wrapped permanent", Wrap(ErrPermanent, "create", "gcp", "id", nil), ErrPermanent}, {"wrapped transient", Wrap(ErrTransient, "create", "gcp", "id", nil), ErrTransient}, {"unclassified defaults to transient", errors.New("boom"), ErrTransient}, {"further-wrapped preserves classification", fmt.Errorf("outer: %w", Wrap(ErrQuotaExceeded, "create", "gcp", "id", nil)), ErrQuotaExceeded}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { t.Parallel() if got := Class(tc.err); got != tc.want { t.Errorf("Class(%v) = %v, want %v", tc.err, got, tc.want) } }) } }