"""
Comprehensive unit tests for testing_agent.py core functions.
"""
import json
import sys
import tempfile
from pathlib import Path
import pytest
# Add project root to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from testing_agent import (
detect_languages,
detect_test_frameworks,
plan_test_runs,
write_scan_snapshot,
write_all_skipped_report,
build_test_plan,
build_coverage_map,
execute_single_test_run,
execute_planned_runs,
execute_planned_runs_parallel,
scan_repository,
run_all_tests,
full_phase2_pipeline,
run_all_tests_parallel,
LanguageDetection,
)
class TestDetectLanguages:
"""Test the detect_languages function."""
def test_detects_python_files(self, tmp_path):
"""Should detect Python source and test files."""
# Create Python source files
(tmp_path / "main.py").write_text("def main(): pass")
(tmp_path / "utils.py").write_text("def util(): pass")
# Create test files
(tmp_path / "tests").mkdir()
(tmp_path / "tests" / "test_main.py").write_text("def test_main(): pass")
(tmp_path / "tests" / "test_utils.py").write_text("def test_util(): pass")
result = detect_languages(tmp_path)
assert result["python"].source_files == 2
assert result["python"].test_files == 2
assert result["python"].status == "detected"
assert "tests" in result["python"].test_roots
def test_detects_javascript_files(self, tmp_path):
"""Should detect JavaScript/TypeScript source and test files."""
# Create JS source files
(tmp_path / "app.js").write_text("console.log('hello');")
(tmp_path / "utils.ts").write_text("export const util = () => {};")
# Create test files
(tmp_path / "__tests__").mkdir()
(tmp_path / "__tests__" / "app.test.js").write_text("test('app', () => {});")
(tmp_path / "utils.spec.ts").write_text("describe('util', () => {});")
result = detect_languages(tmp_path)
assert result["js_ts"].source_files == 2
assert result["js_ts"].test_files == 2
assert result["js_ts"].status == "detected"
def test_detects_go_files(self, tmp_path):
"""Should detect Go source and test files."""
(tmp_path / "main.go").write_text("package main")
(tmp_path / "utils.go").write_text("package utils")
(tmp_path / "main_test.go").write_text("package main_test")
result = detect_languages(tmp_path)
assert result["go"].source_files == 2
assert result["go"].test_files == 1
assert result["go"].status == "detected"
def test_detects_rust_files(self, tmp_path):
"""Should detect Rust source and test files."""
(tmp_path / "src").mkdir()
(tmp_path / "src" / "main.rs").write_text("fn main() {}")
(tmp_path / "tests").mkdir()
(tmp_path / "tests" / "integration_test.rs").write_text("#[test] fn test() {}")
result = detect_languages(tmp_path)
assert result["rust"].source_files == 1
assert result["rust"].test_files == 1
assert result["rust"].status == "detected"
def test_detects_java_files(self, tmp_path):
"""Should detect Java source and test files."""
(tmp_path / "src" / "main" / "java").mkdir(parents=True)
(tmp_path / "src" / "main" / "java" / "App.java").write_text("class App {}")
(tmp_path / "src" / "test" / "java").mkdir(parents=True)
(tmp_path / "src" / "test" / "java" / "AppTest.java").write_text("class AppTest {}")
result = detect_languages(tmp_path)
assert result["java"].source_files == 1
assert result["java"].test_files == 1
assert result["java"].status == "detected"
def test_detects_ruby_files(self, tmp_path):
"""Should detect Ruby source and test files."""
(tmp_path / "lib").mkdir()
(tmp_path / "lib" / "app.rb").write_text("class App; end")
(tmp_path / "spec").mkdir()
(tmp_path / "spec" / "app_spec.rb").write_text("describe App do; end")
result = detect_languages(tmp_path)
assert result["ruby"].source_files == 1
assert result["ruby"].test_files == 1
assert result["ruby"].status == "detected"
def test_excludes_venv_directories(self, tmp_path):
"""Should exclude virtual environment directories."""
(tmp_path / ".venv" / "lib").mkdir(parents=True)
(tmp_path / ".venv" / "lib" / "python.py").write_text("# venv file")
(tmp_path / "venv" / "lib").mkdir(parents=True)
(tmp_path / "venv" / "lib" / "python.py").write_text("# venv file")
(tmp_path / "main.py").write_text("def main(): pass")
result = detect_languages(tmp_path)
# Should only find main.py, not the venv files
assert result["python"].source_files == 1
def test_excludes_node_modules(self, tmp_path):
"""Should exclude node_modules directory."""
(tmp_path / "node_modules" / "package").mkdir(parents=True)
(tmp_path / "node_modules" / "package" / "index.js").write_text("// node module")
(tmp_path / "app.js").write_text("console.log('app');")
result = detect_languages(tmp_path)
# Should only find app.js
assert result["js_ts"].source_files == 1
def test_no_sources_detected(self, tmp_path):
"""Should report 'none' status when no sources found."""
# Empty directory
result = detect_languages(tmp_path)
assert result["python"].status == "none"
assert result["python"].source_files == 0
assert result["python"].note == "No Python *.py files detected"
class TestDetectTestFrameworks:
"""Test the detect_test_frameworks function."""
def test_detects_jest_from_package_json(self, tmp_path):
"""Should detect Jest from package.json test script."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({
"scripts": {
"test": "jest"
}
}))
frameworks = detect_test_frameworks(tmp_path)
assert "js_ts" in frameworks
assert "jest" in frameworks["js_ts"]
def test_detects_npm_test_fallback(self, tmp_path):
"""Should fallback to npm test for other JS test commands."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({
"scripts": {
"test": "mocha"
}
}))
frameworks = detect_test_frameworks(tmp_path)
assert "js_ts" in frameworks
assert frameworks["js_ts"] == "npm test"
def test_detects_maven_from_pom_xml(self, tmp_path):
"""Should detect Maven from pom.xml."""
(tmp_path / "pom.xml").write_text("<project></project>")
frameworks = detect_test_frameworks(tmp_path)
assert "java" in frameworks
assert frameworks["java"] == "mvn test"
def test_detects_gradle_from_build_gradle(self, tmp_path):
"""Should detect Gradle from build.gradle."""
(tmp_path / "build.gradle").write_text("// Gradle build file")
frameworks = detect_test_frameworks(tmp_path)
assert "java" in frameworks
assert frameworks["java"] == "gradle test"
def test_detects_cargo_from_cargo_toml(self, tmp_path):
"""Should detect Cargo from Cargo.toml."""
(tmp_path / "Cargo.toml").write_text("[package]\nname = 'test'")
frameworks = detect_test_frameworks(tmp_path)
assert "rust" in frameworks
assert frameworks["rust"] == "cargo test --verbose"
def test_detects_rspec_from_gemfile(self, tmp_path):
"""Should detect RSpec from Gemfile."""
gemfile = tmp_path / "Gemfile"
gemfile.write_text("gem 'rspec'")
frameworks = detect_test_frameworks(tmp_path)
assert "ruby" in frameworks
assert "rspec" in frameworks["ruby"]
def test_detects_rspec_from_spec_directory(self, tmp_path):
"""Should detect RSpec from spec/ directory."""
(tmp_path / "spec").mkdir()
frameworks = detect_test_frameworks(tmp_path)
assert "ruby" in frameworks
assert frameworks["ruby"] == "rspec"
def test_returns_empty_when_no_frameworks_detected(self, tmp_path):
"""Should return empty dict when no frameworks detected."""
frameworks = detect_test_frameworks(tmp_path)
assert frameworks == {}
def test_handles_invalid_package_json(self, tmp_path):
"""Should handle malformed package.json gracefully."""
package_json = tmp_path / "package.json"
package_json.write_text("{ invalid json")
frameworks = detect_test_frameworks(tmp_path)
# Should not crash, should return empty or partial results
assert isinstance(frameworks, dict)
class TestPlanTestRuns:
"""Test the plan_test_runs function."""
def test_plans_python_tests_when_present(self):
"""Should plan pytest execution when Python tests exist."""
languages = {
"python": LanguageDetection("python", 5, 3, ["tests"], "detected", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", "No JS/TS source files detected"),
"go": LanguageDetection("go", 0, 0, [], "none", "No Go *.go files detected"),
"rust": LanguageDetection("rust", 0, 0, [], "none", "No Rust *.rs files detected"),
"java": LanguageDetection("java", 0, 0, [], "none", "No Java *.java files detected"),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", "No Ruby *.rb files detected"),
}
planned = plan_test_runs(languages)
# Find the Python test plan
python_plan = next((p for p in planned if p.get("framework") == "pytest" and "command" in p and p["command"] == "pytest"), None)
assert python_plan is not None
assert python_plan["framework"] == "pytest"
assert python_plan["command"] == "pytest"
def test_skips_when_no_test_files(self):
"""Should skip execution when no test files exist."""
languages = {
"python": LanguageDetection("python", 5, 0, [], "detected", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", "No JS/TS source files detected"),
"go": LanguageDetection("go", 0, 0, [], "none", "No Go *.go files detected"),
"rust": LanguageDetection("rust", 0, 0, [], "none", "No Rust *.rs files detected"),
"java": LanguageDetection("java", 0, 0, [], "none", "No Java *.java files detected"),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", "No Ruby *.rb files detected"),
}
planned = plan_test_runs(languages)
# Find the Python skip result
python_skip = next((p for p in planned if p.get("framework") == "pytest"), None)
assert python_skip is not None
assert python_skip["status"] == "skipped"
assert "no test files" in python_skip["reason"].lower()
def test_plans_multiple_languages(self):
"""Should plan tests for multiple languages."""
languages = {
"python": LanguageDetection("python", 5, 3, ["tests"], "detected", None),
"js_ts": LanguageDetection("javascript_typescript", 10, 5, ["__tests__"], "detected", None),
"go": LanguageDetection("go", 3, 2, ["."], "detected", None),
"rust": LanguageDetection("rust", 0, 0, [], "none", "No Rust *.rs files detected"),
"java": LanguageDetection("java", 0, 0, [], "none", "No Java *.java files detected"),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", "No Ruby *.rb files detected"),
}
planned = plan_test_runs(languages)
# Should have plans for all 6 languages (3 execute, 3 skip)
assert len(planned) == 6
# Count executable plans (those with just framework and command keys)
executable = [p for p in planned if "status" not in p]
assert len(executable) == 3 # Python, JS/TS, Go
class TestWriteScanSnapshot:
"""Test the write_scan_snapshot function."""
def test_writes_valid_json(self, tmp_path):
"""Should write valid JSON snapshot file."""
languages = {
"python": LanguageDetection("python", 5, 3, ["tests"], "detected", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", "No JS/TS source files detected"),
"go": LanguageDetection("go", 0, 0, [], "none", "No Go *.go files detected"),
"rust": LanguageDetection("rust", 0, 0, [], "none", "No Rust *.rs files detected"),
"java": LanguageDetection("java", 0, 0, [], "none", "No Java *.java files detected"),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", "No Ruby *.rb files detected"),
}
snapshot = write_scan_snapshot(tmp_path, languages)
# Verify snapshot structure
assert "scanned_at" in snapshot
assert "root" in snapshot
assert "excluded_dirs" in snapshot
assert "languages" in snapshot
# Verify snapshot file was written
snapshot_file = tmp_path / ".mcp_scan_snapshot.json"
assert snapshot_file.exists()
# Verify it's valid JSON
with open(snapshot_file) as f:
data = json.load(f)
assert data["languages"]["python"]["source_files"] == 5
def test_includes_all_languages(self, tmp_path):
"""Should include all 6 languages in snapshot."""
languages = {
"python": LanguageDetection("python", 1, 1, ["tests"], "detected", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", "No JS/TS source files detected"),
"go": LanguageDetection("go", 0, 0, [], "none", "No Go *.go files detected"),
"rust": LanguageDetection("rust", 0, 0, [], "none", "No Rust *.rs files detected"),
"java": LanguageDetection("java", 0, 0, [], "none", "No Java *.java files detected"),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", "No Ruby *.rb files detected"),
}
snapshot = write_scan_snapshot(tmp_path, languages)
assert len(snapshot["languages"]) == 6
assert "python" in snapshot["languages"]
assert "js_ts" in snapshot["languages"]
assert "go" in snapshot["languages"]
assert "rust" in snapshot["languages"]
assert "java" in snapshot["languages"]
assert "ruby" in snapshot["languages"]
class TestBuildTestPlan:
"""Test the build_test_plan function."""
def test_creates_markdown_file(self, tmp_path):
"""Should create TEST_PLAN_AI.md file."""
languages = {
"python": LanguageDetection("python", 5, 3, ["tests"], "detected", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", "No JS/TS source files detected"),
"go": LanguageDetection("go", 0, 0, [], "none", "No Go *.go files detected"),
"rust": LanguageDetection("rust", 0, 0, [], "none", "No Rust *.rs files detected"),
"java": LanguageDetection("java", 0, 0, [], "none", "No Java *.java files detected"),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", "No Ruby *.rb files detected"),
}
plan_path = build_test_plan(tmp_path, languages)
assert Path(plan_path).exists()
assert Path(plan_path).name == "TEST_PLAN_AI.md"
# Verify content
content = Path(plan_path).read_text()
assert "AI Test Plan" in content
assert "Python Tests" in content or "python" in content.lower()
class TestBuildCoverageMap:
"""Test the build_coverage_map function."""
def test_creates_coverage_markdown(self, tmp_path):
"""Should create TEST_COVERAGE_MAP.md file."""
snapshot = {
"scanned_at": "2025-12-10T00:00:00Z",
"root": str(tmp_path),
"excluded_dirs": [],
"languages": {
"python": {
"language": "python",
"source_files": 5,
"test_files": 3,
"test_roots": ["tests"],
"status": "detected",
"note": None
}
}
}
report = {
"executed_at": "2025-12-10T00:00:00Z",
"root": str(tmp_path),
"summary": {
"total_runs": 1,
"success": 1,
"failed": 0,
"skipped": 0
},
"runs": [
{
"framework": "pytest",
"status": "success",
"exit_code": 0,
"reason": None
}
]
}
coverage_path = build_coverage_map(tmp_path, snapshot, report)
assert Path(coverage_path).exists()
assert Path(coverage_path).name == "TEST_COVERAGE_MAP.md"
# Verify content
content = Path(coverage_path).read_text()
assert "Test Coverage Map" in content
assert "pytest" in content
class TestWriteAllSkippedReport:
"""Test the write_all_skipped_report function."""
def test_creates_all_skipped_report(self, tmp_path):
"""Should create report with all tests skipped."""
languages = {
"python": LanguageDetection("python", 0, 0, [], "none", "No Python *.py files detected"),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", "No JS/TS source files detected"),
"go": LanguageDetection("go", 0, 0, [], "none", "No Go *.go files detected"),
"rust": LanguageDetection("rust", 0, 0, [], "none", "No Rust *.rs files detected"),
"java": LanguageDetection("java", 0, 0, [], "none", "No Java *.java files detected"),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", "No Ruby *.rb files detected"),
}
report = write_all_skipped_report(tmp_path, languages)
assert report["summary"]["total_runs"] == 6
assert report["summary"]["success"] == 0
assert report["summary"]["failed"] == 0
assert report["summary"]["skipped"] == 6
# Verify file was written
report_file = tmp_path / ".mcp_test_report.json"
assert report_file.exists()
class TestExecuteSingleTestRun:
"""Test the execute_single_test_run function for parallel execution."""
def test_executes_successful_test(self, tmp_path):
"""Should execute a single test run successfully."""
item = {
"framework": "pytest",
"command": "python --version" # Simple command that should succeed
}
result = execute_single_test_run(tmp_path, item)
assert result["framework"] == "pytest"
assert result["command"] == "python --version"
assert result["status"] == "success"
assert result["exit_code"] == 0
assert "Python" in result["raw_output"]
assert "start_time" in result
assert "end_time" in result
def test_handles_failed_test(self, tmp_path):
"""Should handle a failed test command."""
item = {
"framework": "pytest",
"command": "exit 1" # Command that will fail
}
result = execute_single_test_run(tmp_path, item)
assert result["framework"] == "pytest"
assert result["status"] == "failed"
assert result["exit_code"] == 1
def test_handles_invalid_command(self, tmp_path):
"""Should handle nonexistent commands as failed."""
item = {
"framework": "pytest",
"command": "nonexistentcommand12345"
}
result = execute_single_test_run(tmp_path, item)
assert result["framework"] == "pytest"
assert result["status"] == "failed"
assert result["exit_code"] != 0 # Non-zero exit code for command not found
class TestExecutePlannedRunsParallel:
"""Test the execute_planned_runs_parallel function."""
def test_executes_multiple_tests_in_parallel(self, tmp_path):
"""Should execute multiple test runs in parallel."""
planned = [
{"framework": "test1", "command": "echo test1"},
{"framework": "test2", "command": "echo test2"},
{"framework": "test3", "command": "echo test3"},
]
report = execute_planned_runs_parallel(tmp_path, planned, max_workers=2)
assert report["summary"]["total_runs"] == 3
assert report["summary"]["success"] == 3
assert report["summary"]["failed"] == 0
assert len(report["runs"]) == 3
# Verify all frameworks executed
frameworks = [run["framework"] for run in report["runs"]]
assert "test1" in frameworks
assert "test2" in frameworks
assert "test3" in frameworks
def test_handles_skipped_items(self, tmp_path):
"""Should preserve skipped test entries."""
planned = [
{"framework": "test1", "command": "echo test1"},
{
"framework": "pytest",
"command": "pytest",
"status": "skipped",
"exit_code": -1,
"raw_output": "Skipped",
"start_time": "2025-12-10T00:00:00Z",
"end_time": "2025-12-10T00:00:00Z",
"reason": "No test files"
},
]
report = execute_planned_runs_parallel(tmp_path, planned, max_workers=2)
assert report["summary"]["total_runs"] == 2
assert report["summary"]["success"] == 1
assert report["summary"]["skipped"] == 1
# Find skipped entry
skipped_runs = [r for r in report["runs"] if r.get("status") == "skipped"]
assert len(skipped_runs) == 1
assert skipped_runs[0]["framework"] == "pytest"
def test_writes_report_file(self, tmp_path):
"""Should write report to .mcp_test_report.json."""
planned = [
{"framework": "test1", "command": "echo test1"},
]
execute_planned_runs_parallel(tmp_path, planned, max_workers=1)
report_file = tmp_path / ".mcp_test_report.json"
assert report_file.exists()
# Verify JSON is valid
report_data = json.loads(report_file.read_text())
assert "summary" in report_data
assert "runs" in report_data
def test_respects_max_workers_parameter(self, tmp_path):
"""Should execute with specified max_workers."""
planned = [
{"framework": f"test{i}", "command": "echo test"}
for i in range(5)
]
# Should complete successfully with different worker counts
report1 = execute_planned_runs_parallel(tmp_path, planned, max_workers=1)
assert report1["summary"]["total_runs"] == 5
report2 = execute_planned_runs_parallel(tmp_path, planned, max_workers=4)
assert report2["summary"]["total_runs"] == 5
class TestExecutePlannedRuns:
"""Test the sequential execute_planned_runs function."""
def test_executes_tests_sequentially(self, tmp_path):
"""Should execute tests sequentially."""
planned = [
{"framework": "test1", "command": "echo test1"},
{"framework": "test2", "command": "echo test2"}
]
report = execute_planned_runs(tmp_path, planned)
assert report["summary"]["total_runs"] == 2
assert report["summary"]["success"] == 2
assert "runs" in report
def test_handles_failed_tests(self, tmp_path):
"""Should track failed tests."""
planned = [
{"framework": "test1", "command": "exit 0"},
{"framework": "test2", "command": "exit 1"}
]
report = execute_planned_runs(tmp_path, planned)
assert report["summary"]["total_runs"] == 2
assert report["summary"]["success"] == 1
assert report["summary"]["failed"] == 1
def test_handles_nonexistent_commands(self, tmp_path):
"""Should handle nonexistent commands as failed tests."""
planned = [
{"framework": "test1", "command": "nonexistentcommand12345"}
]
report = execute_planned_runs(tmp_path, planned)
assert report["summary"]["total_runs"] == 1
# Nonexistent commands result in failed status, not skipped
assert report["summary"]["failed"] == 1
def test_writes_report_file(self, tmp_path):
"""Should write test report to file."""
planned = [
{"framework": "test1", "command": "echo test"}
]
execute_planned_runs(tmp_path, planned)
report_file = tmp_path / ".mcp_test_report.json"
assert report_file.exists()
class TestPlanTestRunsEdgeCases:
"""Test edge cases in plan_test_runs function."""
def test_plans_go_tests_when_present(self):
"""Should plan go test execution when Go tests exist."""
languages = {
"python": LanguageDetection("python", 0, 0, [], "none", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", None),
"go": LanguageDetection("go", 5, 3, ["tests"], "detected", None),
"rust": LanguageDetection("rust", 0, 0, [], "none", None),
"java": LanguageDetection("java", 0, 0, [], "none", None),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", None),
}
planned = plan_test_runs(languages)
# Find the Go test plan
go_plan = next((p for p in planned if p.get("framework") == "go test"), None)
assert go_plan is not None
assert "go test" in go_plan["command"]
def test_plans_rust_tests_when_present(self):
"""Should plan cargo test execution when Rust tests exist."""
languages = {
"python": LanguageDetection("python", 0, 0, [], "none", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", None),
"go": LanguageDetection("go", 0, 0, [], "none", None),
"rust": LanguageDetection("rust", 5, 3, ["tests"], "detected", None),
"java": LanguageDetection("java", 0, 0, [], "none", None),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", None),
}
planned = plan_test_runs(languages)
# Find the Rust test plan
rust_plan = next((p for p in planned if p.get("framework") == "cargo test"), None)
assert rust_plan is not None
assert "cargo test" in rust_plan["command"]
def test_plans_java_tests_when_present(self):
"""Should plan maven/gradle execution when Java tests exist."""
languages = {
"python": LanguageDetection("python", 0, 0, [], "none", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", None),
"go": LanguageDetection("go", 0, 0, [], "none", None),
"rust": LanguageDetection("rust", 0, 0, [], "none", None),
"java": LanguageDetection("java", 5, 3, ["tests"], "detected", None),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", None),
}
planned = plan_test_runs(languages)
# Find the Java test plan
java_plan = next((p for p in planned if p.get("framework") == "maven/gradle"), None)
assert java_plan is not None
def test_plans_ruby_tests_when_present(self):
"""Should plan rspec/minitest execution when Ruby tests exist."""
languages = {
"python": LanguageDetection("python", 0, 0, [], "none", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", None),
"go": LanguageDetection("go", 0, 0, [], "none", None),
"rust": LanguageDetection("rust", 0, 0, [], "none", None),
"java": LanguageDetection("java", 0, 0, [], "none", None),
"ruby": LanguageDetection("ruby", 5, 3, ["tests"], "detected", None),
}
planned = plan_test_runs(languages)
# Find the Ruby test plan
ruby_plan = next((p for p in planned if p.get("framework") == "rspec/minitest"), None)
assert ruby_plan is not None
class TestFrameworkDetectionEdgeCases:
"""Test additional framework detection scenarios."""
def test_detects_mocha_from_package_json(self, tmp_path):
"""Should detect Mocha from package.json."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({
"scripts": {
"test": "mocha"
}
}))
frameworks = detect_test_frameworks(tmp_path)
assert "js_ts" in frameworks
assert frameworks["js_ts"] == "npm test"
def test_detects_vitest_from_package_json(self, tmp_path):
"""Should detect Vitest from package.json."""
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({
"scripts": {
"test": "vitest run"
}
}))
frameworks = detect_test_frameworks(tmp_path)
assert "js_ts" in frameworks
assert "vitest" in frameworks["js_ts"]
def test_detects_go_from_go_files(self, tmp_path):
"""Should detect Go test framework from .go files."""
(tmp_path / "main.go").write_text("package main")
frameworks = detect_test_frameworks(tmp_path)
assert "go" in frameworks
assert frameworks["go"] == "go test ./... -v"
def test_detects_minitest_from_gemfile(self, tmp_path):
"""Should detect Minitest from Gemfile."""
gemfile = tmp_path / "Gemfile"
gemfile.write_text("gem 'minitest'")
frameworks = detect_test_frameworks(tmp_path)
assert "ruby" in frameworks
assert "rake test" in frameworks["ruby"]
class TestMCPTools:
"""Test MCP tool functions."""
def test_scan_repository_basic(self, tmp_path):
"""Should scan repository and return snapshot."""
# Create a simple Python file
(tmp_path / "test.py").write_text("def foo(): pass")
result = scan_repository(str(tmp_path))
assert "snapshot_file" in result
assert "languages" in result
assert result["languages"]["python"]["source_files"] > 0
def test_run_all_tests_with_no_sources(self, tmp_path):
"""Should handle empty repository."""
result = run_all_tests(str(tmp_path))
assert "note" in result
assert "No source files detected" in result["note"]
def test_full_phase2_pipeline_calls_run_all_tests(self, tmp_path):
"""Should call run_all_tests."""
result = full_phase2_pipeline(str(tmp_path))
# Should return same structure as run_all_tests
assert "snapshot_file" in result
assert "test_plan_file" in result
assert "report_file" in result
assert "coverage_map_file" in result
def test_run_all_tests_parallel_with_no_sources(self, tmp_path):
"""Should handle empty repository with parallel execution."""
result = run_all_tests_parallel(str(tmp_path))
assert "note" in result
assert "No source files detected" in result["note"]
class TestBuildTestPlanEdgeCases:
"""Test edge cases in build_test_plan."""
def test_generates_plan_for_language_with_no_tests(self, tmp_path):
"""Should generate appropriate suggestions when no tests exist."""
languages = {
"python": LanguageDetection("python", 5, 0, [], "detected", None),
"js_ts": LanguageDetection("javascript_typescript", 0, 0, [], "none", None),
"go": LanguageDetection("go", 0, 0, [], "none", None),
"rust": LanguageDetection("rust", 0, 0, [], "none", None),
"java": LanguageDetection("java", 0, 0, [], "none", None),
"ruby": LanguageDetection("ruby", 0, 0, [], "none", None),
}
plan_path = build_test_plan(tmp_path, languages)
assert plan_path
plan_file = Path(plan_path)
assert plan_file.exists()
content = plan_file.read_text()
assert "Create an initial minimal test suite" in content or "smoke tests" in content