...

Source file src/github.com/go-openapi/errors/api_test.go

Documentation: github.com/go-openapi/errors

     1  // Copyright 2015 go-swagger maintainers
     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 errors
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"net/http"
    21  	"net/http/httptest"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  )
    28  
    29  type customError struct {
    30  	apiError
    31  }
    32  
    33  func TestServeError(t *testing.T) {
    34  	// method not allowed wins
    35  	// err abides by the Error interface
    36  	err := MethodNotAllowed("GET", []string{"POST", "PUT"})
    37  	recorder := httptest.NewRecorder()
    38  	ServeError(recorder, nil, err)
    39  	assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code)
    40  	assert.Equal(t, "POST,PUT", recorder.Header().Get("Allow"))
    41  	// assert.Equal(t, "application/json", recorder.Header().Get("content-type"))
    42  	assert.Equal(t, `{"code":405,"message":"method GET is not allowed, but [POST,PUT] are"}`, recorder.Body.String())
    43  
    44  	// renders status code from error when present
    45  	err = NotFound("")
    46  	recorder = httptest.NewRecorder()
    47  	ServeError(recorder, nil, err)
    48  	assert.Equal(t, http.StatusNotFound, recorder.Code)
    49  	// assert.Equal(t, "application/json", recorder.Header().Get("content-type"))
    50  	assert.Equal(t, `{"code":404,"message":"Not found"}`, recorder.Body.String())
    51  
    52  	// renders mapped status code from error when present
    53  	err = InvalidTypeName("someType")
    54  	recorder = httptest.NewRecorder()
    55  	ServeError(recorder, nil, err)
    56  	assert.Equal(t, http.StatusUnprocessableEntity, recorder.Code)
    57  	// assert.Equal(t, "application/json", recorder.Header().Get("content-type"))
    58  	assert.Equal(t, `{"code":601,"message":"someType is an invalid type name"}`, recorder.Body.String())
    59  
    60  	// same, but override DefaultHTTPCode
    61  	func() {
    62  		oldDefaultHTTPCode := DefaultHTTPCode
    63  		defer func() { DefaultHTTPCode = oldDefaultHTTPCode }()
    64  		DefaultHTTPCode = http.StatusBadRequest
    65  
    66  		err = InvalidTypeName("someType")
    67  		recorder = httptest.NewRecorder()
    68  		ServeError(recorder, nil, err)
    69  		assert.Equal(t, http.StatusBadRequest, recorder.Code)
    70  		// assert.Equal(t, "application/json", recorder.Header().Get("content-type"))
    71  		assert.Equal(t, `{"code":601,"message":"someType is an invalid type name"}`, recorder.Body.String())
    72  	}()
    73  
    74  	// defaults to internal server error
    75  	simpleErr := errors.New("some error")
    76  	recorder = httptest.NewRecorder()
    77  	ServeError(recorder, nil, simpleErr)
    78  	assert.Equal(t, http.StatusInternalServerError, recorder.Code)
    79  	// assert.Equal(t, "application/json", recorder.Header().Get("content-type"))
    80  	assert.Equal(t, `{"code":500,"message":"some error"}`, recorder.Body.String())
    81  
    82  	// composite errors
    83  
    84  	// unrecognized: return internal error with first error only - the second error is ignored
    85  	compositeErr := &CompositeError{
    86  		Errors: []error{
    87  			errors.New("firstError"),
    88  			errors.New("anotherError"),
    89  		},
    90  	}
    91  	recorder = httptest.NewRecorder()
    92  	ServeError(recorder, nil, compositeErr)
    93  	assert.Equal(t, http.StatusInternalServerError, recorder.Code)
    94  	assert.Equal(t, `{"code":500,"message":"firstError"}`, recorder.Body.String())
    95  
    96  	// recognized: return internal error with first error only - the second error is ignored
    97  	compositeErr = &CompositeError{
    98  		Errors: []error{
    99  			New(600, "myApiError"),
   100  			New(601, "myOtherApiError"),
   101  		},
   102  	}
   103  	recorder = httptest.NewRecorder()
   104  	ServeError(recorder, nil, compositeErr)
   105  	assert.Equal(t, CompositeErrorCode, recorder.Code)
   106  	assert.Equal(t, `{"code":600,"message":"myApiError"}`, recorder.Body.String())
   107  
   108  	// recognized API Error, flattened
   109  	compositeErr = &CompositeError{
   110  		Errors: []error{
   111  			&CompositeError{
   112  				Errors: []error{
   113  					New(600, "myApiError"),
   114  					New(601, "myOtherApiError"),
   115  				},
   116  			},
   117  		},
   118  	}
   119  	recorder = httptest.NewRecorder()
   120  	ServeError(recorder, nil, compositeErr)
   121  	assert.Equal(t, CompositeErrorCode, recorder.Code)
   122  	assert.Equal(t, `{"code":600,"message":"myApiError"}`, recorder.Body.String())
   123  
   124  	// check guard against empty CompositeError (e.g. nil Error interface)
   125  	compositeErr = &CompositeError{
   126  		Errors: []error{
   127  			&CompositeError{
   128  				Errors: []error{},
   129  			},
   130  		},
   131  	}
   132  	recorder = httptest.NewRecorder()
   133  	ServeError(recorder, nil, compositeErr)
   134  	assert.Equal(t, http.StatusInternalServerError, recorder.Code)
   135  	assert.Equal(t, `{"code":500,"message":"Unknown error"}`, recorder.Body.String())
   136  
   137  	// check guard against nil type
   138  	recorder = httptest.NewRecorder()
   139  	ServeError(recorder, nil, nil)
   140  	assert.Equal(t, http.StatusInternalServerError, recorder.Code)
   141  	assert.Equal(t, `{"code":500,"message":"Unknown error"}`, recorder.Body.String())
   142  
   143  	recorder = httptest.NewRecorder()
   144  	var z *customError
   145  	ServeError(recorder, nil, z)
   146  	assert.Equal(t, http.StatusInternalServerError, recorder.Code)
   147  	assert.Equal(t, `{"code":500,"message":"Unknown error"}`, recorder.Body.String())
   148  }
   149  
   150  func TestAPIErrors(t *testing.T) {
   151  	err := New(402, "this failed %s", "yada")
   152  	require.Error(t, err)
   153  	assert.EqualValues(t, 402, err.Code())
   154  	assert.EqualValues(t, "this failed yada", err.Error())
   155  
   156  	err = NotFound("this failed %d", 1)
   157  	require.Error(t, err)
   158  	assert.EqualValues(t, http.StatusNotFound, err.Code())
   159  	assert.EqualValues(t, "this failed 1", err.Error())
   160  
   161  	err = NotFound("")
   162  	require.Error(t, err)
   163  	assert.EqualValues(t, http.StatusNotFound, err.Code())
   164  	assert.EqualValues(t, "Not found", err.Error())
   165  
   166  	err = NotImplemented("not implemented")
   167  	require.Error(t, err)
   168  	assert.EqualValues(t, http.StatusNotImplemented, err.Code())
   169  	assert.EqualValues(t, "not implemented", err.Error())
   170  
   171  	err = MethodNotAllowed("GET", []string{"POST", "PUT"})
   172  	require.Error(t, err)
   173  	assert.EqualValues(t, http.StatusMethodNotAllowed, err.Code())
   174  	assert.EqualValues(t, "method GET is not allowed, but [POST,PUT] are", err.Error())
   175  
   176  	err = InvalidContentType("application/saml", []string{"application/json", "application/x-yaml"})
   177  	require.Error(t, err)
   178  	assert.EqualValues(t, http.StatusUnsupportedMediaType, err.Code())
   179  	assert.EqualValues(t, "unsupported media type \"application/saml\", only [application/json application/x-yaml] are allowed", err.Error())
   180  
   181  	err = InvalidResponseFormat("application/saml", []string{"application/json", "application/x-yaml"})
   182  	require.Error(t, err)
   183  	assert.EqualValues(t, http.StatusNotAcceptable, err.Code())
   184  	assert.EqualValues(t, "unsupported media type requested, only [application/json application/x-yaml] are available", err.Error())
   185  }
   186  
   187  func TestValidateName(t *testing.T) {
   188  	v := &Validation{Name: "myValidation", message: "myMessage"}
   189  
   190  	// unchanged
   191  	vv := v.ValidateName("")
   192  	assert.EqualValues(t, "myValidation", vv.Name)
   193  	assert.EqualValues(t, "myMessage", vv.message)
   194  
   195  	// forced
   196  	vv = v.ValidateName("myNewName")
   197  	assert.EqualValues(t, "myNewName.myValidation", vv.Name)
   198  	assert.EqualValues(t, "myNewName.myMessage", vv.message)
   199  
   200  	v.Name = ""
   201  	v.message = "myMessage"
   202  
   203  	// unchanged
   204  	vv = v.ValidateName("")
   205  	assert.EqualValues(t, "", vv.Name)
   206  	assert.EqualValues(t, "myMessage", vv.message)
   207  
   208  	// forced
   209  	vv = v.ValidateName("myNewName")
   210  	assert.EqualValues(t, "myNewName", vv.Name)
   211  	assert.EqualValues(t, "myNewNamemyMessage", vv.message)
   212  }
   213  
   214  func TestMarshalJSON(t *testing.T) {
   215  	const (
   216  		expectedCode = http.StatusUnsupportedMediaType
   217  		value        = "myValue"
   218  	)
   219  	list := []string{"a", "b"}
   220  
   221  	e := InvalidContentType(value, list)
   222  
   223  	jazon, err := e.MarshalJSON()
   224  	require.NoError(t, err)
   225  
   226  	expectedMessage := strings.ReplaceAll(fmt.Sprintf(contentTypeFail, value, list), `"`, `\"`)
   227  
   228  	expectedJSON := fmt.Sprintf(
   229  		`{"code":%d,"message":"%s","name":"Content-Type","in":"header","value":"%s","values":["a","b"]}`,
   230  		expectedCode, expectedMessage, value,
   231  	)
   232  	assert.JSONEq(t, expectedJSON, string(jazon))
   233  
   234  	a := apiError{code: 1, message: "a"}
   235  	jazon, err = a.MarshalJSON()
   236  	require.NoError(t, err)
   237  	assert.JSONEq(t, `{"code":1,"message":"a"}`, string(jazon))
   238  
   239  	m := MethodNotAllowedError{code: 1, message: "a", Allowed: []string{"POST"}}
   240  	jazon, err = m.MarshalJSON()
   241  	require.NoError(t, err)
   242  	assert.JSONEq(t, `{"code":1,"message":"a","allowed":["POST"]}`, string(jazon))
   243  
   244  	c := CompositeError{Errors: []error{e}, code: 1, message: "a"}
   245  	jazon, err = c.MarshalJSON()
   246  	require.NoError(t, err)
   247  	assert.JSONEq(t, fmt.Sprintf(`{"code":1,"message":"a","errors":[%s]}`, expectedJSON), string(jazon))
   248  
   249  	p := ParseError{code: 1, message: "x", Name: "a", In: "b", Value: "c", Reason: errors.New("d")}
   250  	jazon, err = p.MarshalJSON()
   251  	require.NoError(t, err)
   252  	assert.JSONEq(t, `{"code":1,"message":"x","name":"a","in":"b","value":"c","reason":"d"}`, string(jazon))
   253  }
   254  

View as plain text