func TestValidateParameters(t *testing.T) { doc, api := petstore.NewAPI(t) validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc res := validator.validateParameters() assert.Empty(t, res.Errors) sw := doc.Spec() sw.Paths.Paths["/pets"].Get.Parameters = append(sw.Paths.Paths["/pets"].Get.Parameters, *spec.QueryParam("limit").Typed("string", "")) res = validator.validateParameters() assert.NotEmpty(t, res.Errors) doc, api = petstore.NewAPI(t) sw = doc.Spec() sw.Paths.Paths["/pets"].Post.Parameters = append(sw.Paths.Paths["/pets"].Post.Parameters, *spec.BodyParam("fake", spec.RefProperty("#/definitions/Pet"))) validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc res = validator.validateParameters() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 1) assert.Contains(t, res.Errors[0].Error(), "has more than 1 body param") doc, api = petstore.NewAPI(t) sw = doc.Spec() pp := sw.Paths.Paths["/pets/{id}"] pp.Delete = nil var nameParams []spec.Parameter for _, p := range pp.Parameters { if p.Name == "id" { p.Name = "name" nameParams = append(nameParams, p) } } pp.Parameters = nameParams sw.Paths.Paths["/pets/{name}"] = pp doc.Reload() validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc res = validator.validateParameters() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 1) assert.Contains(t, res.Errors[0].Error(), "overlaps with") doc, api = petstore.NewAPI(t) validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc sw = doc.Spec() pp = sw.Paths.Paths["/pets/{id}"] pp.Delete = nil pp.Get.Parameters = nameParams pp.Parameters = nil sw.Paths.Paths["/pets/{id}"] = pp doc.Reload() res = validator.validateParameters() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 2) assert.Contains(t, res.Errors[1].Error(), "is not present in the path") assert.Contains(t, res.Errors[0].Error(), "has no parameter definition") }
func TestContentTypeValidation(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := newValidation(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/pets", nil) request.Header.Add("Accept", "*/*") mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/pets", nil) request.Header.Add("content-type", "application(") mw.ServeHTTP(recorder, request) assert.Equal(t, 400, recorder.Code) assert.Equal(t, "application/json", recorder.Header().Get("content-type")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/pets", nil) request.Header.Add("Accept", "application/json") request.Header.Add("content-type", "text/html") mw.ServeHTTP(recorder, request) assert.Equal(t, 415, recorder.Code) assert.Equal(t, "application/json", recorder.Header().Get("content-type")) }
func TestServeSpecMiddleware(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) handler := specMiddleware(ctx, nil) // serves spec request, _ := http.NewRequest("GET", "/swagger.json", nil) request.Header.Add(httpkit.HeaderContentType, httpkit.JSONMime) recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) // returns 404 when no next handler request, _ = http.NewRequest("GET", "/api/pets", nil) request.Header.Add(httpkit.HeaderContentType, httpkit.JSONMime) recorder = httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 404, recorder.Code) // forwards to next handler for other url handler = specMiddleware(ctx, http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusOK) })) request, _ = http.NewRequest("GET", "/api/pets", nil) request.Header.Add(httpkit.HeaderContentType, httpkit.JSONMime) recorder = httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) }
func TestRouterBuilder(t *testing.T) { spec, api := petstore.NewAPI(t) assert.Len(t, spec.RequiredConsumes(), 3) assert.Len(t, spec.RequiredProduces(), 5) assert.Len(t, spec.OperationIDs(), 4) // context := NewContext(spec, api) builder := petAPIRouterBuilder(spec, api) getRecords := builder.records["GET"] postRecords := builder.records["POST"] deleteRecords := builder.records["DELETE"] assert.Len(t, getRecords, 2) assert.Len(t, postRecords, 1) assert.Len(t, deleteRecords, 1) assert.Empty(t, builder.records["PATCH"]) assert.Empty(t, builder.records["OPTIONS"]) assert.Empty(t, builder.records["HEAD"]) assert.Empty(t, builder.records["PUT"]) rec := postRecords[0] assert.Equal(t, rec.Key, "/pets") val := rec.Value.(*routeEntry) assert.Len(t, val.Consumers, 3) assert.Len(t, val.Producers, 5) assert.Len(t, val.Consumes, 3) assert.Len(t, val.Produces, 5) assert.Len(t, val.Parameters, 1) }
func TestContextBindAndValidate(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("POST", "/pets", nil) request.Header.Add("Accept", "*/*") request.Header.Add("content-type", "text/html") v, ok := context.GetOk(request, ctxBoundParams) assert.False(t, ok) assert.Nil(t, v) ri, _ := ctx.RouteInfo(request) data, result := ctx.BindAndValidate(request, ri) // this requires a much more thorough test assert.NotNil(t, data) assert.NotNil(t, result) v, ok = context.GetOk(request, ctxBoundParams) assert.True(t, ok) assert.NotNil(t, v) dd, rr := ctx.BindAndValidate(request, ri) assert.Equal(t, data, dd) assert.Equal(t, result, rr) }
func TestContextInvalidResponseFormat(t *testing.T) { ct := "application/x-yaml" other := "application/sgml" spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("GET", "http://localhost:8080", nil) request.Header.Set(httpkit.HeaderAccept, ct) // check there's nothing there cached, ok := context.GetOk(request, ctxResponseFormat) assert.False(t, ok) assert.Empty(t, cached) // trigger the parse mt := ctx.ResponseFormat(request, []string{other}) assert.Empty(t, mt) // check it was cached cached, ok = context.GetOk(request, ctxResponseFormat) assert.False(t, ok) assert.Empty(t, cached) // check if the cast works and fetch from cache too mt = ctx.ResponseFormat(request, []string{other}) assert.Empty(t, mt) }
func TestContextRender(t *testing.T) { ct := httpkit.JSONMime spec, api := petstore.NewAPI(t) assert.NotNil(t, spec) assert.NotNil(t, api) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("GET", "pets", nil) request.Header.Set(httpkit.HeaderAccept, ct) ri, _ := ctx.RouteInfo(request) recorder := httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"}) assert.Equal(t, 200, recorder.Code) assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong")) assert.Equal(t, 500, recorder.Code) recorder = httptest.NewRecorder() assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) }) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "pets", nil) assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, ri, map[string]interface{}{"name": "hello"}) }) request, _ = http.NewRequest("GET", "/pets", nil) request.Header.Set(httpkit.HeaderAccept, ct) ri, _ = ctx.RouteInfo(request) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"}) assert.Equal(t, 200, recorder.Code) assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong")) assert.Equal(t, 500, recorder.Code) recorder = httptest.NewRecorder() assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) }) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/pets", nil) assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, ri, map[string]interface{}{"name": "hello"}) }) recorder = httptest.NewRecorder() request, _ = http.NewRequest("DELETE", "/pets/1", nil) ri, _ = ctx.RouteInfo(request) ctx.Respond(recorder, request, ri.Produces, ri, nil) assert.Equal(t, 204, recorder.Code) }
func TestRouterMiddleware(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) mw := newRouter(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("DELETE", "/api/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code) methods := strings.Split(recorder.Header().Get("Allow"), ",") sort.Sort(sort.StringSlice(methods)) assert.Equal(t, "GET,POST", strings.Join(methods, ",")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/nopets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotFound, recorder.Code) spec, api = petstore.NewRootAPI(t) context = NewContext(spec, api, nil) mw = newRouter(context, http.HandlerFunc(terminator)) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) recorder = httptest.NewRecorder() request, _ = http.NewRequest("DELETE", "/pets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code) methods = strings.Split(recorder.Header().Get("Allow"), ",") sort.Sort(sort.StringSlice(methods)) assert.Equal(t, "GET,POST", strings.Join(methods, ",")) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/nopets", nil) mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotFound, recorder.Code) }
func TestOperationExecutor(t *testing.T) { spec, api := petstore.NewAPI(t) api.RegisterOperation("getAllPets", httpkit.OperationHandlerFunc(func(params interface{}) (interface{}, error) { return []interface{}{ map[string]interface{}{"id": 1, "name": "a dog"}, }, nil })) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := newOperationExecutor(context) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/pets", nil) request.Header.Add("Accept", "application/json") request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) assert.Equal(t, `[{"id":1,"name":"a dog"}]`+"\n", recorder.Body.String()) spec, api = petstore.NewAPI(t) api.RegisterOperation("getAllPets", httpkit.OperationHandlerFunc(func(params interface{}) (interface{}, error) { return nil, errors.New(422, "expected") })) context = NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw = newOperationExecutor(context) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/pets", nil) request.Header.Add("Accept", "application/json") request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, 422, recorder.Code) assert.Equal(t, `{"code":422,"message":"expected"}`, recorder.Body.String()) }
func TestContextAuthorize(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := httpkit.JSONRequest("GET", "/pets", nil) v, ok := context.GetOk(request, ctxSecurityPrincipal) assert.False(t, ok) assert.Nil(t, v) ri, ok := ctx.RouteInfo(request) assert.True(t, ok) p, err := ctx.Authorize(request, ri) assert.Error(t, err) assert.Nil(t, p) v, ok = context.GetOk(request, ctxSecurityPrincipal) assert.False(t, ok) assert.Nil(t, v) request.SetBasicAuth("wrong", "wrong") p, err = ctx.Authorize(request, ri) assert.Error(t, err) assert.Nil(t, p) v, ok = context.GetOk(request, ctxSecurityPrincipal) assert.False(t, ok) assert.Nil(t, v) request.SetBasicAuth("admin", "admin") p, err = ctx.Authorize(request, ri) assert.NoError(t, err) assert.Equal(t, "admin", p) v, ok = context.GetOk(request, ctxSecurityPrincipal) assert.True(t, ok) assert.Equal(t, "admin", v) request.SetBasicAuth("doesn't matter", "doesn't") pp, rr := ctx.Authorize(request, ri) assert.Equal(t, p, pp) assert.Equal(t, err, rr) }
func TestRouterStruct(t *testing.T) { spec, api := petstore.NewAPI(t) router := DefaultRouter(spec, newRoutableUntypedAPI(spec, api, new(Context))) methods := router.OtherMethods("post", "/pets/{id}") assert.Len(t, methods, 2) entry, ok := router.Lookup("delete", "/pets/{id}") assert.True(t, ok) assert.NotNil(t, entry) assert.Len(t, entry.Params, 1) assert.Equal(t, "id", entry.Params[0].Name) _, ok = router.Lookup("delete", "/pets") assert.False(t, ok) _, ok = router.Lookup("post", "/no-pets") assert.False(t, ok) }
func TestServe(t *testing.T) { spec, api := petstore.NewAPI(t) handler := Serve(spec, api) // serve spec document request, _ := http.NewRequest("GET", "http://localhost:8080/swagger.json", nil) request.Header.Add("Content-Type", httpkit.JSONMime) request.Header.Add("Accept", httpkit.JSONMime) recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) request, _ = http.NewRequest("GET", "http://localhost:8080/swagger-ui", nil) recorder = httptest.NewRecorder() handler.ServeHTTP(recorder, request) assert.Equal(t, 404, recorder.Code) }
func TestValidateRequiredDefinitions(t *testing.T) { doc, api := petstore.NewAPI(t) validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc res := validator.validateRequiredDefinitions() assert.Empty(t, res.Errors) // properties sw := doc.Spec() def := sw.Definitions["Tag"] def.Required = append(def.Required, "type") sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.NotEmpty(t, res.Errors) // pattern properties def.PatternProperties = make(map[string]spec.Schema) def.PatternProperties["ty.*"] = *spec.StringProperty() sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.Empty(t, res.Errors) def.PatternProperties = make(map[string]spec.Schema) def.PatternProperties["^ty.$"] = *spec.StringProperty() sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.NotEmpty(t, res.Errors) // additional properties def.PatternProperties = nil def.AdditionalProperties = &spec.SchemaOrBool{Allows: true} sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.Empty(t, res.Errors) def.AdditionalProperties = &spec.SchemaOrBool{Allows: false} sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.NotEmpty(t, res.Errors) }
func TestResponseFormatValidation(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := newValidation(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(`{"name":"Dog"}`))) request.Header.Set(httpkit.HeaderContentType, "application/json") request.Header.Set(httpkit.HeaderAccept, "application/json") mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code, recorder.Body.String()) recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/pets", bytes.NewBuffer([]byte(`{"name":"Dog"}`))) request.Header.Set(httpkit.HeaderContentType, "application/json") request.Header.Set(httpkit.HeaderAccept, "application/sml") mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusNotAcceptable, recorder.Code) }
func TestContextInvalidRoute(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) ctx.router = DefaultRouter(spec, ctx.api) request, _ := http.NewRequest("DELETE", "pets", nil) // check there's nothing there _, ok := context.GetOk(request, ctxMatchedRoute) assert.False(t, ok) matched, ok := ctx.RouteInfo(request) assert.False(t, ok) assert.Nil(t, matched) // check it was cached _, ok = context.GetOk(request, ctxMatchedRoute) assert.False(t, ok) matched, ok = ctx.RouteInfo(request) assert.False(t, ok) assert.Nil(t, matched) }
func TestValidateItems(t *testing.T) { doc, api := petstore.NewAPI(t) validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc res := validator.validateItems() assert.Empty(t, res.Errors) // in operation parameters sw := doc.Spec() sw.Paths.Paths["/pets"].Get.Parameters[0].Type = "array" res = validator.validateItems() assert.NotEmpty(t, res.Errors) sw.Paths.Paths["/pets"].Get.Parameters[0].Items = spec.NewItems().Typed("string", "") res = validator.validateItems() assert.Empty(t, res.Errors) sw.Paths.Paths["/pets"].Get.Parameters[0].Items = spec.NewItems().Typed("array", "") res = validator.validateItems() assert.NotEmpty(t, res.Errors) sw.Paths.Paths["/pets"].Get.Parameters[0].Items.Items = spec.NewItems().Typed("string", "") res = validator.validateItems() assert.Empty(t, res.Errors) // in global parameters sw.Parameters = make(map[string]spec.Parameter) sw.Parameters["other"] = *spec.SimpleArrayParam("other", "array", "csv") res = validator.validateItems() assert.Empty(t, res.Errors) pp := spec.SimpleArrayParam("other", "array", "") pp.Items = nil sw.Parameters["other"] = *pp res = validator.validateItems() assert.NotEmpty(t, res.Errors) // in shared path object parameters doc, api = petstore.NewAPI(t) validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc sw = doc.Spec() pa := sw.Paths.Paths["/pets"] pa.Parameters = []spec.Parameter{*spec.SimpleArrayParam("another", "array", "csv")} sw.Paths.Paths["/pets"] = pa res = validator.validateItems() assert.Empty(t, res.Errors) pa = sw.Paths.Paths["/pets"] pp = spec.SimpleArrayParam("other", "array", "") pp.Items = nil pa.Parameters = []spec.Parameter{*pp} sw.Paths.Paths["/pets"] = pa res = validator.validateItems() assert.NotEmpty(t, res.Errors) // in body param schema doc, api = petstore.NewAPI(t) validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc sw = doc.Spec() pa = sw.Paths.Paths["/pets"] pa.Post.Parameters[0].Schema = spec.ArrayProperty(nil) res = validator.validateItems() assert.NotEmpty(t, res.Errors) // in response headers doc, api = petstore.NewAPI(t) validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc sw = doc.Spec() pa = sw.Paths.Paths["/pets"] rp := pa.Post.Responses.StatusCodeResponses[200] var hdr spec.Header hdr.Type = "array" rp.Headers = make(map[string]spec.Header) rp.Headers["X-YADA"] = hdr pa.Post.Responses.StatusCodeResponses[200] = rp res = validator.validateItems() assert.NotEmpty(t, res.Errors) // in response schema doc, api = petstore.NewAPI(t) validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), api.Formats()) validator.spec = doc sw = doc.Spec() pa = sw.Paths.Paths["/pets"] rp = pa.Post.Responses.StatusCodeResponses[200] rp.Schema = spec.ArrayProperty(nil) pa.Post.Responses.StatusCodeResponses[200] = rp res = validator.validateItems() assert.NotEmpty(t, res.Errors) }