|
14 | 14 | from __future__ import annotations |
15 | 15 |
|
16 | 16 | import ast |
| 17 | +import importlib |
17 | 18 | from pathlib import Path |
18 | 19 |
|
19 | 20 | import pytest |
@@ -47,6 +48,32 @@ def _render_project(tmp_path: Path, template_type: TemplateType, use_uv: bool = |
47 | 48 | return tmp_path / context["project_name"] |
48 | 49 |
|
49 | 50 |
|
| 51 | +def _agentex_imports(source: str, filename: str) -> list[tuple[str, str]]: |
| 52 | + """(module, name) for every `from agentex... import name` in the source.""" |
| 53 | + found: list[tuple[str, str]] = [] |
| 54 | + for node in ast.walk(ast.parse(source, filename=filename)): |
| 55 | + if isinstance(node, ast.ImportFrom) and node.module and node.module.startswith("agentex"): |
| 56 | + found.extend((node.module, alias.name) for alias in node.names) |
| 57 | + return found |
| 58 | + |
| 59 | + |
| 60 | +@pytest.mark.parametrize("template_type", list(TemplateType)) |
| 61 | +def test_all_templates_import_existing_agentex_symbols(tmp_path: Path, template_type: TemplateType): |
| 62 | + """Every `from agentex... import X` a template renders resolves at import time. |
| 63 | +
|
| 64 | + Parsing alone lets a template keep naming a symbol the SDK removed, so every scaffolded |
| 65 | + agent fails on its first start instead of here. |
| 66 | + """ |
| 67 | + project_dir = _render_project(tmp_path, template_type) |
| 68 | + |
| 69 | + missing: list[str] = [] |
| 70 | + for py_file in project_dir.rglob("*.py"): |
| 71 | + for module, name in _agentex_imports(py_file.read_text(), str(py_file)): |
| 72 | + if not hasattr(importlib.import_module(module), name): |
| 73 | + missing.append(f"{py_file.relative_to(project_dir)}: from {module} import {name}") |
| 74 | + assert not missing, "\n".join(missing) |
| 75 | + |
| 76 | + |
50 | 77 | @pytest.mark.parametrize("template_type", list(TemplateType)) |
51 | 78 | def test_all_templates_render_to_valid_python(tmp_path: Path, template_type: TemplateType): |
52 | 79 | """Every template renders, and every rendered .py file is syntactically valid.""" |
|
0 commit comments