106 lines
4.0 KiB
Python
106 lines
4.0 KiB
Python
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()
|