Files
backend/scripts/swagger-drogon/tests/test_generator.py
T

171 lines
6.6 KiB
Python

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()