fix route parity parser for promise-all frontend calls
This commit is contained in:
@@ -19,6 +19,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Test Web Route Parity Parser
|
||||||
|
run: python3 -m unittest scripts/test_check_web_route_parity.py
|
||||||
|
|
||||||
- name: Check Web Route Parity
|
- name: Check Web Route Parity
|
||||||
run: python3 scripts/check_web_route_parity.py
|
run: python3 scripts/check_web_route_parity.py
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,8 @@ Route = tuple[str, str]
|
|||||||
BACKEND_ROUTE_RE = re.compile(
|
BACKEND_ROUTE_RE = re.compile(
|
||||||
r'server_\.on\(\s*"(?P<path>/api/[^"]+)"\s*,\s*HTTP_(?P<method>[A-Z]+)'
|
r'server_\.on\(\s*"(?P<path>/api/[^"]+)"\s*,\s*HTTP_(?P<method>[A-Z]+)'
|
||||||
)
|
)
|
||||||
FRONTEND_CALL_RE = re.compile(
|
FRONTEND_CALL_START_RE = re.compile(r"\brequestJson\(")
|
||||||
r'requestJson\(\s*["\'](?P<path>/api/[^"\']+)["\'](?P<args>.*?)\);',
|
FRONTEND_PATH_ARG_RE = re.compile(r'^\s*(["\'])(?P<path>/api/[^"\']+)\1')
|
||||||
re.DOTALL,
|
|
||||||
)
|
|
||||||
METHOD_RE = re.compile(r'method\s*:\s*["\'](?P<method>[A-Z]+)["\']')
|
METHOD_RE = re.compile(r'method\s*:\s*["\'](?P<method>[A-Z]+)["\']')
|
||||||
|
|
||||||
|
|
||||||
@@ -31,11 +29,54 @@ def parse_backend_routes(source: str) -> set[Route]:
|
|||||||
return routes
|
return routes
|
||||||
|
|
||||||
|
|
||||||
|
def find_matching_paren(source: str, open_paren_index: int) -> int:
|
||||||
|
depth = 1
|
||||||
|
in_string: str | None = None
|
||||||
|
escaped = False
|
||||||
|
|
||||||
|
for index in range(open_paren_index + 1, len(source)):
|
||||||
|
char = source[index]
|
||||||
|
|
||||||
|
if in_string is not None:
|
||||||
|
if escaped:
|
||||||
|
escaped = False
|
||||||
|
continue
|
||||||
|
if char == "\\":
|
||||||
|
escaped = True
|
||||||
|
continue
|
||||||
|
if char == in_string:
|
||||||
|
in_string = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
if char in ('"', "'", "`"):
|
||||||
|
in_string = char
|
||||||
|
continue
|
||||||
|
|
||||||
|
if char == "(":
|
||||||
|
depth += 1
|
||||||
|
continue
|
||||||
|
if char == ")":
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0:
|
||||||
|
return index
|
||||||
|
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
def parse_frontend_routes(source: str) -> set[Route]:
|
def parse_frontend_routes(source: str) -> set[Route]:
|
||||||
routes: set[Route] = set()
|
routes: set[Route] = set()
|
||||||
for match in FRONTEND_CALL_RE.finditer(source):
|
for match in FRONTEND_CALL_START_RE.finditer(source):
|
||||||
path = match.group("path")
|
open_paren = match.end() - 1
|
||||||
args = match.group("args")
|
close_paren = find_matching_paren(source, open_paren)
|
||||||
|
if close_paren < 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
args = source[open_paren + 1 : close_paren]
|
||||||
|
path_match = FRONTEND_PATH_ARG_RE.match(args)
|
||||||
|
if not path_match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
path = path_match.group("path")
|
||||||
method_match = METHOD_RE.search(args)
|
method_match = METHOD_RE.search(args)
|
||||||
method = method_match.group("method").upper() if method_match else "GET"
|
method = method_match.group("method").upper() if method_match else "GET"
|
||||||
routes.add((method, path))
|
routes.add((method, path))
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Unit tests for check_web_route_parity parser helpers."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from scripts.check_web_route_parity import parse_frontend_routes
|
||||||
|
|
||||||
|
|
||||||
|
class ParseFrontendRoutesTest(unittest.TestCase):
|
||||||
|
def test_detects_direct_calls(self) -> None:
|
||||||
|
source = """
|
||||||
|
async function refresh() {
|
||||||
|
await requestJson("/api/status");
|
||||||
|
await requestJson("/api/control", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ action: "CALL" }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
routes = parse_frontend_routes(source)
|
||||||
|
self.assertIn(("GET", "/api/status"), routes)
|
||||||
|
self.assertIn(("POST", "/api/control"), routes)
|
||||||
|
|
||||||
|
def test_detects_calls_inside_promise_all(self) -> None:
|
||||||
|
source = """
|
||||||
|
const [wifi, mqtt, espnow, peers] = await Promise.all([
|
||||||
|
requestJson("/api/network/wifi"),
|
||||||
|
requestJson("/api/network/mqtt"),
|
||||||
|
requestJson("/api/network/espnow"),
|
||||||
|
requestJson("/api/network/espnow/peer"),
|
||||||
|
]);
|
||||||
|
"""
|
||||||
|
routes = parse_frontend_routes(source)
|
||||||
|
self.assertIn(("GET", "/api/network/wifi"), routes)
|
||||||
|
self.assertIn(("GET", "/api/network/mqtt"), routes)
|
||||||
|
self.assertIn(("GET", "/api/network/espnow"), routes)
|
||||||
|
self.assertIn(("GET", "/api/network/espnow/peer"), routes)
|
||||||
|
|
||||||
|
def test_handles_nested_parentheses_in_payload(self) -> None:
|
||||||
|
source = """
|
||||||
|
await requestJson("/api/network/mqtt/publish", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
topic: "rtc/test",
|
||||||
|
payload: JSON.stringify({ ping: true }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
routes = parse_frontend_routes(source)
|
||||||
|
self.assertIn(("POST", "/api/network/mqtt/publish"), routes)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -12,6 +12,9 @@ mkdir -p .pio/host
|
|||||||
c++ -std=c++17 -Wall -Wextra -pedantic -Isrc test/host/test_dtmf_host.cpp src/telephony/DtmfDecoder.cpp -o .pio/host/test_dtmf_host
|
c++ -std=c++17 -Wall -Wextra -pedantic -Isrc test/host/test_dtmf_host.cpp src/telephony/DtmfDecoder.cpp -o .pio/host/test_dtmf_host
|
||||||
.pio/host/test_dtmf_host
|
.pio/host/test_dtmf_host
|
||||||
|
|
||||||
|
echo "[test_terminal] run web route parity parser tests"
|
||||||
|
python3 -m unittest scripts/test_check_web_route_parity.py
|
||||||
|
|
||||||
echo "[test_terminal] check web route parity"
|
echo "[test_terminal] check web route parity"
|
||||||
python3 scripts/check_web_route_parity.py
|
python3 scripts/check_web_route_parity.py
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user