import flask_restx as restx class PayloadTest(object): def assert_errors(self, client, url, data, *errors): out = client.post_json(url, data, status=400) assert "message" in out assert "errors" in out for error in errors: assert error in out["errors"] def test_validation_false_on_constructor(self, app, client): api = restx.Api(app, validate=False) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class ValidationOff(restx.Resource): @api.expect(fields) def post(self): return {} data = client.post_json("/validation/", {}) assert data == {} def test_validation_false_on_constructor_with_override(self, app, client): api = restx.Api(app, validate=False) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class ValidationOn(restx.Resource): @api.expect(fields, validate=True) def post(self): return {} self.assert_errors(client, "/validation/", {}, "name") def test_validation_true_on_constructor(self, app, client): api = restx.Api(app, validate=True) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class ValidationOff(restx.Resource): @api.expect(fields) def post(self): return {} self.assert_errors(client, "/validation/", {}, "name") def test_validation_true_on_constructor_with_override(self, app, client): api = restx.Api(app, validate=True) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class ValidationOff(restx.Resource): @api.expect(fields, validate=False) def post(self): return {} data = client.post_json("/validation/", {}) assert data == {} def _setup_api_format_checker_tests(self, app, format_checker=None): class IPAddress(restx.fields.Raw): __schema_type__ = "string" __schema_format__ = "ipv4" api = restx.Api(app, format_checker=format_checker) model = api.model("MyModel", {"ip": IPAddress(required=True)}) @api.route("/format_checker/") class TestResource(restx.Resource): @api.expect(model, validate=True) def post(self): return {} def test_format_checker_none_on_constructor(self, app, client): self._setup_api_format_checker_tests(app) out = client.post_json("/format_checker/", {"ip": "192.168.1"}) assert out == {} def test_format_checker_object_on_constructor(self, app, client): from jsonschema import FormatChecker self._setup_api_format_checker_tests(app, format_checker=FormatChecker()) out = client.post_json("/format_checker/", {"ip": "192.168.1"}, status=400) assert "ipv4" in out["errors"]["ip"] def test_validation_false_in_config(self, app, client): app.config["RESTX_VALIDATE"] = False api = restx.Api(app) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class ValidationOff(restx.Resource): @api.expect(fields) def post(self): return {} out = client.post_json("/validation/", {}) # assert response.status_code == 200 # out = json.loads(response.data.decode('utf8')) assert out == {} def test_validation_in_config(self, app, client): app.config["RESTX_VALIDATE"] = True api = restx.Api(app) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class ValidationOn(restx.Resource): @api.expect(fields) def post(self): return {} self.assert_errors(client, "/validation/", {}, "name") def test_api_payload(self, app, client): api = restx.Api(app, validate=True) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class Payload(restx.Resource): payload = None @api.expect(fields) def post(self): Payload.payload = api.payload return {} data = { "name": "John Doe", "age": 15, } client.post_json("/validation/", data) assert Payload.payload == data def test_validation_with_inheritance(self, app, client): """It should perform validation with inheritance (allOf/$ref)""" api = restx.Api(app, validate=True) fields = api.model( "Parent", { "name": restx.fields.String(required=True), }, ) child_fields = api.inherit( "Child", fields, { "age": restx.fields.Integer, }, ) @api.route("/validation/") class Inheritance(restx.Resource): @api.expect(child_fields) def post(self): return {} client.post_json( "/validation/", { "name": "John Doe", "age": 15, }, ) self.assert_errors( client, "/validation/", { "age": "15", }, "name", "age", ) def test_validation_on_list(self, app, client): """It should perform validation on lists""" api = restx.Api(app, validate=True) person = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer(required=True), }, ) family = api.model( "Family", { "name": restx.fields.String(required=True), "members": restx.fields.List(restx.fields.Nested(person)), }, ) @api.route("/validation/") class List(restx.Resource): @api.expect(family) def post(self): return {} self.assert_errors( client, "/validation/", {"name": "Doe", "members": [{"name": "Jonn"}, {"age": 42}]}, "members.0.age", "members.1.name", ) def _setup_expect_validation_single_resource_tests(self, app): # Setup a minimal Api with endpoint that expects in input payload # a single object of a resource api = restx.Api(app, validate=True) user = api.model("User", {"username": restx.fields.String()}) @api.route("/validation/") class Users(restx.Resource): @api.expect(user) def post(self): return {} def _setup_expect_validation_collection_resource_tests(self, app): # Setup a minimal Api with endpoint that expects in input payload # one or more objects of a resource api = restx.Api(app, validate=True) user = api.model("User", {"username": restx.fields.String()}) @api.route("/validation/") class Users(restx.Resource): @api.expect([user]) def post(self): return {} def test_expect_validation_single_resource_success(self, app, client): self._setup_expect_validation_single_resource_tests(app) # Input payload is a valid JSON object out = client.post_json("/validation/", {"username": "alice"}) assert {} == out def test_expect_validation_single_resource_error(self, app, client): self._setup_expect_validation_single_resource_tests(app) # Input payload is an invalid JSON object self.assert_errors(client, "/validation/", {"username": 123}, "username") # Input payload is a JSON array (expected JSON object) self.assert_errors(client, "/validation/", [{"username": 123}], "") def test_expect_validation_collection_resource_success(self, app, client): self._setup_expect_validation_collection_resource_tests(app) # Input payload is a valid JSON object out = client.post_json("/validation/", {"username": "alice"}) assert {} == out # Input payload is a JSON array with valid JSON objects out = client.post_json( "/validation/", [{"username": "alice"}, {"username": "bob"}] ) assert {} == out def test_expect_validation_collection_resource_error(self, app, client): self._setup_expect_validation_collection_resource_tests(app) # Input payload is an invalid JSON object self.assert_errors(client, "/validation/", {"username": 123}, "username") # Input payload is a JSON array but with an invalid JSON object self.assert_errors( client, "/validation/", [{"username": "alice"}, {"username": 123}], "username", ) def test_validation_with_propagate(self, app, client): app.config["PROPAGATE_EXCEPTIONS"] = True api = restx.Api(app, validate=True) fields = api.model( "Person", { "name": restx.fields.String(required=True), "age": restx.fields.Integer, "birthdate": restx.fields.DateTime, }, ) @api.route("/validation/") class ValidationOff(restx.Resource): @api.expect(fields) def post(self): return {} self.assert_errors(client, "/validation/", {}, "name") def test_empty_payload(self, app, client): api = restx.Api(app, validate=True) @api.route("/empty/") class Payload(restx.Resource): def post(self): return {} response = client.post( "/empty/", data="", headers={"content-type": "application/json"} ) assert response.status_code == 200