#!/usr/bin/env python3 """Tests for scripts/refactoring_opportunity_finder.py — 10 tests.""" import json import os import sys import tempfile sys.path.insert(0, os.path.dirname(__file__) or ".") import importlib.util spec = importlib.util.spec_from_file_location( "rof", os.path.join(os.path.dirname(__file__) or ".", "refactoring_opportunity_finder.py")) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) compute_file_complexity = mod.compute_file_complexity calculate_refactoring_score = mod.calculate_refactoring_score FileMetrics = mod.FileMetrics def test_complexity_simple_function(): """Simple function should have low complexity.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write(""" def simple(): return 42 """) f.flush() avg, max_c, funcs, classes, lines = compute_file_complexity(f.name) assert avg == 1.0, f"Expected 1.0, got {avg}" assert max_c == 1, f"Expected 1, got {max_c}" assert funcs == 1, f"Expected 1, got {funcs}" assert classes == 0, f"Expected 0, got {classes}" os.unlink(f.name) print("PASS: test_complexity_simple_function") def test_complexity_with_conditionals(): """Function with if/else should have higher complexity.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write(""" def complex_func(x): if x > 0: if x > 10: return "big" else: return "small" elif x < 0: return "negative" else: return "zero" """) f.flush() avg, max_c, funcs, classes, lines = compute_file_complexity(f.name) # Base 1 + 3 if/elif + 1 nested if = 5 assert max_c >= 4, f"Expected max_c >= 4, got {max_c}" assert funcs == 1, f"Expected 1, got {funcs}" os.unlink(f.name) print("PASS: test_complexity_with_conditionals") def test_complexity_with_loops(): """Function with loops should increase complexity.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write(""" def loop_func(items): result = [] for item in items: if item > 0: result.append(item) while len(result) > 10: result.pop() return result """) f.flush() avg, max_c, funcs, classes, lines = compute_file_complexity(f.name) # Base 1 + 1 for + 1 if + 1 while = 4 assert max_c >= 3, f"Expected max_c >= 3, got {max_c}" os.unlink(f.name) print("PASS: test_complexity_with_loops") def test_complexity_with_class(): """Class with methods should count both.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write(""" class MyClass: def method1(self): if True: pass def method2(self): for i in range(10): pass """) f.flush() avg, max_c, funcs, classes, lines = compute_file_complexity(f.name) assert classes == 1, f"Expected 1 class, got {classes}" assert funcs == 2, f"Expected 2 functions, got {funcs}" os.unlink(f.name) print("PASS: test_complexity_with_class") def test_complexity_syntax_error(): """File with syntax error should return zeros.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write("def broken(:\n pass") f.flush() avg, max_c, funcs, classes, lines = compute_file_complexity(f.name) assert avg == 0.0, f"Expected 0.0, got {avg}" assert funcs == 0, f"Expected 0, got {funcs}" os.unlink(f.name) print("PASS: test_complexity_syntax_error") def test_refactoring_score_high_complexity(): """High complexity should give high score.""" metrics = FileMetrics( path="test.py", lines=200, complexity=15.0, max_complexity=25, functions=10, classes=2, churn_30d=5, churn_90d=15, test_coverage=0.3, refactoring_score=0.0 ) score = calculate_refactoring_score(metrics) assert score > 50, f"Expected score > 50, got {score}" print("PASS: test_refactoring_score_high_complexity") def test_refactoring_score_low_complexity(): """Low complexity should give lower score.""" metrics = FileMetrics( path="test.py", lines=50, complexity=2.0, max_complexity=3, functions=3, classes=0, churn_30d=0, churn_90d=1, test_coverage=0.9, refactoring_score=0.0 ) score = calculate_refactoring_score(metrics) assert score < 30, f"Expected score < 30, got {score}" print("PASS: test_refactoring_score_low_complexity") def test_refactoring_score_high_churn(): """High churn should increase score.""" metrics = FileMetrics( path="test.py", lines=100, complexity=5.0, max_complexity=8, functions=5, classes=0, churn_30d=10, churn_90d=20, test_coverage=0.5, refactoring_score=0.0 ) score = calculate_refactoring_score(metrics) # Churn should contribute significantly assert score > 40, f"Expected score > 40 for high churn, got {score}" print("PASS: test_refactoring_score_high_churn") def test_refactoring_score_no_coverage(): """No coverage data should assume medium risk.""" metrics = FileMetrics( path="test.py", lines=100, complexity=5.0, max_complexity=8, functions=5, classes=0, churn_30d=1, churn_90d=2, test_coverage=None, refactoring_score=0.0 ) score = calculate_refactoring_score(metrics) # Should have some score from the 5-point coverage component assert score > 0, f"Expected positive score, got {score}" print("PASS: test_refactoring_score_no_coverage") def test_refactoring_score_large_file(): """Large files should score higher.""" metrics_small = FileMetrics( path="small.py", lines=50, complexity=5.0, max_complexity=8, functions=3, classes=0, churn_30d=1, churn_90d=2, test_coverage=0.8, refactoring_score=0.0 ) metrics_large = FileMetrics( path="large.py", lines=1000, complexity=5.0, max_complexity=8, functions=3, classes=0, churn_30d=1, churn_90d=2, test_coverage=0.8, refactoring_score=0.0 ) score_small = calculate_refactoring_score(metrics_small) score_large = calculate_refactoring_score(metrics_large) assert score_large > score_small, \ f"Large file ({score_large}) should score higher than small ({score_small})" print("PASS: test_refactoring_score_large_file") def run_all(): test_complexity_simple_function() test_complexity_with_conditionals() test_complexity_with_loops() test_complexity_with_class() test_complexity_syntax_error() test_refactoring_score_high_complexity() test_refactoring_score_low_complexity() test_refactoring_score_high_churn() test_refactoring_score_no_coverage() test_refactoring_score_large_file() print("\nAll 10 tests passed!") if __name__ == "__main__": run_all()