-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtester.py
More file actions
129 lines (111 loc) · 4.59 KB
/
tester.py
File metadata and controls
129 lines (111 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""
tester.py — Tester node. No API calls — pure subprocess.
Auto-detects test runner and reports pass/fail.
Now installs package in development mode before testing.
"""
from __future__ import annotations
import re
import subprocess
import sys
from pathlib import Path
from .state import AgentState
from rich.console import Console
console = Console()
_RUNNERS = [
("pytest.ini", "python -m pytest -x -v --tb=short"),
("setup.cfg", "python -m pytest -x -v --tb=short"),
("pyproject.toml", "python -m pytest -x -v --tb=short"),
("tox.ini", "python -m tox"),
("Makefile", "make test"),
("package.json", "npm test"),
("Cargo.toml", "cargo test"),
("go.mod", "go test ./..."),
]
def _detect_cmd(repo_path: str) -> str:
root = Path(repo_path)
for fname, cmd in _RUNNERS:
if (root / fname).exists():
return cmd
return "python -m pytest -x -v --tb=short"
def _install_dependencies(repo_path: str) -> tuple[bool, str]:
"""Install the package in development mode and its dependencies."""
root = Path(repo_path)
# Check for different package management files
if (root / "pyproject.toml").exists():
# Modern Python with pyproject.toml
try:
# Try to install with pip first
result = subprocess.run(
f"cd {repo_path} && pip install -e .",
shell=True, capture_output=True, text=True, timeout=60
)
if result.returncode == 0:
return True, "Package installed successfully"
# If that fails, try installing dependencies only
result = subprocess.run(
f"cd {repo_path} && pip install .",
shell=True, capture_output=True, text=True, timeout=60
)
return result.returncode == 0, result.stdout + result.stderr
except subprocess.TimeoutExpired:
return False, "Timeout installing dependencies"
elif (root / "setup.py").exists():
# Legacy Python with setup.py
try:
result = subprocess.run(
f"cd {repo_path} && pip install -e .",
shell=True, capture_output=True, text=True, timeout=60
)
return result.returncode == 0, result.stdout + result.stderr
except subprocess.TimeoutExpired:
return False, "Timeout installing dependencies"
elif (root / "requirements.txt").exists():
# Just requirements.txt, no package
try:
result = subprocess.run(
f"cd {repo_path} && pip install -r requirements.txt",
shell=True, capture_output=True, text=True, timeout=60
)
return result.returncode == 0, result.stdout + result.stderr
except subprocess.TimeoutExpired:
return False, "Timeout installing dependencies"
# No package management files found
return True, "No dependencies to install"
def tester_node(state: AgentState) -> dict:
console.print("[bold cyan]Running tests...[/bold cyan]")
repo_path = state["repo_path"]
cmd = state.get("test_command") or _detect_cmd(repo_path)
# First, install dependencies
console.print(" [dim]Installing dependencies...[/dim]")
install_ok, install_output = _install_dependencies(repo_path)
if not install_ok:
console.print(" [yellow]Warning: Failed to install dependencies[/yellow]")
console.print(f" [dim]{install_output[:200]}[/dim]")
# Run the actual tests
try:
result = subprocess.run(
cmd, shell=True, cwd=repo_path,
capture_output=True, text=True, timeout=120,
)
output = result.stdout + result.stderr
exit_code = result.returncode
except subprocess.TimeoutExpired:
output = "ERROR: tests timed out after 120s"
exit_code = 1
passed = (exit_code == 0)
# Extra check: even exit 0 with "failed" text = failure
if passed and re.search(r"\d+ failed", output):
passed = False
status = "[green]PASSED ✓[/green]" if passed else "[red]FAILED ✗[/red]"
console.print(f" Tests: {status}")
if not passed:
# Show last 20 lines of output for quick diagnosis
lines = [l for l in output.splitlines() if l.strip()][-20:]
if lines:
console.print("[dim]" + "\n".join(f" {l}" for l in lines) + "[/dim]")
return {
"test_output": output[:4000],
"test_passed": passed,
"retry_count": state.get("retry_count", 0),
"total_tokens": state.get("total_tokens", 0),
}