262 lines
8.8 KiB
Python
262 lines
8.8 KiB
Python
"""Tests for reporter modules."""
|
|
|
|
import json
|
|
|
|
from repohealth.models.file_stats import FileAnalysis
|
|
from repohealth.models.result import RepositoryResult
|
|
from repohealth.reporters.html_reporter import HTMLReporter
|
|
from repohealth.reporters.json_reporter import JSONReporter
|
|
|
|
|
|
class TestJSONReporter:
|
|
"""Tests for JSONReporter."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures."""
|
|
self.reporter = JSONReporter()
|
|
self.sample_result = RepositoryResult(
|
|
repository_path="/test/repo",
|
|
files_analyzed=10,
|
|
total_commits=100,
|
|
unique_authors=5,
|
|
overall_bus_factor=2.5,
|
|
gini_coefficient=0.35,
|
|
files=[
|
|
{
|
|
"path": "src/main.py",
|
|
"total_commits": 20,
|
|
"num_authors": 2,
|
|
"author_commits": {"a@x.com": 15, "b@x.com": 5},
|
|
"bus_factor": 1.5,
|
|
"risk_level": "high",
|
|
"top_author_share": 0.75,
|
|
"module": "src",
|
|
"extension": "py"
|
|
}
|
|
],
|
|
hotspots=[
|
|
{
|
|
"file_path": "src/main.py",
|
|
"risk_level": "high",
|
|
"bus_factor": 1.5,
|
|
"top_author": "a@x.com",
|
|
"top_author_share": 0.75,
|
|
"total_commits": 20,
|
|
"num_authors": 2,
|
|
"module": "src",
|
|
"suggestion": "Consider code reviews"
|
|
}
|
|
],
|
|
suggestions=[
|
|
{
|
|
"file_path": "src/main.py",
|
|
"current_author": "a@x.com",
|
|
"suggested_authors": ["b@x.com"],
|
|
"priority": "high",
|
|
"reason": "High ownership concentration",
|
|
"action": "Assign reviews to b@x.com"
|
|
}
|
|
],
|
|
risk_summary={
|
|
"critical": 0,
|
|
"high": 1,
|
|
"medium": 3,
|
|
"low": 6,
|
|
"percentage_critical": 0.0,
|
|
"percentage_high": 10.0,
|
|
"overall_risk": "low"
|
|
}
|
|
)
|
|
|
|
def test_generate_json(self):
|
|
"""Test JSON generation."""
|
|
json_output = self.reporter.generate(self.sample_result)
|
|
|
|
assert isinstance(json_output, str)
|
|
parsed = json.loads(json_output)
|
|
|
|
assert parsed["repository"] == "/test/repo"
|
|
assert parsed["summary"]["overall_bus_factor"] == 2.5
|
|
|
|
def test_generate_file_dict(self):
|
|
"""Test file analysis to dictionary conversion."""
|
|
analysis = FileAnalysis(
|
|
path="src/main.py",
|
|
total_commits=20,
|
|
author_commits={"a@x.com": 15, "b@x.com": 5},
|
|
bus_factor=1.5,
|
|
risk_level="high",
|
|
module="src",
|
|
extension="py"
|
|
)
|
|
|
|
file_dict = self.reporter.generate_file_dict(analysis)
|
|
|
|
assert file_dict["path"] == "src/main.py"
|
|
assert file_dict["total_commits"] == 20
|
|
assert file_dict["num_authors"] == 2
|
|
assert file_dict["bus_factor"] == 1.5
|
|
|
|
def test_save_json(self, temp_dir):
|
|
"""Test saving JSON to file."""
|
|
output_file = temp_dir / "output.json"
|
|
|
|
self.reporter.save(self.sample_result, str(output_file))
|
|
|
|
assert output_file.exists()
|
|
|
|
content = json.loads(output_file.read_text())
|
|
assert content["repository"] == "/test/repo"
|
|
|
|
def test_indent_parameter(self):
|
|
"""Test JSON indentation."""
|
|
reporter_no_indent = JSONReporter(indent=0)
|
|
json_output = reporter_no_indent.generate(self.sample_result)
|
|
|
|
lines = json_output.strip().split("\n")
|
|
assert len(lines) <= 2
|
|
|
|
def test_json_contains_required_fields(self):
|
|
"""Test that JSON output contains all required fields."""
|
|
json_output = self.reporter.generate(self.sample_result)
|
|
parsed = json.loads(json_output)
|
|
|
|
assert "version" in parsed
|
|
assert "repository" in parsed
|
|
assert "analyzed_at" in parsed
|
|
assert "summary" in parsed
|
|
assert "files" in parsed
|
|
assert "hotspots" in parsed
|
|
assert "suggestions" in parsed
|
|
|
|
|
|
class TestHTMLReporter:
|
|
"""Tests for HTMLReporter."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures."""
|
|
self.reporter = HTMLReporter()
|
|
self.sample_result = RepositoryResult(
|
|
repository_path="/test/repo",
|
|
files_analyzed=10,
|
|
total_commits=100,
|
|
unique_authors=5,
|
|
overall_bus_factor=2.5,
|
|
gini_coefficient=0.35,
|
|
files=[
|
|
{
|
|
"path": "src/main.py",
|
|
"total_commits": 20,
|
|
"num_authors": 2,
|
|
"author_commits": {"a@x.com": 15, "b@x.com": 5},
|
|
"bus_factor": 1.5,
|
|
"risk_level": "high",
|
|
"top_author_share": 0.75,
|
|
"module": "src",
|
|
"extension": "py"
|
|
}
|
|
],
|
|
hotspots=[
|
|
{
|
|
"file_path": "src/main.py",
|
|
"risk_level": "high",
|
|
"bus_factor": 1.5,
|
|
"top_author": "a@x.com",
|
|
"top_author_share": 0.75,
|
|
"total_commits": 20,
|
|
"num_authors": 2,
|
|
"module": "src",
|
|
"suggestion": "Consider code reviews"
|
|
}
|
|
],
|
|
suggestions=[
|
|
{
|
|
"file_path": "src/main.py",
|
|
"current_author": "a@x.com",
|
|
"suggested_authors": ["b@x.com"],
|
|
"priority": "high",
|
|
"reason": "High ownership concentration",
|
|
"action": "Assign reviews to b@x.com"
|
|
}
|
|
],
|
|
risk_summary={
|
|
"critical": 0,
|
|
"high": 1,
|
|
"medium": 3,
|
|
"low": 6,
|
|
"percentage_critical": 0.0,
|
|
"percentage_high": 10.0,
|
|
"overall_risk": "low"
|
|
}
|
|
)
|
|
|
|
def test_generate_standalone(self):
|
|
"""Test standalone HTML generation."""
|
|
html_output = self.reporter.generate_standalone(self.sample_result)
|
|
|
|
assert isinstance(html_output, str)
|
|
assert "<!doctype html>" in html_output.lower() or "<html>" in html_output.lower()
|
|
assert "</html>" in html_output
|
|
|
|
def test_standalone_contains_summary(self):
|
|
"""Test that standalone HTML contains summary section."""
|
|
html_output = self.reporter.generate_standalone(self.sample_result)
|
|
|
|
assert "repository health report" in html_output.lower()
|
|
|
|
def test_standalone_contains_chart_data(self):
|
|
"""Test that standalone HTML includes Chart.js."""
|
|
html_output = self.reporter.generate_standalone(self.sample_result)
|
|
|
|
assert "chart.js" in html_output.lower()
|
|
|
|
def test_save_standalone(self, temp_dir):
|
|
"""Test saving standalone HTML to file."""
|
|
output_file = temp_dir / "report.html"
|
|
|
|
self.reporter.save_standalone(self.sample_result, str(output_file))
|
|
|
|
assert output_file.exists()
|
|
|
|
content = output_file.read_text()
|
|
assert "<!doctype html>" in content.lower() or "<html>" in content.lower()
|
|
|
|
def test_generate_charts_data(self):
|
|
"""Test chart data generation."""
|
|
charts_data = self.reporter.generate_charts_data(self.sample_result)
|
|
|
|
assert "risk_distribution" in charts_data
|
|
assert "top_hotspots" in charts_data
|
|
assert "file_data" in charts_data
|
|
assert "summary" in charts_data
|
|
|
|
def test_risk_colors_defined(self):
|
|
"""Test that risk colors are properly defined."""
|
|
assert "critical" in self.reporter.RISK_COLORS
|
|
assert "high" in self.reporter.RISK_COLORS
|
|
assert "medium" in self.reporter.RISK_COLORS
|
|
assert "low" in self.reporter.RISK_COLORS
|
|
|
|
|
|
class TestTerminalReporter:
|
|
"""Tests for TerminalReporter."""
|
|
|
|
def test_reporter_initialization(self):
|
|
"""Test terminal reporter initialization."""
|
|
from repohealth.reporters.terminal import TerminalReporter
|
|
|
|
reporter = TerminalReporter()
|
|
|
|
assert reporter.RISK_COLORS is not None
|
|
|
|
def test_risk_colors_mapping(self):
|
|
"""Test risk color mappings."""
|
|
from repohealth.reporters.terminal import TerminalReporter
|
|
|
|
reporter = TerminalReporter()
|
|
|
|
assert reporter.RISK_COLORS["critical"] == "red"
|
|
assert reporter.RISK_COLORS["high"] == "orange3"
|
|
assert reporter.RISK_COLORS["medium"] == "yellow"
|
|
assert reporter.RISK_COLORS["low"] == "green"
|