use tempfile::TempDir;
use pmat::services::enhanced_language_detection::{
detect_all_languages, detect_project_language_enhanced, detect_project_language_with_timeout,
override_language_detection, override_multiple_languages,
};
#[test]
#[ignore = "BUG-011: RED test - will fail until language detection fixed"]
fn test_cpp_project_detected_correctly() {
let project = create_mock_cpp_project();
let detection = detect_project_language_enhanced(project.path());
assert_eq!(
detection.language, "cpp",
"Should detect C++ as primary language"
);
assert!(
detection.confidence > 70.0,
"Should have high confidence (>70%) for C++ detection, got {}",
detection.confidence
);
}
#[test]
#[ignore = "BUG-011: RED test - will fail until confidence calculation fixed"]
fn test_confidence_calculation_cpp_vs_python() {
let project = create_mock_cpp_with_python_scripts();
let detection = detect_project_language_enhanced(project.path());
assert_eq!(
detection.language, "cpp",
"C++ should be primary language (70% of files)"
);
assert!(
detection.confidence >= 80.0,
"C++ confidence should be >= 80% with CMakeLists.txt, got {}",
detection.confidence
);
}
#[test]
#[ignore = "BUG-011: RED test - multi-language detection not implemented"]
fn test_detect_all_languages_in_polyglot_project() {
let project = create_polyglot_project();
let detection = detect_all_languages(project.path());
assert_eq!(detection.languages.len(), 3, "Should detect 3 languages");
let rust_lang = detection.languages.iter().find(|l| l.language == "rust");
let python_lang = detection.languages.iter().find(|l| l.language == "python");
let ts_lang = detection
.languages
.iter()
.find(|l| l.language == "typescript");
assert!(rust_lang.is_some(), "Should detect Rust");
assert!(python_lang.is_some(), "Should detect Python");
assert!(ts_lang.is_some(), "Should detect TypeScript");
assert_eq!(detection.primary, "rust", "Rust should be primary language");
}
#[test]
#[ignore = "BUG-011: RED test - percentage threshold not implemented"]
fn test_ignore_languages_below_5_percent() {
let project = create_project_with_minor_languages();
let detection = detect_all_languages(project.path());
assert_eq!(
detection.languages.len(),
2,
"Should only detect Rust and Python (>5%)"
);
let has_shell = detection.languages.iter().any(|l| l.language == "bash");
assert!(!has_shell, "Shell scripts (<5%) should not be included");
}
#[test]
#[ignore = "BUG-011: RED test - primary indicator weighting not implemented"]
fn test_primary_indicators_boost_confidence() {
let project = create_mixed_project_with_cargo_toml();
let detection = detect_project_language_enhanced(project.path());
assert_eq!(
detection.language, "rust",
"Rust should be detected due to Cargo.toml"
);
assert!(
detection.confidence >= 90.0,
"Cargo.toml should boost confidence to >=90%, got {}",
detection.confidence
);
}
#[test]
#[ignore = "BUG-011: RED test - CMakeLists.txt indicator not implemented"]
fn test_cmake_indicates_cpp_project() {
let project = create_cpp_project_with_cmake();
let detection = detect_project_language_enhanced(project.path());
assert_eq!(detection.language, "cpp");
assert!(
detection.confidence >= 85.0,
"CMakeLists.txt should indicate C++ with >=85% confidence"
);
}
#[test]
#[ignore = "BUG-011: RED test - timeout not implemented"]
fn test_discovery_completes_within_timeout() {
use std::time::{Duration, Instant};
let project = create_large_project();
let start = Instant::now();
let result = detect_project_language_with_timeout(project.path(), Duration::from_secs(5));
let elapsed = start.elapsed();
assert!(
elapsed < Duration::from_secs(5),
"Detection should complete within timeout"
);
assert!(result.is_ok(), "Detection should succeed within timeout");
}
#[test]
#[ignore = "BUG-011: RED test - manual override not implemented"]
fn test_language_override_flag() {
let project = create_python_project();
let detection = override_language_detection(project.path(), "cpp");
assert_eq!(
detection.language, "cpp",
"Should use manually overridden language"
);
assert_eq!(
detection.confidence, 100.0,
"Manual override should have 100% confidence"
);
}
#[test]
#[ignore = "BUG-011: RED test - multi-language override not implemented"]
fn test_languages_override_flag() {
let project = create_polyglot_project();
let languages = vec!["rust".to_string(), "python".to_string()];
let detection = override_multiple_languages(project.path(), languages);
assert_eq!(detection.languages.len(), 2);
assert!(detection.languages.iter().any(|l| l.language == "rust"));
assert!(detection.languages.iter().any(|l| l.language == "python"));
assert!(!detection
.languages
.iter()
.any(|l| l.language == "typescript"));
}
fn create_mock_cpp_project() -> TempDir {
let temp = TempDir::new().unwrap();
let base = temp.path();
std::fs::create_dir_all(base.join("src")).unwrap();
for i in 0..70 {
std::fs::write(
base.join(format!("src/module_{}.cc", i)),
"int main() { return 0; }",
)
.unwrap();
std::fs::write(base.join(format!("src/module_{}.h", i)), "#pragma once").unwrap();
}
std::fs::write(
base.join("CMakeLists.txt"),
"cmake_minimum_required(VERSION 3.10)\nproject(TestProject)\n",
)
.unwrap();
std::fs::create_dir_all(base.join("scripts")).unwrap();
for i in 0..20 {
std::fs::write(
base.join(format!("scripts/helper_{}.py", i)),
"print('hello')",
)
.unwrap();
}
temp
}
fn create_mock_cpp_with_python_scripts() -> TempDir {
create_mock_cpp_project() }
fn create_polyglot_project() -> TempDir {
let temp = TempDir::new().unwrap();
let base = temp.path();
std::fs::create_dir_all(base.join("src")).unwrap();
for i in 0..45 {
std::fs::write(base.join(format!("src/module_{}.rs", i)), "fn main() {}").unwrap();
}
std::fs::write(base.join("Cargo.toml"), "[package]\nname = \"test\"\n").unwrap();
std::fs::create_dir_all(base.join("scripts")).unwrap();
for i in 0..30 {
std::fs::write(
base.join(format!("scripts/tool_{}.py", i)),
"print('hello')",
)
.unwrap();
}
std::fs::create_dir_all(base.join("frontend")).unwrap();
for i in 0..25 {
std::fs::write(
base.join(format!("frontend/component_{}.ts", i)),
"export {}",
)
.unwrap();
}
std::fs::write(base.join("package.json"), "{}").unwrap();
temp
}
fn create_project_with_minor_languages() -> TempDir {
let temp = TempDir::new().unwrap();
let base = temp.path();
std::fs::create_dir_all(base.join("src")).unwrap();
for i in 0..90 {
std::fs::write(base.join(format!("src/module_{}.rs", i)), "fn main() {}").unwrap();
}
std::fs::create_dir_all(base.join("scripts")).unwrap();
for i in 0..8 {
std::fs::write(
base.join(format!("scripts/tool_{}.py", i)),
"print('hello')",
)
.unwrap();
}
for i in 0..2 {
std::fs::write(base.join(format!("scripts/build_{}.sh", i)), "#!/bin/bash").unwrap();
}
temp
}
fn create_mixed_project_with_cargo_toml() -> TempDir {
let temp = TempDir::new().unwrap();
let base = temp.path();
std::fs::create_dir_all(base.join("src")).unwrap();
std::fs::create_dir_all(base.join("scripts")).unwrap();
for i in 0..50 {
std::fs::write(base.join(format!("src/module_{}.rs", i)), "fn main() {}").unwrap();
std::fs::write(
base.join(format!("scripts/tool_{}.py", i)),
"print('hello')",
)
.unwrap();
}
std::fs::write(base.join("Cargo.toml"), "[package]\nname = \"test\"\n").unwrap();
temp
}
fn create_cpp_project_with_cmake() -> TempDir {
let temp = TempDir::new().unwrap();
let base = temp.path();
std::fs::create_dir_all(base.join("src")).unwrap();
for i in 0..50 {
std::fs::write(
base.join(format!("src/module_{}.cpp", i)),
"int main() { return 0; }",
)
.unwrap();
}
std::fs::write(
base.join("CMakeLists.txt"),
"cmake_minimum_required(VERSION 3.10)\nproject(TestProject)\n",
)
.unwrap();
temp
}
fn create_large_project() -> TempDir {
let temp = TempDir::new().unwrap();
let base = temp.path();
std::fs::create_dir_all(base.join("src")).unwrap();
for i in 0..1000 {
std::fs::write(base.join(format!("src/file_{}.rs", i)), "fn main() {}").unwrap();
}
temp
}
fn create_python_project() -> TempDir {
let temp = TempDir::new().unwrap();
let base = temp.path();
std::fs::create_dir_all(base.join("src")).unwrap();
for i in 0..50 {
std::fs::write(base.join(format!("src/module_{}.py", i)), "print('hello')").unwrap();
}
std::fs::write(base.join("pyproject.toml"), "[project]\nname = \"test\"\n").unwrap();
temp
}