from __future__ import annotations import importlib.util import tempfile import unittest from pathlib import Path SCRIPT_PATH = Path(__file__).with_name("generate_openapi.py") SPEC = importlib.util.spec_from_file_location("generate_openapi", SCRIPT_PATH) generate_openapi = importlib.util.module_from_spec(SPEC) assert SPEC.loader is not None SPEC.loader.exec_module(generate_openapi) class GenerateOpenApiTest(unittest.TestCase): def test_extracts_drogon_route(self) -> None: with tempfile.TemporaryDirectory() as tmp: controllers = Path(tmp) (controllers / "SampleController.h").write_text( """ class SampleController { METHOD_LIST_BEGIN ADD_METHOD_TO(SampleController::detail, "/api/v1/samples/{1}", drogon::Get); METHOD_LIST_END }; """, encoding="utf-8", ) routes = generate_openapi.parse_routes(controllers) operation = routes[("/api/v1/samples/{id}", "get")] self.assertEqual(operation["operationId"], "SampleController.detail") self.assertEqual(operation["parameters"][0]["name"], "id") def test_names_secondary_path_parameters_from_previous_segment(self) -> None: with tempfile.TemporaryDirectory() as tmp: controllers = Path(tmp) (controllers / "SampleController.h").write_text( """ class SampleController { METHOD_LIST_BEGIN ADD_METHOD_TO(SampleController::flagMessage, "/api/v1/reports/{1}/messages/{2}/flags", drogon::Post); METHOD_LIST_END }; """, encoding="utf-8", ) routes = generate_openapi.parse_routes(controllers) operation = routes[ ("/api/v1/reports/{id}/messages/{message_id}/flags", "post") ] self.assertEqual(operation["parameters"][1]["name"], "message_id") def test_manual_operation_overrides_generated_defaults(self) -> None: generated = { ("/api/v1/samples", "get"): { "tags": ["Sample"], "operationId": "SampleController.list", "summary": "List", "parameters": [], "responses": {"200": {"description": "Response"}}, } } manual = { "openapi": "3.0.3", "info": {"title": "Manual", "version": "1.0.0"}, "paths": { "/api/v1/samples": { "get": { "summary": "Manual summary", "responses": {"200": {"description": "Manual response"}}, } } }, } spec = generate_openapi.merge_openapi( generated, manual, "Generated", "0.1.0", "http://localhost" ) operation = spec["paths"]["/api/v1/samples"]["get"] self.assertEqual(operation["summary"], "Manual summary") self.assertEqual(operation["operationId"], "SampleController.list") def test_stale_manual_operation_fails_unless_marked_manual_only(self) -> None: manual = { "paths": { "/api/v1/stale": { "get": {"responses": {"200": {"description": "Stale"}}} } } } with self.assertRaisesRegex(ValueError, "no matching Drogon route"): generate_openapi.merge_openapi({}, manual, "API", "0.1.0", "http://x") manual["paths"]["/api/v1/stale"]["get"]["x-manual-only"] = True spec = generate_openapi.merge_openapi({}, manual, "API", "0.1.0", "http://x") self.assertIn("/api/v1/stale", spec["paths"]) self.assertNotIn("x-manual-only", spec["paths"]["/api/v1/stale"]["get"]) if __name__ == "__main__": unittest.main()