- Fixed pyproject.toml to point to correct source directory (../src) - Fixed import sorting and whitespace issues in sample_code.py - Fixed linting issues in test_full_analysis.py (unused variables, whitespace) - Renamed unused loop variable in test_language_detector.py
This commit is contained in:
328
tests/integration/test_full_analysis.py
Normal file
328
tests/integration/test_full_analysis.py
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
"""Integration tests for full analysis workflow."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from codesnap.core.analyzer import CodeAnalyzer
|
||||||
|
from codesnap.core.language_detector import detect_language
|
||||||
|
from codesnap.output.json_exporter import export_json
|
||||||
|
from codesnap.output.llm_exporter import export_llm_optimized
|
||||||
|
from codesnap.output.markdown_exporter import export_markdown
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_python_project(tmp_path):
|
||||||
|
"""Create a sample Python project for testing."""
|
||||||
|
main_py = tmp_path / "main.py"
|
||||||
|
main_py.write_text('''
|
||||||
|
"""Main module for the application."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from utils import helper
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main entry point."""
|
||||||
|
print("Hello, World!")
|
||||||
|
helper.process()
|
||||||
|
|
||||||
|
|
||||||
|
class Application:
|
||||||
|
"""Main application class."""
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Run the application."""
|
||||||
|
if self.config.debug:
|
||||||
|
print("Debug mode enabled")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class Database:
|
||||||
|
"""Database connection class."""
|
||||||
|
|
||||||
|
def __init__(self, host, port):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"""Establish database connection."""
|
||||||
|
return "Connected"
|
||||||
|
|
||||||
|
def query(self, sql):
|
||||||
|
"""Execute a query."""
|
||||||
|
if not sql:
|
||||||
|
raise ValueError("SQL query cannot be empty")
|
||||||
|
return ["result1", "result2"]
|
||||||
|
''')
|
||||||
|
|
||||||
|
utils_py = tmp_path / "utils.py"
|
||||||
|
utils_py.write_text('''
|
||||||
|
"""Utility functions module."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def process():
|
||||||
|
"""Process data."""
|
||||||
|
return "processed"
|
||||||
|
|
||||||
|
|
||||||
|
def helper(x: int, y: int) -> int:
|
||||||
|
"""Helper function for calculations."""
|
||||||
|
if x > 0:
|
||||||
|
return x + y
|
||||||
|
elif x < 0:
|
||||||
|
return x - y
|
||||||
|
else:
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
class Calculator:
|
||||||
|
"""Calculator class."""
|
||||||
|
|
||||||
|
def add(self, a, b):
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
def multiply(self, a, b):
|
||||||
|
return a * b
|
||||||
|
''')
|
||||||
|
|
||||||
|
return tmp_path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_multilang_project(tmp_path):
|
||||||
|
"""Create a multi-language project for testing."""
|
||||||
|
python_file = tmp_path / "processor.py"
|
||||||
|
python_file.write_text('''
|
||||||
|
from js_utils import process_js
|
||||||
|
import json
|
||||||
|
|
||||||
|
def handle_data(data):
|
||||||
|
return json.dumps(process_js(data))
|
||||||
|
''')
|
||||||
|
|
||||||
|
js_file = tmp_path / "js_utils.js"
|
||||||
|
js_file.write_text('''
|
||||||
|
function process_js(data) {
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
return data.map(x => x * 2);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { process_js };
|
||||||
|
''')
|
||||||
|
|
||||||
|
go_file = tmp_path / "main.go"
|
||||||
|
go_file.write_text('''
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello from Go")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Process() string {
|
||||||
|
return "processed"
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
|
||||||
|
return tmp_path
|
||||||
|
|
||||||
|
|
||||||
|
def check_parser_available(language="python"):
|
||||||
|
"""Check if tree-sitter parser is available for a language."""
|
||||||
|
try:
|
||||||
|
from codesnap.core.parser import TreeSitterParser
|
||||||
|
_ = TreeSitterParser()
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class TestFullAnalysis:
|
||||||
|
"""Integration tests for full analysis workflow."""
|
||||||
|
|
||||||
|
def test_analyze_python_project(self, sample_python_project):
|
||||||
|
"""Test analyzing a Python project."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100, enable_complexity=True)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
assert result.summary["total_files"] == 2
|
||||||
|
if result.error_count == 0:
|
||||||
|
assert result.summary["total_functions"] >= 4
|
||||||
|
assert result.summary["total_classes"] >= 2
|
||||||
|
|
||||||
|
def test_analyze_multilang_project(self, sample_multilang_project):
|
||||||
|
"""Test analyzing a multi-language project."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100, enable_complexity=False)
|
||||||
|
result = analyzer.analyze(sample_multilang_project)
|
||||||
|
|
||||||
|
assert result.summary["total_files"] == 3
|
||||||
|
languages = result.summary.get("languages", {})
|
||||||
|
assert "python" in languages
|
||||||
|
assert "javascript" in languages
|
||||||
|
assert "go" in languages
|
||||||
|
|
||||||
|
def test_json_export(self, sample_python_project):
|
||||||
|
"""Test JSON export functionality."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
json_output = export_json(result, sample_python_project)
|
||||||
|
data = json.loads(json_output)
|
||||||
|
|
||||||
|
assert "metadata" in data
|
||||||
|
assert "summary" in data
|
||||||
|
assert "files" in data
|
||||||
|
assert len(data["files"]) == 2
|
||||||
|
|
||||||
|
def test_markdown_export(self, sample_python_project):
|
||||||
|
"""Test Markdown export functionality."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
md_output = export_markdown(result, sample_python_project)
|
||||||
|
|
||||||
|
assert "# CodeSnap Analysis Report" in md_output
|
||||||
|
assert "## Summary" in md_output
|
||||||
|
assert "## File Structure" in md_output
|
||||||
|
assert "main.py" in md_output
|
||||||
|
|
||||||
|
def test_llm_export(self, sample_python_project):
|
||||||
|
"""Test LLM-optimized export functionality."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
llm_output = export_llm_optimized(result, sample_python_project, max_tokens=1000)
|
||||||
|
|
||||||
|
assert "## CODEBASE ANALYSIS SUMMARY" in llm_output
|
||||||
|
assert "### STRUCTURE" in llm_output
|
||||||
|
|
||||||
|
def test_dependency_detection(self, sample_python_project):
|
||||||
|
"""Test dependency detection."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100, enable_complexity=False)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
if result.error_count == 0:
|
||||||
|
assert len(result.dependencies) >= 0
|
||||||
|
|
||||||
|
dep_sources = [d["source"] for d in result.dependencies]
|
||||||
|
assert any("main.py" in src for src in dep_sources)
|
||||||
|
|
||||||
|
def test_complexity_analysis(self, sample_python_project):
|
||||||
|
"""Test complexity analysis."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100, enable_complexity=True)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
files_with_complexity = [f for f in result.files if f.complexity]
|
||||||
|
|
||||||
|
if result.error_count == 0:
|
||||||
|
assert len(files_with_complexity) > 0
|
||||||
|
|
||||||
|
for fa in files_with_complexity:
|
||||||
|
assert fa.complexity.cyclomatic_complexity >= 1
|
||||||
|
assert fa.complexity.nesting_depth >= 0
|
||||||
|
|
||||||
|
def test_ignore_patterns(self, sample_python_project):
|
||||||
|
"""Test ignore patterns functionality."""
|
||||||
|
ignore_analyzer = CodeAnalyzer(
|
||||||
|
max_files=100,
|
||||||
|
ignore_patterns=["utils.py"],
|
||||||
|
enable_complexity=False
|
||||||
|
)
|
||||||
|
result = ignore_analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
file_names = [f.path.name for f in result.files]
|
||||||
|
assert "utils.py" not in file_names
|
||||||
|
assert "main.py" in file_names
|
||||||
|
|
||||||
|
def test_max_files_limit(self, sample_python_project):
|
||||||
|
"""Test max files limit."""
|
||||||
|
limited_analyzer = CodeAnalyzer(max_files=1)
|
||||||
|
result = limited_analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
assert len(result.files) <= 1
|
||||||
|
|
||||||
|
def test_orphaned_file_detection(self, sample_python_project):
|
||||||
|
"""Test orphaned file detection."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100, enable_complexity=False)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
orphaned = result.metrics.get("orphaned_files", [])
|
||||||
|
if result.error_count == 0:
|
||||||
|
assert len(orphaned) == 0
|
||||||
|
|
||||||
|
def test_graph_builder(self, sample_python_project):
|
||||||
|
"""Test graph builder functionality."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100, enable_complexity=False)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
assert analyzer.graph_builder.graph.number_of_nodes() >= 1
|
||||||
|
if result.error_count == 0:
|
||||||
|
assert analyzer.graph_builder.graph.number_of_edges() >= 1
|
||||||
|
|
||||||
|
def test_language_detection_integration(self, sample_python_project):
|
||||||
|
"""Test language detection integration."""
|
||||||
|
python_file = sample_python_project / "main.py"
|
||||||
|
content = python_file.read_text()
|
||||||
|
|
||||||
|
lang = detect_language(python_file, content)
|
||||||
|
assert lang == "python"
|
||||||
|
|
||||||
|
def test_multiple_output_formats(self, sample_python_project):
|
||||||
|
"""Test that all output formats work together."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100, enable_complexity=True)
|
||||||
|
result = analyzer.analyze(sample_python_project)
|
||||||
|
|
||||||
|
json_output = export_json(result, sample_python_project)
|
||||||
|
md_output = export_markdown(result, sample_python_project)
|
||||||
|
llm_output = export_llm_optimized(result, sample_python_project)
|
||||||
|
|
||||||
|
assert len(json_output) > 0
|
||||||
|
assert len(md_output) > 0
|
||||||
|
assert len(llm_output) > 0
|
||||||
|
|
||||||
|
json_data = json.loads(json_output)
|
||||||
|
assert json_data["summary"]["total_files"] == result.summary["total_files"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestEdgeCases:
|
||||||
|
"""Test edge cases in analysis."""
|
||||||
|
|
||||||
|
def test_empty_directory(self, tmp_path):
|
||||||
|
"""Test analyzing an empty directory."""
|
||||||
|
analyzer = CodeAnalyzer(max_files=100)
|
||||||
|
result = analyzer.analyze(tmp_path)
|
||||||
|
|
||||||
|
assert result.summary["total_files"] == 0
|
||||||
|
assert result.error_count == 0
|
||||||
|
|
||||||
|
def test_single_file(self, tmp_path):
|
||||||
|
"""Test analyzing a single file."""
|
||||||
|
test_file = tmp_path / "single.py"
|
||||||
|
test_file.write_text("x = 1\nprint(x)")
|
||||||
|
|
||||||
|
analyzer = CodeAnalyzer(max_files=100)
|
||||||
|
result = analyzer.analyze(tmp_path)
|
||||||
|
|
||||||
|
assert result.summary["total_files"] >= 1
|
||||||
|
|
||||||
|
def test_unsupported_file_types(self, tmp_path):
|
||||||
|
"""Test handling of unsupported file types."""
|
||||||
|
text_file = tmp_path / "readme.txt"
|
||||||
|
text_file.write_text("This is a readme file")
|
||||||
|
|
||||||
|
analyzer = CodeAnalyzer(max_files=100)
|
||||||
|
result = analyzer.analyze(tmp_path)
|
||||||
|
|
||||||
|
assert len(result.files) == 0 or all(
|
||||||
|
f.language == "unknown" for f in result.files
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user