diff --git a/internal/server/control/control_test.go b/internal/server/control/control_test.go index c40b70b..9d1738d 100644 --- a/internal/server/control/control_test.go +++ b/internal/server/control/control_test.go @@ -130,7 +130,7 @@ func TestAddRouteReturnsBadRequestWhenMalformedJSONBody(t *testing.T) { addRoute(resp, req) for _, e := range checkErrorResponse(resp.Result(), http.StatusBadRequest, "Malformed JSON") { - t.Error(e.Error()) + t.Error(e) } } @@ -194,7 +194,7 @@ func TestAddRouteReturns422ErrorWhenMandatoryFieldsMissing(t *testing.T) { r := resp.Result() if test.testMustFail { for _, e := range checkErrorResponse(r, http.StatusUnprocessableEntity, "Invalid Route") { - t.Error(e.Error()) + t.Error(e) } } else if !test.testMustFail { if r.StatusCode != http.StatusCreated { @@ -258,7 +258,7 @@ func TestAddRoute500sWhenIDGeneratorFails(t *testing.T) { addRoute(resp, req) for _, e := range checkErrorResponse(resp.Result(), http.StatusInternalServerError, "Internal Server Error") { - t.Error(e.Error()) + t.Error(e) } } @@ -324,7 +324,7 @@ func TestAddRoute422sWhenInvalidRoute(t *testing.T) { addRoute(resp, req) for _, e := range checkErrorResponse(resp.Result(), http.StatusUnprocessableEntity, "Invalid Route") { - t.Error(e.Error()) + t.Error(e) } } @@ -345,7 +345,7 @@ func TestRemoveRouteReturnsNotFound(t *testing.T) { handler.ServeHTTP(resp, req) for _, e := range checkErrorResponse(resp.Result(), http.StatusNotFound, "Route Not Found") { - t.Error(e.Error()) + t.Error(e) } } @@ -436,7 +436,7 @@ func TestGetRouteReturns404sWhenRouteDoesntExist(t *testing.T) { handler.ServeHTTP(w, r) for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Route Not Found") { - t.Error(e.Error()) + t.Error(e) } } diff --git a/internal/server/data/decorator_test.go b/internal/server/data/decorator_test.go index 420b7bb..1c75cdd 100644 --- a/internal/server/data/decorator_test.go +++ b/internal/server/data/decorator_test.go @@ -128,7 +128,7 @@ func TestCheckHandlerReturnsAFunctionsThat404sWhenHandlerDoesNotExist(t *testing fn(w, r) for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Handler ID Not Found") { - t.Error(e.Error()) + t.Error(e) } } diff --git a/internal/server/data/resource.go b/internal/server/data/resource.go index caf3e13..d55fc72 100644 --- a/internal/server/data/resource.go +++ b/internal/server/data/resource.go @@ -28,12 +28,18 @@ import ( "github.com/gorilla/mux" ) +const ( + ResourceItemNotFound = "Resource Item Not Found" + NonIntegerValue = "Non Integer Value" + InvalidStatusCode = "Invalid Status Code" +) + func getRequestBody(w http.ResponseWriter, r *http.Request, h *model.Handler) { w.Header().Add("Content-Type", "application/octet-stream") n, err := io.Copy(w, h.Request.Body) if err != nil { if n == 0 { - srverrors.WriteErrorResponse(http.StatusInternalServerError, "Internal Server Error", w) + srverrors.WriteErrorResponse(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), w) } else { // Only way to abort current connection as of go 1.13 // https://github.com/golang/go/issues/16542 @@ -65,7 +71,7 @@ func getRequestMatches(w http.ResponseWriter, r *http.Request, h *model.Handler) if value, ok := vars[name]; ok { _, _ = w.Write([]byte(value)) } else { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } } @@ -75,7 +81,7 @@ func getRequestParams(w http.ResponseWriter, r *http.Request, h *model.Handler) if values, ok := h.Request.URL.Query()[name]; ok { _, _ = w.Write([]byte(values[0])) } else { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } } @@ -85,7 +91,7 @@ func getRequestHeaders(w http.ResponseWriter, r *http.Request, h *model.Handler) if values, ok := h.Request.Header[textproto.CanonicalMIMEHeaderKey(name)]; ok { _, _ = w.Write([]byte(values[0])) } else { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } } @@ -95,7 +101,7 @@ func getRequestCookies(w http.ResponseWriter, r *http.Request, h *model.Handler) if cookie, err := h.Request.Cookie(name); err == nil { _, _ = w.Write([]byte(cookie.Value)) } else { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } } @@ -111,11 +117,11 @@ func getRequestForm(w http.ResponseWriter, r *http.Request, h *model.Handler) { // We tried to exercise this execution path but didn't know how. err := h.Request.ParseForm() if err != nil { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } else if values, ok := h.Request.Form[name]; ok { _, _ = w.Write([]byte(values[0])) } else { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } } @@ -126,7 +132,7 @@ func getRequestFileName(w http.ResponseWriter, r *http.Request, h *model.Handler if err == nil { _, _ = w.Write([]byte(header.Filename)) } else { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } } @@ -137,7 +143,7 @@ func getRequestFileContent(w http.ResponseWriter, r *http.Request, h *model.Hand if err == nil { _, _ = io.Copy(w, file) } else { - w.WriteHeader(http.StatusNotFound) + srverrors.WriteErrorResponse(http.StatusNotFound, ResourceItemNotFound, w) } } @@ -146,17 +152,16 @@ func getRequestFileContent(w http.ResponseWriter, r *http.Request, h *model.Hand func setResponseStatus(w http.ResponseWriter, r *http.Request, h *model.Handler) { sb, err := ioutil.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusInternalServerError) + srverrors.WriteErrorResponse(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), w) return } - si, err := strconv.Atoi(string(sb)) - if http.StatusText(si) == "" { - w.WriteHeader(http.StatusBadRequest) - } else if err == nil { - h.Writer.WriteHeader(int(si)) + if si, err := strconv.Atoi(string(sb)); err != nil { + srverrors.WriteErrorResponse(http.StatusUnprocessableEntity, NonIntegerValue, w) + } else if http.StatusText(si) == "" { + srverrors.WriteErrorResponse(http.StatusBadRequest, InvalidStatusCode, w) } else { - w.WriteHeader(http.StatusBadRequest) + h.Writer.WriteHeader(int(si)) } } @@ -164,7 +169,7 @@ func setResponseHeaders(w http.ResponseWriter, r *http.Request, h *model.Handler name := mux.Vars(r)["name"] vb, err := ioutil.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusInternalServerError) + srverrors.WriteErrorResponse(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), w) return } @@ -180,7 +185,7 @@ func setResponseCookies(w http.ResponseWriter, r *http.Request, h *model.Handler name := mux.Vars(r)["name"] vb, err := ioutil.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusInternalServerError) + srverrors.WriteErrorResponse(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), w) return } @@ -193,6 +198,6 @@ func setResponseBody(w http.ResponseWriter, r *http.Request, h *model.Handler) { if n > 0 { panic("Truncated body") } - w.WriteHeader(http.StatusInternalServerError) + srverrors.WriteErrorResponse(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), w) } } diff --git a/internal/server/data/resource_test.go b/internal/server/data/resource_test.go index 73e7c32..7ed8fae 100644 --- a/internal/server/data/resource_test.go +++ b/internal/server/data/resource_test.go @@ -121,8 +121,8 @@ func TestGetRequestBody500sWhenHandlerRequestErrors(t *testing.T) { getRequestBody(w, r, &h) - for _, e := range checkErrorResponse(w.Result(), http.StatusInternalServerError, "Internal Server Error") { - t.Error(e.Error()) + for _, e := range checkErrorResponse(w.Result(), http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) { + t.Error(e) } } @@ -369,9 +369,8 @@ func TestGetRequestMatchesReturnsNotFoundWhenMatchDoesntExists(t *testing.T) { getRequestMatches(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404. Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -433,9 +432,8 @@ func TestGetRequestParams404sWhenParamDoesntExist(t *testing.T) { getRequestParams(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404. Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -478,14 +476,15 @@ func TestGetRequestHeadersSetsOctectStreamContentType(t *testing.T) { Request: httptest.NewRequest("GET", "/", nil), Writer: httptest.NewRecorder(), } + h.Request.Header.Set("bar", "BAZ") r := createMuxRequest("/handlers/HANDLERID/request/headers/{name}", "/handlers/HANDLERID/request/headers/bar", "GET", nil) w := httptest.NewRecorder() getRequestHeaders(w, r, &h) res := w.Result() - if res.Header.Get("Content-Type") != "application/octet-stream" { - t.Error("Content Type mismatch") + if v := res.Header.Get("Content-Type"); v != "application/octet-stream" { + t.Errorf("Content Type mismatch. Expected: application/octet-stream. Got: %q", v) } } @@ -567,9 +566,8 @@ func TestGetRequestHeaders404sWhenHeaderDoesntExist(t *testing.T) { getRequestHeaders(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Error("Status code mismatch") + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -652,9 +650,8 @@ func TestGetRequestCookies404sIfCookieDoesntExist(t *testing.T) { getRequestCookies(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -754,9 +751,8 @@ func TestGetRequestForm404sWhenFieldDoesntExist(t *testing.T) { getRequestForm(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -810,9 +806,8 @@ func TestGetRequestForm404sWhenFormDoesntExist(t *testing.T) { getRequestForm(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -885,9 +880,8 @@ func TestGetRequestFileName404sWhenFileDoesntExist(t *testing.T) { getRequestFileName(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -902,9 +896,8 @@ func TestGetRequestFileName404sWhenFormDoesntExist(t *testing.T) { getRequestFileName(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -966,9 +959,8 @@ func TestGetRequestFileContent404sWhenFileDoesntExist(t *testing.T) { getRequestFileContent(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -983,9 +975,8 @@ func TestGetRequestFileContent404sWhenFormDoesntExist(t *testing.T) { getRequestFileContent(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusNotFound { - t.Errorf("Status code mismatch. Expected: 404, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusNotFound, "Resource Item Not Found") { + t.Error(e) } } @@ -1009,9 +1000,8 @@ func TestGetRequestFileContent500sWhenHandlerRequestErrors(t *testing.T) { getRequestFileContent(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusInternalServerError { - t.Error("status not 500", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) { + t.Error(e) } } @@ -1058,9 +1048,8 @@ func TestSetResponseStatus400sWhenNonparseableStatusCode(t *testing.T) { setResponseStatus(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusBadRequest { - t.Errorf("Status code mismatch. Expected: 400, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusUnprocessableEntity, "Non Integer Value") { + t.Error(e) } } @@ -1074,9 +1063,8 @@ func TestSetResponseStatus500sWhenErrorReadingRequest(t *testing.T) { setResponseStatus(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusInternalServerError { - t.Errorf("Status code mismatch. Expected: 500, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) { + t.Error(e) } } @@ -1092,9 +1080,8 @@ func TestSetResponseStatus400sWhenStatusCodeNotSupportedByGo(t *testing.T) { setResponseStatus(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusBadRequest { - t.Errorf("Status code mismatch. Expected: 400, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusBadRequest, "Invalid Status Code") { + t.Error(e) } } @@ -1162,9 +1149,8 @@ func TestSetResponseHeaders500sWhenErrorReadingRequest(t *testing.T) { setResponseHeaders(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusInternalServerError { - t.Errorf("Status code mismatch. Expected: 500, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) { + t.Error(e) } } @@ -1181,9 +1167,8 @@ func TestSetResponseHeaders400sOnInvalidHeaderKey(t *testing.T) { setResponseHeaders(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusBadRequest { - t.Errorf("Status code mismatch. Expected: 400, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusBadRequest, "Invalid Header Name") { + t.Error(e) } } @@ -1200,9 +1185,8 @@ func TestSetResponseHeaders400sOnInvalidHeaderValue(t *testing.T) { setResponseHeaders(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusBadRequest { - t.Errorf("Status code mismatch. Expected: 400, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusBadRequest, "Invalid Header Value") { + t.Error(e) } } @@ -1250,9 +1234,8 @@ func TestSetResponseCookies500sWhenErrorReadingRequest(t *testing.T) { setResponseCookies(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusInternalServerError { - t.Errorf("Status code mismatch. Expected: 500, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) { + t.Error(e) } } @@ -1322,9 +1305,8 @@ func TestSetResponseBody500sWhenReaderFailsInFirstRead(t *testing.T) { setResponseBody(w, r, &h) - res := w.Result() - if res.StatusCode != http.StatusInternalServerError { - t.Errorf("Status code mismatch. Expected: 500, Got: %d", res.StatusCode) + for _, e := range checkErrorResponse(w.Result(), http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) { + t.Error(e) } } diff --git a/internal/server/data/server_test.go b/internal/server/data/server_test.go index 30cadf5..7481e5a 100644 --- a/internal/server/data/server_test.go +++ b/internal/server/data/server_test.go @@ -78,6 +78,6 @@ func TestConfigRouterReturnsRouterThat400sOnUnconfiguredResources(t *testing.T) m.ServeHTTP(w, httptest.NewRequest("GET", "/handlers/FOO/dummy", nil)) for _, e := range checkErrorResponse(w.Result(), http.StatusBadRequest, "Invalid Resource Path") { - t.Error(e.Error()) + t.Error(e) } } diff --git a/spec/README.md b/spec/README.md index ba95891..aadca97 100644 --- a/spec/README.md +++ b/spec/README.md @@ -554,6 +554,10 @@ path doesn't exist or is invalid. * **Error Responses**: * **Code**: `400`; Reason: `Invalid Resource Path`
**Notes**: Check the list of valid resource paths at the top of this section. + * **Code**: `422`; Reason: `Non Integer Value`
+ **Notes**: When setting the status code with a non integer value. + * **Code**: `400`; Reason: `Invalid Status Code`
+ **Notes**: When setting a non-supported status code. * **Code**: `404`; Reason: `Handler ID Not Found`
**Notes**: Refers to the handler resource itself. * **Sample Call**: