import json import unittest.mock from openapi_core import create_spec # type: ignore from openapi_core.exceptions import OpenAPIError # type: ignore import tornado.httpclient # type: ignore import tornado.web # type: ignore import tornado.testing # type: ignore from tornado_openapi3.handler import OpenAPIRequestHandler class ResourceHandler(OpenAPIRequestHandler): spec = create_spec( { "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0", }, "components": { "schemas": { "resource": { "type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"], }, }, "securitySchemes": { "basicAuth": { "type": "http", "scheme": "bearer", } }, }, "security": [{"basicAuth": []}], "paths": { "/resource": { "post": { "requestBody": { "required": True, "content": { "application/vnd.example.resource+json": { "schema": {"$ref": "#/components/schemas/resource"}, } }, }, "responses": { "200": { "description": "Success", "content": { "application/vnd.example.resource+json": { "schema": { "$ref": "#/components/schemas/resource" }, } }, }, "401": { "description": "Missing or invalid credentials", }, }, } } }, } ) custom_media_type_deserializers = { "application/vnd.example.resource+json": json.loads, } async def post(self) -> None: self.set_header("Content-Type", "application/vnd.example.resource+json") self.finish( json.dumps( { "name": self.validated.body["name"], } ) ) class DefaultSchemaTest(tornado.testing.AsyncHTTPTestCase): def get_app(self) -> tornado.web.Application: test = self class RequestHandler(OpenAPIRequestHandler): async def prepare(self) -> None: with test.assertRaises(NotImplementedError): self.spec async def get(self) -> None: ... return tornado.web.Application( [ (r"/", RequestHandler), ] ) def test_schema_must_be_implemented(self) -> None: response = self.fetch("/") self.assertEqual(200, response.code) class DefaultDeserializers(tornado.testing.AsyncHTTPTestCase): def get_app(self) -> tornado.web.Application: test = self class RequestHandler(OpenAPIRequestHandler): async def prepare(self) -> None: test.assertEqual(dict(), self.custom_media_type_deserializers) async def get(self) -> None: ... return tornado.web.Application( [ (r"/", RequestHandler), ] ) def test_schema_must_be_implemented(self) -> None: response = self.fetch("/") self.assertEqual(200, response.code) class RequestHandlerTests(tornado.testing.AsyncHTTPTestCase): def get_app(self) -> tornado.web.Application: return tornado.web.Application( [ (r"/resource", ResourceHandler), (r"/undocumented", ResourceHandler), ] ) def test_invalid_operation(self) -> None: response = self.fetch("/resource") self.assertEqual(405, response.code) def test_bad_data(self) -> None: response = self.fetch( "/resource", method="POST", headers={ "Authorization": "Bearer secret", "Content-Type": "application/vnd.example.resource+json", }, body="asdf", ) self.assertEqual(400, response.code) def test_missing_field(self) -> None: response = self.fetch( "/resource", method="POST", headers={ "Authorization": "Bearer secret", "Content-Type": "application/vnd.example.resource+json", }, body=json.dumps({}), ) self.assertEqual(400, response.code) def test_missing_security(self) -> None: response = self.fetch( "/resource", method="POST", headers={ "Content-Type": "application/vnd.example.resource+json", }, body=json.dumps({"name": "Name"}), ) self.assertEqual(401, response.code) def test_invalid_content_type(self) -> None: response = self.fetch( "/resource", method="POST", headers={ "Authorization": "Bearer secret", "Content-Type": "application/json", }, body=json.dumps({"name": "Name"}), ) self.assertEqual(415, response.code) def test_undocumented_endpoint(self) -> None: response = self.fetch( "/undocumented", method="POST", headers={ "Authorization": "Bearer secret", "Content-Type": "application/vnd.example.resource+json", }, body=json.dumps({"name": "Name"}), ) self.assertEqual(404, response.code) def test_unexpected_openapi_error(self) -> None: with unittest.mock.patch( "openapi_core.validation.datatypes.BaseValidationResult.raise_for_errors", side_effect=OpenAPIError, ): response = self.fetch( "/resource", method="POST", headers={ "Authorization": "Bearer secret", "Content-Type": "application/vnd.example.resource+json", }, body=json.dumps({"name": "Name"}), ) self.assertEqual(500, response.code) def test_success(self) -> None: response = self.fetch( "/resource", method="POST", headers={ "Authorization": "Bearer secret", "Content-Type": "application/vnd.example.resource+json", }, body=json.dumps({"name": "Name"}), ) self.assertEqual(200, response.code)