55 lines
1.9 KiB
Python
55 lines
1.9 KiB
Python
import ast
|
|
from pathlib import Path
|
|
|
|
|
|
def test_wndb_editor_routes_are_synchronous() -> None:
|
|
"""Sync psycopg-backed routes must let FastAPI schedule them in its thread pool."""
|
|
endpoints = Path(__file__).resolve().parents[2] / "app" / "api" / "v1" / "endpoints"
|
|
route_roots = [endpoints / "network", endpoints / "components"]
|
|
async_routes: list[str] = []
|
|
|
|
for root in route_roots:
|
|
for path in root.glob("*.py"):
|
|
tree = ast.parse(path.read_text(encoding="utf-8"))
|
|
for node in tree.body:
|
|
if not isinstance(node, ast.AsyncFunctionDef):
|
|
continue
|
|
is_route = any(
|
|
isinstance(decorator, ast.Call)
|
|
and isinstance(decorator.func, ast.Attribute)
|
|
and isinstance(decorator.func.value, ast.Name)
|
|
and decorator.func.value.id == "router"
|
|
for decorator in node.decorator_list
|
|
)
|
|
if is_route:
|
|
async_routes.append(f"{path.name}:{node.lineno}:{node.name}")
|
|
|
|
assert async_routes == []
|
|
|
|
|
|
def test_simulation_routes_are_synchronous() -> None:
|
|
"""EPANET and synchronous WNDB work must run in FastAPI's thread pool."""
|
|
path = (
|
|
Path(__file__).resolve().parents[2]
|
|
/ "app"
|
|
/ "api"
|
|
/ "v1"
|
|
/ "endpoints"
|
|
/ "simulation.py"
|
|
)
|
|
tree = ast.parse(path.read_text(encoding="utf-8"))
|
|
async_routes = [
|
|
node.name
|
|
for node in tree.body
|
|
if isinstance(node, ast.AsyncFunctionDef)
|
|
and any(
|
|
isinstance(decorator, ast.Call)
|
|
and isinstance(decorator.func, ast.Attribute)
|
|
and isinstance(decorator.func.value, ast.Name)
|
|
and decorator.func.value.id == "router"
|
|
for decorator in node.decorator_list
|
|
)
|
|
]
|
|
|
|
assert async_routes == []
|