use super::*;
#[allow(unused_imports)]
use crate::cli::commands::HooksCommands;
#[allow(unused_imports)]
use crate::tdg::TdgHooksConfig;
#[allow(unused_imports)]
use std::path::PathBuf;
mod tests {
use super::*;
#[tokio::test]
async fn test_hooks_install() {
let cmd = HooksCommands::Install {
interactive: false,
force: false,
backup: true,
tdg_enforcement: false,
stack: false,
update: false,
};
let result = handle_hooks_command(&cmd).await;
let _ = result;
}
#[tokio::test]
async fn test_hooks_status() {
let cmd = HooksCommands::Status {
stack: false,
format: crate::cli::enums::OutputFormat::Table,
};
let result = handle_hooks_command(&cmd).await;
let _ = result;
}
#[tokio::test]
async fn test_hooks_verify() {
let cmd = HooksCommands::Verify { fix: false };
let result = handle_hooks_command(&cmd).await;
let _ = result;
}
#[test]
fn test_normalize_hook_content_removes_timestamp() {
let content_with_timestamp = r#"#!/bin/bash
# Generated pre-commit hook (auto-managed by PMAT)
# DO NOT EDIT: This file is automatically generated
# Generated at: 2025-10-06 12:04:56
set -e
echo "Running hooks...""#;
let content_with_different_timestamp = r#"#!/bin/bash
# Generated pre-commit hook (auto-managed by PMAT)
# DO NOT EDIT: This file is automatically generated
# Generated at: 2025-10-06 14:30:22
set -e
echo "Running hooks...""#;
let normalized1 = HooksCommand::normalize_hook_content(content_with_timestamp);
let normalized2 = HooksCommand::normalize_hook_content(content_with_different_timestamp);
assert_eq!(normalized1, normalized2);
assert!(!normalized1.contains("Generated at:"));
assert!(!normalized2.contains("Generated at:"));
}
#[test]
fn test_normalize_hook_content_preserves_other_content() {
let content = r#"#!/bin/bash
# Some comment
# Generated at: 2025-10-06 12:00:00
echo "test"
pmat analyze complexity"#;
let normalized = HooksCommand::normalize_hook_content(content);
assert!(normalized.contains("#!/bin/bash"));
assert!(normalized.contains("# Some comment"));
assert!(normalized.contains("echo \"test\""));
assert!(normalized.contains("pmat analyze complexity"));
assert!(!normalized.contains("Generated at:"));
}
}
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}
mod coverage_tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_hooks_command_new() {
let hooks_dir = PathBuf::from("/tmp/test_hooks");
let config_path = PathBuf::from("/tmp/pmat.toml");
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
assert_eq!(cmd.hooks_dir, hooks_dir);
}
#[test]
fn test_hooks_command_for_current_repo() {
let result = HooksCommand::for_current_repo();
let _ = result;
}
#[test]
fn test_hook_install_result_equality() {
let result1 = HookInstallResult {
success: true,
hook_created: true,
backup_created: false,
message: "test".to_string(),
};
let result2 = HookInstallResult {
success: true,
hook_created: true,
backup_created: false,
message: "test".to_string(),
};
assert_eq!(result1, result2);
}
#[test]
fn test_hook_install_result_debug() {
let result = HookInstallResult {
success: true,
hook_created: true,
backup_created: true,
message: "Success".to_string(),
};
let debug_str = format!("{:?}", result);
assert!(debug_str.contains("success"));
assert!(debug_str.contains("hook_created"));
assert!(debug_str.contains("backup_created"));
}
#[test]
fn test_hook_uninstall_result_equality() {
let result1 = HookUninstallResult {
success: true,
hook_removed: true,
backup_restored: false,
message: "removed".to_string(),
};
let result2 = HookUninstallResult {
success: true,
hook_removed: true,
backup_restored: false,
message: "removed".to_string(),
};
assert_eq!(result1, result2);
}
#[test]
fn test_hook_uninstall_result_debug() {
let result = HookUninstallResult {
success: false,
hook_removed: false,
backup_restored: true,
message: "test".to_string(),
};
let debug_str = format!("{:?}", result);
assert!(debug_str.contains("success"));
assert!(debug_str.contains("hook_removed"));
}
#[test]
fn test_hook_status_equality() {
let status1 = HookStatus {
installed: true,
is_pmat_managed: true,
config_up_to_date: true,
last_updated: Some("2025-01-01".to_string()),
hook_content_preview: Some("#!/bin/bash".to_string()),
};
let status2 = HookStatus {
installed: true,
is_pmat_managed: true,
config_up_to_date: true,
last_updated: Some("2025-01-01".to_string()),
hook_content_preview: Some("#!/bin/bash".to_string()),
};
assert_eq!(status1, status2);
}
#[test]
fn test_hook_status_debug() {
let status = HookStatus {
installed: true,
is_pmat_managed: false,
config_up_to_date: false,
last_updated: None,
hook_content_preview: None,
};
let debug_str = format!("{:?}", status);
assert!(debug_str.contains("installed"));
assert!(debug_str.contains("is_pmat_managed"));
}
#[test]
fn test_hook_verification_result_equality() {
let result1 = HookVerificationResult {
is_valid: true,
issues: vec![],
fixes_applied: vec![],
};
let result2 = HookVerificationResult {
is_valid: true,
issues: vec![],
fixes_applied: vec![],
};
assert_eq!(result1, result2);
}
#[test]
fn test_hook_verification_result_with_issues() {
let result = HookVerificationResult {
is_valid: false,
issues: vec!["issue1".to_string(), "issue2".to_string()],
fixes_applied: vec!["fix1".to_string()],
};
assert!(!result.is_valid);
assert_eq!(result.issues.len(), 2);
assert_eq!(result.fixes_applied.len(), 1);
}
#[test]
fn test_hook_refresh_result_equality() {
let result1 = HookRefreshResult {
success: true,
hook_updated: true,
config_changes_detected: true,
message: "refreshed".to_string(),
};
let result2 = HookRefreshResult {
success: true,
hook_updated: true,
config_changes_detected: true,
message: "refreshed".to_string(),
};
assert_eq!(result1, result2);
}
#[test]
fn test_hook_refresh_result_debug() {
let result = HookRefreshResult {
success: false,
hook_updated: false,
config_changes_detected: false,
message: "no changes".to_string(),
};
let debug_str = format!("{:?}", result);
assert!(debug_str.contains("success"));
assert!(debug_str.contains("hook_updated"));
}
#[test]
fn test_hook_run_result_equality() {
let result1 = HookRunResult {
success: true,
checks_passed: 5,
checks_failed: 0,
output: "all passed".to_string(),
};
let result2 = HookRunResult {
success: true,
checks_passed: 5,
checks_failed: 0,
output: "all passed".to_string(),
};
assert_eq!(result1, result2);
}
#[test]
fn test_hook_run_result_debug() {
let result = HookRunResult {
success: false,
checks_passed: 3,
checks_failed: 2,
output: "some failed".to_string(),
};
let debug_str = format!("{:?}", result);
assert!(debug_str.contains("success"));
assert!(debug_str.contains("checks_passed"));
assert!(debug_str.contains("checks_failed"));
}
#[tokio::test]
async fn test_install_creates_hooks_directory() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.install(false, false, false).await.unwrap();
assert!(result.success);
assert!(result.hook_created);
assert!(hooks_dir.exists());
}
#[tokio::test]
async fn test_install_with_existing_non_pmat_hook_no_force() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\necho 'custom hook'",
)
.unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.install(false, false, false).await.unwrap();
assert!(!result.success);
assert!(!result.hook_created);
assert!(result.message.contains("not PMAT-managed"));
}
#[tokio::test]
async fn test_install_with_force_overwrites() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\necho 'custom hook'",
)
.unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.install(true, false, false).await.unwrap();
assert!(result.success);
assert!(result.hook_created);
}
#[tokio::test]
async fn test_install_with_backup() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT",
)
.unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.install(false, true, false).await.unwrap();
assert!(result.success);
assert!(result.backup_created);
assert!(hooks_dir.join("pre-commit.pmat-backup").exists());
}
#[tokio::test]
async fn test_install_backup_already_exists() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT",
)
.unwrap();
fs::write(
hooks_dir.join("pre-commit.pmat-backup"),
"#!/bin/bash\nold backup",
)
.unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.install(false, true, false).await.unwrap();
assert!(result.success);
assert!(!result.backup_created); }
#[tokio::test]
async fn test_uninstall_no_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.uninstall(false).await.unwrap();
assert!(result.success);
assert!(!result.hook_removed);
assert!(result.message.contains("No hook to uninstall"));
}
#[tokio::test]
async fn test_uninstall_non_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\necho 'custom hook'",
)
.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.uninstall(false).await.unwrap();
assert!(!result.success);
assert!(!result.hook_removed);
assert!(result.message.contains("not PMAT-managed"));
}
#[tokio::test]
async fn test_uninstall_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT",
)
.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.uninstall(false).await.unwrap();
assert!(result.success);
assert!(result.hook_removed);
assert!(!hooks_dir.join("pre-commit").exists());
}
#[tokio::test]
async fn test_uninstall_with_restore_backup() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT",
)
.unwrap();
fs::write(
hooks_dir.join("pre-commit.pmat-backup"),
"#!/bin/bash\noriginal hook",
)
.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.uninstall(true).await.unwrap();
assert!(result.success);
assert!(result.hook_removed);
assert!(result.backup_restored);
let content = fs::read_to_string(hooks_dir.join("pre-commit")).unwrap();
assert!(content.contains("original hook"));
}
#[tokio::test]
async fn test_status_no_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let status = cmd.status().await.unwrap();
assert!(!status.installed);
assert!(!status.is_pmat_managed);
assert!(!status.config_up_to_date);
assert!(status.last_updated.is_none());
assert!(status.hook_content_preview.is_none());
}
#[tokio::test]
#[ignore = "Flaky: config_up_to_date detection varies by environment"]
async fn test_status_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT\necho test",
)
.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let status = cmd.status().await.unwrap();
assert!(status.installed);
assert!(status.is_pmat_managed);
assert!(status.config_up_to_date);
assert!(status.last_updated.is_some());
assert!(status.hook_content_preview.is_some());
}
#[tokio::test]
async fn test_status_non_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\necho 'custom hook'",
)
.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let status = cmd.status().await.unwrap();
assert!(status.installed);
assert!(!status.is_pmat_managed);
assert!(!status.config_up_to_date);
}
#[tokio::test]
async fn test_verify_no_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.verify(false).await.unwrap();
assert!(!result.is_valid);
assert!(result.issues.iter().any(|i| i.contains("not installed")));
}
#[tokio::test]
async fn test_verify_with_fix_installs_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.verify(true).await.unwrap();
assert!(result.is_valid);
assert!(result.fixes_applied.iter().any(|f| f.contains("Installed")));
assert!(hooks_dir.join("pre-commit").exists());
}
#[tokio::test]
async fn test_verify_non_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(hooks_dir.join("pre-commit"), "#!/bin/bash\necho 'custom'").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.verify(false).await.unwrap();
assert!(!result.is_valid);
assert!(result.issues.iter().any(|i| i.contains("not PMAT-managed")));
}
#[tokio::test]
async fn test_verify_outdated_hook_with_fix() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(hooks_dir.join("pre-commit"), "#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT\n# Generated at: 2020-01-01 00:00:00\necho old").unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.verify(true).await.unwrap();
assert!(result.is_valid);
assert!(result.fixes_applied.iter().any(|f| f.contains("Updated")));
}
#[tokio::test]
async fn test_refresh_no_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.refresh().await.unwrap();
assert!(!result.success);
assert!(!result.hook_updated);
assert!(result.message.contains("No hook to refresh"));
}
#[tokio::test]
async fn test_refresh_non_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(hooks_dir.join("pre-commit"), "#!/bin/bash\necho 'custom'").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.refresh().await.unwrap();
assert!(!result.success);
assert!(!result.hook_updated);
assert!(result.message.contains("not PMAT-managed"));
}
#[tokio::test]
async fn test_refresh_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(hooks_dir.join("pre-commit"), "#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT\n# Generated at: 2020-01-01 00:00:00\nold content").unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.refresh().await.unwrap();
assert!(result.success);
assert!(result.config_changes_detected);
}
#[tokio::test]
async fn test_run_no_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.run(false, false).await.unwrap();
assert!(!result.success);
assert!(result.output.contains("not installed"));
}
#[tokio::test]
async fn test_run_with_hook_verbose() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\necho '✅ passed'\nexit 0",
)
.unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(hooks_dir.join("pre-commit"))
.unwrap()
.permissions();
perms.set_mode(0o755);
fs::set_permissions(hooks_dir.join("pre-commit"), perms).unwrap();
}
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.run(false, true).await.unwrap();
assert!(result.success);
assert_eq!(result.checks_passed, 1); assert_eq!(result.checks_failed, 0);
}
#[tokio::test]
async fn test_run_all_files_mode() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\necho 'running all'\nexit 0",
)
.unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(hooks_dir.join("pre-commit"))
.unwrap()
.permissions();
perms.set_mode(0o755);
fs::set_permissions(hooks_dir.join("pre-commit"), perms).unwrap();
}
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.run(true, true).await.unwrap();
assert!(result.success);
}
#[test]
fn test_is_pmat_managed_nonexistent() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.is_pmat_managed(&hooks_dir.join("pre-commit")).unwrap();
assert!(!result);
}
#[test]
fn test_is_pmat_managed_custom_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(hooks_dir.join("pre-commit"), "#!/bin/bash\necho custom").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.is_pmat_managed(&hooks_dir.join("pre-commit")).unwrap();
assert!(!result);
}
#[test]
fn test_is_pmat_managed_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT",
)
.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.is_pmat_managed(&hooks_dir.join("pre-commit")).unwrap();
assert!(result);
}
#[test]
fn test_is_pmat_managed_partial_marker() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\necho test",
)
.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.is_pmat_managed(&hooks_dir.join("pre-commit")).unwrap();
assert!(!result);
}
#[test]
fn test_generate_hook_header() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let cmd = HooksCommand::new(hooks_dir, config_path);
let header = cmd.generate_hook_header();
assert!(header.contains("#!/bin/bash"));
assert!(header.contains("auto-managed by PMAT"));
assert!(header.contains("DO NOT EDIT"));
assert!(header.contains("set -e"));
}
#[test]
fn test_generate_quality_checks() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let cmd = HooksCommand::new(hooks_dir, config_path);
let checks = cmd.generate_quality_checks();
assert!(checks.contains("pmat analyze complexity"));
assert!(checks.contains("pmat analyze satd"));
assert!(checks.contains("exit 0"));
}
#[test]
fn test_generate_quality_checks_handles_non_code_repos() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let cmd = HooksCommand::new(hooks_dir, config_path);
let checks = cmd.generate_quality_checks();
assert!(checks.contains("HAS_SOURCE_FILES"));
assert!(checks.contains("non-code"));
assert!(checks.contains("no source files in repo"));
assert!(checks.contains("STAGED_SRC"));
assert!(checks.contains("no source files staged"));
assert!(checks.contains("docs/execution"));
}
#[test]
fn test_generate_config_content() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let cmd = HooksCommand::new(hooks_dir, config_path);
let config = cmd.generate_config_content(15, 20, 85, 3);
assert!(config.contains("max_complexity = 15"));
assert!(config.contains("max_cognitive_complexity = 20"));
assert!(config.contains("min_coverage = 85"));
assert!(config.contains("max_satd_comments = 3"));
assert!(config.contains("[quality]"));
assert!(config.contains("[hooks]"));
}
#[test]
fn test_extract_current_value() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let cmd = HooksCommand::new(hooks_dir, config_path);
let content = "max_complexity = 20\nmax_cognitive_complexity = 25";
assert_eq!(cmd.extract_current_value(content, "max_complexity"), "20");
assert_eq!(
cmd.extract_current_value(content, "max_cognitive_complexity"),
"25"
);
assert_eq!(cmd.extract_current_value(content, "nonexistent"), "10"); }
#[test]
fn test_update_config_values() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let cmd = HooksCommand::new(hooks_dir, config_path);
let content = "max_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80";
let updated = cmd.update_config_values(content, 20, 30, 90, 5);
assert!(updated.contains("max_complexity = 20"));
assert!(updated.contains("max_cognitive_complexity = 30"));
assert!(updated.contains("min_coverage = 90"));
}
#[tokio::test]
async fn test_install_tdg_hooks_no_git() {
let temp_dir = TempDir::new().unwrap();
let result = install_tdg_hooks(temp_dir.path()).await;
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Not a git repository"));
}
#[tokio::test]
async fn test_install_tdg_hooks_success() {
let temp_dir = TempDir::new().unwrap();
let git_dir = temp_dir.path().join(".git");
fs::create_dir_all(&git_dir).unwrap();
let result = install_tdg_hooks(temp_dir.path()).await;
assert!(result.is_ok());
assert!(git_dir.join("hooks").join("pre-commit").exists());
assert!(git_dir.join("hooks").join("post-commit").exists());
}
#[test]
fn test_install_tdg_pre_commit_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
fs::create_dir_all(&hooks_dir).unwrap();
let config = TdgHooksConfig::default();
let result = install_tdg_pre_commit_hook(&hooks_dir, &config);
assert!(result.is_ok());
let hook_path = hooks_dir.join("pre-commit");
assert!(hook_path.exists());
let content = fs::read_to_string(&hook_path).unwrap();
assert!(content.contains("PMAT TDG Enforcement"));
assert!(content.contains("B+")); }
#[test]
fn test_install_tdg_post_commit_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
fs::create_dir_all(&hooks_dir).unwrap();
let config = TdgHooksConfig::default();
let result = install_tdg_post_commit_hook(&hooks_dir, &config);
assert!(result.is_ok());
let hook_path = hooks_dir.join("post-commit");
assert!(hook_path.exists());
let content = fs::read_to_string(&hook_path).unwrap();
assert!(content.contains("PMAT TDG Baseline Auto-Update"));
assert!(content.contains(".pmat/baseline.json")); }
#[test]
fn test_print_installed_status() {
let status = HookStatus {
installed: true,
is_pmat_managed: true,
config_up_to_date: true,
last_updated: Some("2025-01-01 12:00:00".to_string()),
hook_content_preview: Some("#!/bin/bash\necho test".to_string()),
};
print_installed_status(&status);
}
#[test]
fn test_print_installed_status_not_managed() {
let status = HookStatus {
installed: true,
is_pmat_managed: false,
config_up_to_date: false,
last_updated: None,
hook_content_preview: None,
};
print_installed_status(&status);
}
#[test]
fn test_print_verification_issues_empty() {
let result = HookVerificationResult {
is_valid: true,
issues: vec![],
fixes_applied: vec![],
};
print_verification_issues(&result);
}
#[test]
fn test_print_verification_issues_with_issues() {
let result = HookVerificationResult {
is_valid: false,
issues: vec!["Issue 1".to_string(), "Issue 2".to_string()],
fixes_applied: vec![],
};
print_verification_issues(&result);
}
#[test]
fn test_print_verification_fixes_empty() {
let result = HookVerificationResult {
is_valid: true,
issues: vec![],
fixes_applied: vec![],
};
print_verification_fixes(&result);
}
#[test]
fn test_print_verification_fixes_with_fixes() {
let result = HookVerificationResult {
is_valid: true,
issues: vec![],
fixes_applied: vec!["Fix 1".to_string(), "Fix 2".to_string()],
};
print_verification_fixes(&result);
}
#[tokio::test]
async fn test_handle_hooks_command_init() {
let cmd = HooksCommands::Init {
interactive: false,
force: false,
backup: false,
tdg_enforcement: false,
};
let _ = handle_hooks_command(&cmd).await;
}
#[tokio::test]
async fn test_handle_hooks_command_uninstall() {
let cmd = HooksCommands::Uninstall {
restore_backup: false,
};
let _ = handle_hooks_command(&cmd).await;
}
#[tokio::test]
async fn test_handle_hooks_command_refresh() {
let cmd = HooksCommands::Refresh;
let _ = handle_hooks_command(&cmd).await;
}
#[tokio::test]
async fn test_handle_hooks_command_run() {
let cmd = HooksCommands::Run {
all_files: false,
verbose: false,
cache: true,
};
let _ = handle_hooks_command(&cmd).await;
}
#[tokio::test]
async fn test_handle_hooks_command_run_verbose() {
let cmd = HooksCommands::Run {
all_files: true,
verbose: true,
cache: false,
};
let _ = handle_hooks_command(&cmd).await;
}
#[test]
fn test_normalize_hook_content_empty() {
let normalized = HooksCommand::normalize_hook_content("");
assert_eq!(normalized, "");
}
#[test]
fn test_normalize_hook_content_no_timestamp() {
let content = "#!/bin/bash\necho test\nexit 0";
let normalized = HooksCommand::normalize_hook_content(content);
assert_eq!(normalized, content);
}
#[test]
fn test_normalize_hook_content_multiple_timestamps() {
let content = "#!/bin/bash\n# Generated at: 2025-01-01\necho test\n# Generated at: 2025-01-02\nexit 0";
let normalized = HooksCommand::normalize_hook_content(content);
assert!(!normalized.contains("Generated at:"));
assert!(normalized.contains("#!/bin/bash"));
assert!(normalized.contains("echo test"));
}
#[test]
#[ignore] fn test_detect_project_type_unknown() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let cmd = HooksCommand::new(hooks_dir, config_path);
let project_type = cmd.detect_project_type();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(project_type, "Unknown");
}
#[test]
#[ignore] fn test_detect_project_type_rust() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::write(
temp_dir.path().join("Cargo.toml"),
"[package]\nname = \"test\"",
)
.unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let cmd = HooksCommand::new(hooks_dir, config_path);
let project_type = cmd.detect_project_type();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(project_type, "Rust");
}
#[test]
#[ignore] fn test_detect_project_type_javascript() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::write(temp_dir.path().join("package.json"), "{}").unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let cmd = HooksCommand::new(hooks_dir, config_path);
let project_type = cmd.detect_project_type();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(project_type, "JavaScript/TypeScript");
}
#[test]
#[ignore] fn test_detect_project_type_python_pyproject() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::write(
temp_dir.path().join("pyproject.toml"),
"[project]\nname = \"test\"",
)
.unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let cmd = HooksCommand::new(hooks_dir, config_path);
let project_type = cmd.detect_project_type();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(project_type, "Python");
}
#[test]
#[ignore] fn test_detect_project_type_python_setup() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::write(
temp_dir.path().join("setup.py"),
"from setuptools import setup",
)
.unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let cmd = HooksCommand::new(hooks_dir, config_path);
let project_type = cmd.detect_project_type();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(project_type, "Python");
}
#[test]
#[ignore] fn test_detect_project_type_go() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::write(temp_dir.path().join("go.mod"), "module example.com/test").unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let cmd = HooksCommand::new(hooks_dir, config_path);
let project_type = cmd.detect_project_type();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(project_type, "Go");
}
#[tokio::test]
async fn test_install_existing_pmat_hook_no_force() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(
hooks_dir.join("pre-commit"),
"#!/bin/bash\n# auto-managed by PMAT\n# DO NOT EDIT\nold content",
)
.unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.install(false, false, false).await.unwrap();
assert!(result.success);
assert!(result.hook_created);
}
#[tokio::test]
async fn test_verify_valid_pmat_hook() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path.clone());
let _ = cmd.install(false, false, false).await.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.verify(false).await.unwrap();
assert!(result.is_valid);
assert!(result.issues.is_empty());
}
#[tokio::test]
async fn test_refresh_already_up_to_date() {
let temp_dir = TempDir::new().unwrap();
let hooks_dir = temp_dir.path().join("hooks");
let config_path = temp_dir.path().join("pmat.toml");
fs::create_dir_all(&hooks_dir).unwrap();
fs::write(&config_path, "[quality]\nmax_complexity = 10\nmax_cognitive_complexity = 15\nmin_coverage = 80.0\nallow_satd = false\nrequire_docs = true\nlint_compliance = true\nfail_on_violation = true").unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path.clone());
let _ = cmd.install(false, false, false).await.unwrap();
let cmd = HooksCommand::new(hooks_dir.clone(), config_path);
let result = cmd.refresh().await.unwrap();
assert!(result.success);
}
}