82 lines
2.4 KiB
Python
82 lines
2.4 KiB
Python
|
|
"""Tests for Python syntax validation in execute_code."""
|
||
|
|
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
# Import the validation function directly
|
||
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||
|
|
from tools.code_execution_tool import _validate_python_syntax
|
||
|
|
|
||
|
|
|
||
|
|
class TestValidatePythonSyntax:
|
||
|
|
"""Test _validate_python_syntax catches errors before subprocess spawn."""
|
||
|
|
|
||
|
|
def test_valid_code_returns_none(self):
|
||
|
|
assert _validate_python_syntax("print('hello')") is None
|
||
|
|
|
||
|
|
def test_valid_multiline_returns_none(self):
|
||
|
|
code = """
|
||
|
|
import os
|
||
|
|
def foo():
|
||
|
|
return 42
|
||
|
|
result = foo()
|
||
|
|
"""
|
||
|
|
assert _validate_python_syntax(code) is None
|
||
|
|
|
||
|
|
def test_syntax_error_detected(self):
|
||
|
|
result = _validate_python_syntax("def foo(
|
||
|
|
")
|
||
|
|
assert result is not None
|
||
|
|
data = json.loads(result)
|
||
|
|
assert data["syntax_error"] is True
|
||
|
|
assert "line" in data
|
||
|
|
assert "message" in data
|
||
|
|
|
||
|
|
def test_missing_colon(self):
|
||
|
|
result = _validate_python_syntax("def foo()
|
||
|
|
pass")
|
||
|
|
data = json.loads(result)
|
||
|
|
assert data["syntax_error"] is True
|
||
|
|
assert data["line"] == 1
|
||
|
|
|
||
|
|
def test_unmatched_paren(self):
|
||
|
|
result = _validate_python_syntax("print('hello'")
|
||
|
|
data = json.loads(result)
|
||
|
|
assert data["syntax_error"] is True
|
||
|
|
|
||
|
|
def test_indentation_error(self):
|
||
|
|
result = _validate_python_syntax("def foo():
|
||
|
|
pass")
|
||
|
|
data = json.loads(result)
|
||
|
|
assert data["syntax_error"] is True
|
||
|
|
assert data["line"] == 2
|
||
|
|
|
||
|
|
def test_invalid_character(self):
|
||
|
|
result = _validate_python_syntax("x = 5 √ 2")
|
||
|
|
data = json.loads(result)
|
||
|
|
assert data["syntax_error"] is True
|
||
|
|
|
||
|
|
def test_error_format_has_required_fields(self):
|
||
|
|
result = _validate_python_syntax("def(
|
||
|
|
")
|
||
|
|
data = json.loads(result)
|
||
|
|
assert "error" in data
|
||
|
|
assert "syntax_error" in data
|
||
|
|
assert "line" in data
|
||
|
|
assert "offset" in data
|
||
|
|
assert "message" in data
|
||
|
|
|
||
|
|
def test_empty_string_returns_none(self):
|
||
|
|
# Empty code is caught by the guard before validation
|
||
|
|
# But if called directly, ast.parse("") is valid
|
||
|
|
assert _validate_python_syntax("") is None
|
||
|
|
|
||
|
|
def test_comment_only_returns_none(self):
|
||
|
|
assert _validate_python_syntax("# just a comment") is None
|
||
|
|
|
||
|
|
def test_complex_valid_code(self):
|
||
|
|
code =
|