from __future__ import annotations import json import sys import tempfile import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from swagger_drogon.contract import load_contract from swagger_drogon.generator import generate from swagger_drogon.infer import infer_operation, load_sources from swagger_drogon.parser import parse_routes class SwaggerDrogonTest(unittest.TestCase): def test_parse_route_metadata(self) -> None: with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) controllers = root / "controllers" controllers.mkdir() (controllers / "SampleController.h").write_text( """ class SampleController { METHOD_LIST_BEGIN /* * openapi: * tags: Sample * summary: List samples * description: Lists samples. * auth: public * body: none * response: Sample list. */ ADD_METHOD_TO(SampleController::list, "/api/v1/samples", drogon::Get); METHOD_LIST_END }; """, encoding="utf-8", ) routes = parse_routes(controllers) self.assertEqual(len(routes), 1) self.assertEqual(routes[0].operation_id, "SampleController.list") self.assertEqual(routes[0].metadata["summary"], "List samples") def test_infer_query_and_json_fields(self) -> None: with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) controllers = root / "controllers" services = root / "services" controllers.mkdir() services.mkdir() header = controllers / "SampleController.h" header.write_text( """ class SampleController { METHOD_LIST_BEGIN /* * openapi: * tags: Sample * summary: Create sample * description: Creates a sample. * auth: bearer * body: json * response: Created sample. */ ADD_METHOD_TO(SampleController::create, "/api/v1/samples", drogon::Post); METHOD_LIST_END }; """, encoding="utf-8", ) (controllers / "SampleController.cc").write_text( """ void SampleController::create(const drogon::HttpRequestPtr &req) { auto status = queryValue(req, "status"); SampleService service; service.create(body); } """, encoding="utf-8", ) (services / "SampleService.cc").write_text( """ Json::Value SampleService::create(const Json::Value &body) { const auto missing = validation::missingStrings(body, {"name"}); if (!validation::hasNumber(body, "category_id")) {} auto response = success(); response["sample_id"] = Json::Int64(1); return response; } """, encoding="utf-8", ) route = parse_routes(controllers)[0] inferred = infer_operation(route, load_sources([controllers, services])) self.assertEqual(inferred.query_parameters[0]["name"], "status") self.assertIn("name", inferred.request_schema["required"]) self.assertIn("category_id", inferred.request_schema["required"]) self.assertIn("sample_id", inferred.response_schema["properties"]) def test_strict_validation_rejects_generic_contract(self) -> None: with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) controllers = root / "controllers" controllers.mkdir() (controllers / "SampleController.h").write_text( """ class SampleController { METHOD_LIST_BEGIN /* * openapi: * tags: Sample * summary: Create sample * description: Creates a sample. * auth: public * body: json * response: Created sample. */ ADD_METHOD_TO(SampleController::create, "/api/v1/samples", drogon::Post); METHOD_LIST_END }; """, encoding="utf-8", ) with self.assertRaisesRegex(ValueError, "generic object"): generate( controllers_dir=controllers, source_dirs=[], contract={ "operations": { "SampleController.create": { "request": {"type": "object"}, "response": {"type": "object"}, } } }, title="Sample", version="0.1.0", server="http://localhost", ) def test_visione_contract_generates_concrete_media_path(self) -> None: repo = Path(__file__).resolve().parents[3] spec = generate( controllers_dir=repo / "controllers", source_dirs=[repo / "controllers", repo / "services"], contract=load_contract(repo / "docs/openapi/visione.yaml"), title="Visione API", version="0.1.0", server="http://localhost:5555", ) self.assertIn("/api/v1/reports/{id}/media", spec["paths"]) self.assertFalse(any("/photos" in path for path in spec["paths"])) for path, path_item in spec["paths"].items(): for method, operation in path_item.items(): if method == "get": self.assertNotIn("requestBody", operation, path) success = next( response for status, response in operation["responses"].items() if str(status).startswith("2") ) self.assertTrue(success.get("content"), operation["operationId"]) json.dumps(spec) if __name__ == "__main__": unittest.main()