//go:build go1.18
// +build go1.18
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package exported
import (
"errors"
"io"
"net/http"
"net/url"
"strings"
"testing"
)
func TestNewResponseErrorNoBodyNoErrorCode(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: http.NoBody,
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if re.ErrorCode != "" {
t.Fatal("expected empty error code")
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE UNAVAILABLE
--------------------------------------------------------------------------------
Response contained no body
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorNoBody(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
respHeader := http.Header{}
const errorCode = "ErrorTooManyCheats"
respHeader.Set("x-ms-error-code", errorCode)
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: http.NoBody,
Header: respHeader,
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if ec := re.ErrorCode; ec != errorCode {
t.Fatalf("unexpected error code %s", ec)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE: ErrorTooManyCheats
--------------------------------------------------------------------------------
Response contained no body
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorNoErrorCode(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: io.NopCloser(strings.NewReader(`{ "code": "ErrorItsBroken", "message": "it's not working" }`)),
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE: ErrorItsBroken
--------------------------------------------------------------------------------
{
"code": "ErrorItsBroken",
"message": "it's not working"
}
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorPreferErrorCodeHeader(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
respHeader := http.Header{}
respHeader.Set("x-ms-error-code", "ErrorTooManyCheats")
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: io.NopCloser(strings.NewReader(`{ "code": "ErrorItsBroken", "message": "it's not working" }`)),
Header: respHeader,
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE: ErrorTooManyCheats
--------------------------------------------------------------------------------
{
"code": "ErrorItsBroken",
"message": "it's not working"
}
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorNoErrorCodeWrappedError(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: io.NopCloser(strings.NewReader(`{ "error": { "code": "ErrorItsBroken", "message": "it's not working" } }`)),
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE: ErrorItsBroken
--------------------------------------------------------------------------------
{
"error": {
"code": "ErrorItsBroken",
"message": "it's not working"
}
}
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorNoErrorCodeInvalidBody(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: io.NopCloser(strings.NewReader("JSON error string")),
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE UNAVAILABLE
--------------------------------------------------------------------------------
JSON error string
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
type readFailer struct{}
func (r *readFailer) Close() error {
return nil
}
func (r *readFailer) Read(p []byte) (int, error) {
return 0, errors.New("mock read failure")
}
func TestNewResponseErrorNoErrorCodeCantReadBody(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: &readFailer{},
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
_, ok := err.(*ResponseError)
if ok {
t.Fatalf("unexpected error type %T", err)
}
const want = `mock read failure`
if got := err.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorNoErrorCodeXML(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Body: io.NopCloser(strings.NewReader(`ContainerAlreadyExists
The specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z`)),
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE: ContainerAlreadyExists
--------------------------------------------------------------------------------
ContainerAlreadyExists
The specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorErrorCodeHeaderXML(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
respHeader := http.Header{}
respHeader.Set("x-ms-error-code", "ContainerAlreadyExists")
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Header: respHeader,
Body: io.NopCloser(strings.NewReader(`ContainerAlreadyExists
The specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z`)),
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE: ContainerAlreadyExists
--------------------------------------------------------------------------------
ContainerAlreadyExists
The specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorErrorCodeHeaderXMLWithNamespace(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
respHeader := http.Header{}
respHeader.Set("x-ms-error-code", "ContainerAlreadyExists")
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Header: respHeader,
Body: io.NopCloser(strings.NewReader(`ContainerAlreadyExistsThe specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z`)),
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE: ContainerAlreadyExists
--------------------------------------------------------------------------------
ContainerAlreadyExistsThe specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestNewResponseErrorAllMissingXML(t *testing.T) {
fakeURL, err := url.Parse("https://fakeurl.com/the/path?qp=removed")
if err != nil {
t.Fatal(err)
}
respHeader := http.Header{}
err = NewResponseError(&http.Response{
Status: "the system is down",
StatusCode: http.StatusInternalServerError,
Header: respHeader,
Body: io.NopCloser(strings.NewReader(`The specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z`)),
Request: &http.Request{
Method: http.MethodGet,
URL: fakeURL,
},
})
re, ok := err.(*ResponseError)
if !ok {
t.Fatalf("unexpected error type %T", err)
}
if c := re.StatusCode; c != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", c)
}
const want = `GET https://fakeurl.com/the/path
--------------------------------------------------------------------------------
RESPONSE 500: the system is down
ERROR CODE UNAVAILABLE
--------------------------------------------------------------------------------
The specified container already exists.\nRequestId:73b2473b-c1c8-4162-97bb-dc171bff61c9\nTime:2021-12-13T19:45:40.679Z
--------------------------------------------------------------------------------
`
if got := re.Error(); got != want {
t.Fatalf("\ngot:\n%s\nwant:\n%s\n", got, want)
}
}
func TestExtractErrorCodeFromJSON(t *testing.T) {
errorBody := []byte(`{"odata.error": {
"code": "ResourceNotFound",
"message": {
"lang": "en-us",
"value": "The specified resource does not exist.\nRequestID:b2437f3b-ca2d-47a1-95a7-92f73a768a1c\n"
}
}
}`)
code := extractErrorCodeJSON(errorBody)
if code != "ResourceNotFound" {
t.Fatalf("expected %s got %s", "ResourceNotFound", code)
}
errorBody = []byte(`{"error": {
"code": "ResourceNotFound",
"message": {
"lang": "en-us",
"value": "The specified resource does not exist.\nRequestID:b2437f3b-ca2d-47a1-95a7-92f73a768a1c\n"
}
}
}`)
code = extractErrorCodeJSON(errorBody)
if code != "ResourceNotFound" {
t.Fatalf("expected %s got %s", "ResourceNotFound", code)
}
}