"""Tests for code analyzers.""" import tempfile from pathlib import Path from src.auto_readme.analyzers import ( PythonAnalyzer, JavaScriptAnalyzer, GoAnalyzer, RustAnalyzer, CodeAnalyzerFactory, ) class TestPythonAnalyzer: """Tests for PythonAnalyzer.""" def test_can_analyze_py_file(self): """Test that analyzer recognizes Python files.""" analyzer = PythonAnalyzer() with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f: f.write("def hello():\n pass") f.flush() assert analyzer.can_analyze(Path(f.name)) Path(f.name).unlink() def test_can_analyze_pyi_file(self): """Test that analyzer recognizes .pyi stub files.""" analyzer = PythonAnalyzer() with tempfile.NamedTemporaryFile(suffix=".pyi", delete=False, mode="w") as f: f.write("def hello() -> None: ...") f.flush() assert analyzer.can_analyze(Path(f.name)) Path(f.name).unlink() def test_analyze_simple_function(self): """Test analyzing a simple function.""" with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f: f.write('''"""Module docstring.""" def hello(): """Say hello.""" print("Hello, World!") class Calculator: """A calculator class.""" def add(self, a, b): """Add two numbers.""" return a + b ''') f.flush() file_path = Path(f.name) analyzer = PythonAnalyzer() result = analyzer.analyze(file_path) assert len(result["functions"]) == 2 # hello and add assert len(result["classes"]) == 1 hello_func = next((f for f in result["functions"] if f.name == "hello"), None) assert hello_func is not None assert hello_func.docstring == "Say hello." assert hello_func.parameters == [] calc_class = result["classes"][0] assert calc_class.name == "Calculator" assert calc_class.docstring == "A calculator class." Path(f.name).unlink() def test_analyze_with_parameters(self): """Test analyzing functions with parameters.""" with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f: f.write('''def greet(name, greeting="Hello"): """Greet someone.""" return f"{greeting}, {name}!" def add_numbers(a: int, b: int) -> int: """Add two integers.""" return a + b ''') f.flush() file_path = Path(f.name) analyzer = PythonAnalyzer() result = analyzer.analyze(file_path) greet_func = next((f for f in result["functions"] if f.name == "greet"), None) assert greet_func is not None assert "name" in greet_func.parameters assert "greeting" in greet_func.parameters Path(f.name).unlink() class TestJavaScriptAnalyzer: """Tests for JavaScriptAnalyzer.""" def test_can_analyze_js_file(self): """Test that analyzer recognizes JavaScript files.""" analyzer = JavaScriptAnalyzer() with tempfile.NamedTemporaryFile(suffix=".js", delete=False, mode="w") as f: f.write("function hello() { return 'hello'; }") f.flush() assert analyzer.can_analyze(Path(f.name)) Path(f.name).unlink() def test_can_analyze_ts_file(self): """Test that analyzer recognizes TypeScript files.""" analyzer = JavaScriptAnalyzer() with tempfile.NamedTemporaryFile(suffix=".ts", delete=False, mode="w") as f: f.write("const hello = (): string => 'hello';") f.flush() assert analyzer.can_analyze(Path(f.name)) Path(f.name).unlink() def test_analyze_simple_function(self): """Test analyzing a simple JavaScript function.""" with tempfile.NamedTemporaryFile(suffix=".js", delete=False, mode="w") as f: f.write('''function hello(name) { return "Hello, " + name + "!"; } class Calculator { add(a, b) { return a + b; } } module.exports = { hello, Calculator }; ''') f.flush() file_path = Path(f.name) analyzer = JavaScriptAnalyzer() result = analyzer.analyze(file_path) assert len(result["functions"]) >= 1 hello_func = next((f for f in result["functions"] if f.name == "hello"), None) assert hello_func is not None Path(f.name).unlink() class TestGoAnalyzer: """Tests for GoAnalyzer.""" def test_can_analyze_go_file(self): """Test that analyzer recognizes Go files.""" analyzer = GoAnalyzer() with tempfile.NamedTemporaryFile(suffix=".go", delete=False, mode="w") as f: f.write("package main\n\nfunc hello() string { return 'hello' }") f.flush() assert analyzer.can_analyze(Path(f.name)) Path(f.name).unlink() def test_analyze_simple_function(self): """Test analyzing a simple Go function.""" with tempfile.NamedTemporaryFile(suffix=".go", delete=False, mode="w") as f: f.write('''package main import "fmt" func hello(name string) string { return "Hello, " + name } type Calculator struct{} func (c *Calculator) Add(a, b int) int { return a + b } ''') f.flush() file_path = Path(f.name) analyzer = GoAnalyzer() result = analyzer.analyze(file_path) hello_func = next((f for f in result["functions"] if f.name == "hello"), None) assert hello_func is not None assert hello_func.return_type is not None or "string" in str(hello_func.return_type) Path(f.name).unlink() class TestRustAnalyzer: """Tests for RustAnalyzer.""" def test_can_analyze_rs_file(self): """Test that analyzer recognizes Rust files.""" analyzer = RustAnalyzer() with tempfile.NamedTemporaryFile(suffix=".rs", delete=False, mode="w") as f: f.write("fn hello() -> String { String::from('hello') }") f.flush() assert analyzer.can_analyze(Path(f.name)) Path(f.name).unlink() def test_analyze_simple_function(self): """Test analyzing a simple Rust function.""" with tempfile.NamedTemporaryFile(suffix=".rs", delete=False, mode="w") as f: f.write('''fn hello(name: &str) -> String { format!("Hello, {}", name) } pub struct Calculator; impl Calculator { pub fn add(a: i32, b: i32) -> i32 { a + b } } ''') f.flush() file_path = Path(f.name) analyzer = RustAnalyzer() result = analyzer.analyze(file_path) hello_func = next((f for f in result["functions"] if f.name == "hello"), None) assert hello_func is not None assert "name" in hello_func.parameters assert hello_func.visibility == "private" Path(f.name).unlink() class TestCodeAnalyzerFactory: """Tests for CodeAnalyzerFactory.""" def test_get_analyzer_python(self): """Test getting analyzer for Python file.""" analyzer = CodeAnalyzerFactory.get_analyzer(Path("main.py")) assert isinstance(analyzer, PythonAnalyzer) def test_get_analyzer_js(self): """Test getting analyzer for JavaScript file.""" analyzer = CodeAnalyzerFactory.get_analyzer(Path("index.js")) assert isinstance(analyzer, JavaScriptAnalyzer) def test_get_analyzer_go(self): """Test getting analyzer for Go file.""" analyzer = CodeAnalyzerFactory.get_analyzer(Path("main.go")) assert isinstance(analyzer, GoAnalyzer) def test_get_analyzer_rust(self): """Test getting analyzer for Rust file.""" analyzer = CodeAnalyzerFactory.get_analyzer(Path("main.rs")) assert isinstance(analyzer, RustAnalyzer) def test_can_analyze(self): """Test can_analyze returns correct results.""" assert CodeAnalyzerFactory.can_analyze(Path("main.py")) assert CodeAnalyzerFactory.can_analyze(Path("index.js")) assert CodeAnalyzerFactory.can_analyze(Path("main.go")) assert CodeAnalyzerFactory.can_analyze(Path("main.rs")) assert not CodeAnalyzerFactory.can_analyze(Path("random.txt"))