Spaces:
Paused
Paused
| """SPEC-4 §7: 反例验证 — 证明 hygiene 守卫真能拦下违规代码。 | |
| 通过把违规代码字符串直接 ast.parse 后跑相同的 AST 规则,断言它们被识别为 | |
| 违规。该测试不依赖 SRC_ROOT,默认进 CI 跑;它独立于主 hygiene 测试运行, | |
| 因此不会污染主测试的 SRC_ROOT 路径。 | |
| """ | |
| from __future__ import annotations | |
| import ast | |
| import textwrap | |
| from autoteam._playwright_guard import ( | |
| ALLOWED_SYNC_NAMES, | |
| FORBIDDEN_MODULES, | |
| ) | |
| PLAYWRIGHT_NAMESPACE = ALLOWED_SYNC_NAMES | {"async_playwright"} | |
| def _has_forbidden_import(tree: ast.AST) -> bool: | |
| for node in ast.walk(tree): | |
| if isinstance(node, ast.ImportFrom) and node.module in FORBIDDEN_MODULES: | |
| return True | |
| if isinstance(node, ast.Import): | |
| for alias in node.names: | |
| if alias.name in FORBIDDEN_MODULES: | |
| return True | |
| return False | |
| def _has_playwright_in_async_def(tree: ast.AST) -> bool: | |
| for fn in ast.walk(tree): | |
| if not isinstance(fn, ast.AsyncFunctionDef): | |
| continue | |
| for child in ast.walk(fn): | |
| name = getattr(child, "id", None) or getattr(child, "attr", None) | |
| if name in PLAYWRIGHT_NAMESPACE: | |
| return True | |
| return False | |
| def _has_non_whitelist_sync_import(tree: ast.AST) -> bool: | |
| for node in ast.walk(tree): | |
| if isinstance(node, ast.ImportFrom) and node.module == "playwright.sync_api": | |
| for alias in node.names: | |
| if alias.name not in ALLOWED_SYNC_NAMES: | |
| return True | |
| return False | |
| def test_forbidden_async_api_import_is_detected(): | |
| src = "from playwright.async_api import async_playwright\n" | |
| tree = ast.parse(src) | |
| assert _has_forbidden_import(tree), "async_api import 应当被守卫识别" | |
| def test_async_def_with_sync_playwright_is_detected(): | |
| src = textwrap.dedent("""\ | |
| from playwright.sync_api import sync_playwright | |
| async def evil_handler(): | |
| with sync_playwright() as p: | |
| p.chromium.launch() | |
| """) | |
| tree = ast.parse(src) | |
| assert _has_playwright_in_async_def(tree), "async def 内 sync_playwright 应被识别" | |
| def test_async_def_with_page_attribute_is_detected(): | |
| src = textwrap.dedent("""\ | |
| async def evil_handler(browser): | |
| page = await browser.new_page() | |
| return Page # noqa | |
| """) | |
| tree = ast.parse(src) | |
| assert _has_playwright_in_async_def(tree), "async def 内引用 Page 应被识别" | |
| def test_non_whitelist_sync_import_is_detected(): | |
| src = "from playwright.sync_api import Request\n" | |
| tree = ast.parse(src) | |
| assert _has_non_whitelist_sync_import(tree), "白名单外符号应被识别" | |
| def test_clean_file_passes_all_checks(): | |
| """正向反例: 合法的 sync 用法不应被任一规则误伤""" | |
| src = textwrap.dedent("""\ | |
| from playwright.sync_api import sync_playwright, Page, Browser | |
| def login_flow(): | |
| with sync_playwright() as p: | |
| browser: Browser = p.chromium.launch() | |
| page: Page = browser.new_page() | |
| page.goto("https://example.com") | |
| browser.close() | |
| """) | |
| tree = ast.parse(src) | |
| assert not _has_forbidden_import(tree) | |
| assert not _has_playwright_in_async_def(tree) | |
| assert not _has_non_whitelist_sync_import(tree) | |