From 7f695928c39d9cd770b4ad9a11a5899d0eeda7aa Mon Sep 17 00:00:00 2001 From: OoO Date: Thu, 30 Apr 2026 10:12:43 +0800 Subject: [PATCH] =?UTF-8?q?test(app):=20=E9=8E=96=E5=AE=9A=E5=95=9F?= =?UTF-8?q?=E5=8B=95=E8=87=AA=E6=AA=A2=E8=88=87=E6=A8=A1=E6=9D=BF=E5=A5=91?= =?UTF-8?q?=E7=B4=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_app_startup_contracts.py | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/test_app_startup_contracts.py diff --git a/tests/test_app_startup_contracts.py b/tests/test_app_startup_contracts.py new file mode 100644 index 0000000..f1ef5f0 --- /dev/null +++ b/tests/test_app_startup_contracts.py @@ -0,0 +1,63 @@ +import ast +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def _python_files_to_scan(): + return [ROOT / "app.py", *sorted((ROOT / "routes").glob("*.py"))] + + +def test_app_defines_and_runs_metadata_self_check(): + app_source = (ROOT / "app.py").read_text(encoding="utf-8") + + assert "EXPECTED_METADATA_TABLES" in app_source + assert "def verify_metadata_tables()" in app_source + assert "Base.metadata.tables.keys()" in app_source + assert "realtime_sales_monthly" in app_source + assert "verify_metadata_tables()" in app_source + + +def test_app_defines_and_runs_duplicate_route_self_check(): + app_source = (ROOT / "app.py").read_text(encoding="utf-8") + + assert "def verify_unique_routes()" in app_source + assert "app.url_map.iter_rules()" in app_source + assert "frozenset(rule.methods - {'HEAD', 'OPTIONS'})" in app_source + assert "verify_unique_routes()" in app_source + + +def test_literal_render_templates_exist(): + missing = [] + + for path in _python_files_to_scan(): + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + for node in ast.walk(tree): + if not isinstance(node, ast.Call): + continue + func = node.func + if not isinstance(func, ast.Name) or func.id != "render_template": + continue + if not node.args or not isinstance(node.args[0], ast.Constant): + continue + template_name = node.args[0].value + if not isinstance(template_name, str): + continue + + candidates = [ + ROOT / "templates" / template_name, + ROOT / "web" / "templates" / template_name, + ] + if not any(candidate.exists() for candidate in candidates): + missing.append(f"{path.relative_to(ROOT)}: {template_name}") + + assert missing == [] + + +def test_vendor_blueprint_uses_web_templates_absolute_folder(): + source = (ROOT / "routes" / "vendor_routes.py").read_text(encoding="utf-8") + + assert "os.path.dirname(os.path.dirname(os.path.abspath(__file__)))" in source + assert "os.path.join(_base_dir, 'web', 'templates')" in source + assert "template_folder=_template_folder" in source