461 lines
15 KiB
Rust
461 lines
15 KiB
Rust
|
|
#[cfg(test)]
|
||
|
|
#[allow(clippy::module_inception)]
|
||
|
|
mod tests {
|
||
|
|
use std::collections::HashMap;
|
||
|
|
use std::path::PathBuf;
|
||
|
|
|
||
|
|
use tempfile::TempDir;
|
||
|
|
|
||
|
|
use crate::tools::guidance::*;
|
||
|
|
|
||
|
|
fn create_test_system_status() -> SystemStatus {
|
||
|
|
let paths = SystemPaths {
|
||
|
|
provisioning_root: PathBuf::from("/test"),
|
||
|
|
workspace_dir: Some(PathBuf::from("/test/workspace")),
|
||
|
|
docs_dir: PathBuf::from("/test/docs"),
|
||
|
|
config_dir: PathBuf::from("/test/config"),
|
||
|
|
};
|
||
|
|
|
||
|
|
SystemStatus {
|
||
|
|
nushell_version: Some("0.107.1".to_string()),
|
||
|
|
workspace_configured: true,
|
||
|
|
providers: vec!["aws".to_string(), "upcloud".to_string()],
|
||
|
|
orchestrator_running: true,
|
||
|
|
services_status: HashMap::new(),
|
||
|
|
issues: Vec::new(),
|
||
|
|
paths,
|
||
|
|
metadata: HashMap::new(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_system_status_creation() {
|
||
|
|
let status = create_test_system_status();
|
||
|
|
assert!(status.nushell_version.is_some());
|
||
|
|
assert_eq!(status.providers.len(), 2);
|
||
|
|
assert!(status.workspace_configured);
|
||
|
|
assert!(status.orchestrator_running);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_issue_severity_ordering() {
|
||
|
|
assert!(IssueSeverity::Critical > IssueSeverity::Error);
|
||
|
|
assert!(IssueSeverity::Error > IssueSeverity::Warning);
|
||
|
|
assert!(IssueSeverity::Warning > IssueSeverity::Info);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_health_status_variants() {
|
||
|
|
let statuses = [
|
||
|
|
HealthStatus::Healthy,
|
||
|
|
HealthStatus::Degraded,
|
||
|
|
HealthStatus::Unhealthy,
|
||
|
|
HealthStatus::Unknown,
|
||
|
|
];
|
||
|
|
|
||
|
|
assert_eq!(statuses.len(), 4);
|
||
|
|
assert_eq!(HealthStatus::Healthy, HealthStatus::Healthy);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_risk_level_variants() {
|
||
|
|
let risk_low = RiskLevel::Low;
|
||
|
|
let risk_medium = RiskLevel::Medium;
|
||
|
|
let risk_high = RiskLevel::High;
|
||
|
|
|
||
|
|
assert_eq!(risk_low, RiskLevel::Low);
|
||
|
|
assert_ne!(risk_low, risk_high);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_next_action_priority() {
|
||
|
|
let action = NextAction {
|
||
|
|
command: "test".to_string(),
|
||
|
|
description: "test action".to_string(),
|
||
|
|
docs_link: "docs/test.md".to_string(),
|
||
|
|
expected_outcome: "success".to_string(),
|
||
|
|
priority: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
assert_eq!(action.priority, 1);
|
||
|
|
assert!(!action.command.is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_doc_reference_relevance() {
|
||
|
|
let doc = DocReference {
|
||
|
|
path: "docs/test.md".to_string(),
|
||
|
|
title: "Test Document".to_string(),
|
||
|
|
snippet: "This is a test snippet".to_string(),
|
||
|
|
relevance: 0.95,
|
||
|
|
section: Some("Introduction".to_string()),
|
||
|
|
};
|
||
|
|
|
||
|
|
assert!(doc.relevance > 0.9);
|
||
|
|
assert!(doc.relevance <= 1.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_validation_result_with_errors() {
|
||
|
|
let result = ValidationResult {
|
||
|
|
valid: false,
|
||
|
|
errors: vec![ValidationError {
|
||
|
|
message: "Test error".to_string(),
|
||
|
|
path: "config.toml".to_string(),
|
||
|
|
line: Some(10),
|
||
|
|
fix: Some("Fix it".to_string()),
|
||
|
|
}],
|
||
|
|
warnings: Vec::new(),
|
||
|
|
suggestions: Vec::new(),
|
||
|
|
docs: Vec::new(),
|
||
|
|
};
|
||
|
|
|
||
|
|
assert!(!result.valid);
|
||
|
|
assert_eq!(result.errors.len(), 1);
|
||
|
|
assert_eq!(result.errors[0].line, Some(10));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_diagnosis_with_fixes() {
|
||
|
|
let diagnosis = Diagnosis {
|
||
|
|
error: "Nushell not found".to_string(),
|
||
|
|
root_cause: "Nushell is not installed".to_string(),
|
||
|
|
fixes: vec![Fix {
|
||
|
|
description: "Install Nushell".to_string(),
|
||
|
|
commands: vec!["brew install nushell".to_string()],
|
||
|
|
expected_outcome: "Nushell installed".to_string(),
|
||
|
|
risk: RiskLevel::Low,
|
||
|
|
}],
|
||
|
|
related_docs: Vec::new(),
|
||
|
|
confidence: 0.95,
|
||
|
|
};
|
||
|
|
|
||
|
|
assert_eq!(diagnosis.fixes.len(), 1);
|
||
|
|
assert!(diagnosis.confidence > 0.9);
|
||
|
|
assert_eq!(diagnosis.fixes[0].risk, RiskLevel::Low);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_system_status_tool_creation() {
|
||
|
|
let _tool = SystemStatusTool::new(PathBuf::from("/test"));
|
||
|
|
// Tool created successfully - type check is the test
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_next_action_tool_creation() {
|
||
|
|
let _tool = NextActionTool::new(PathBuf::from("/test"));
|
||
|
|
// Tool created successfully - type check is the test
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_doc_finder_tool_creation() {
|
||
|
|
let _tool = DocFinderTool::new(PathBuf::from("/test"));
|
||
|
|
// Tool created successfully - type check is the test
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_troubleshooter_tool_creation() {
|
||
|
|
let _tool = TroubleshooterTool::new(PathBuf::from("/test"));
|
||
|
|
// Tool created successfully - type check is the test
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_config_validator_tool_creation() {
|
||
|
|
let _tool = ConfigValidatorTool::new(PathBuf::from("/test"));
|
||
|
|
// Tool created successfully - type check is the test
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_system_status_with_issues() {
|
||
|
|
let mut status = create_test_system_status();
|
||
|
|
status.issues.push(Issue {
|
||
|
|
severity: IssueSeverity::Warning,
|
||
|
|
category: "Configuration".to_string(),
|
||
|
|
description: "Test warning".to_string(),
|
||
|
|
fix: Some("Run command".to_string()),
|
||
|
|
docs_link: Some("docs/help.md".to_string()),
|
||
|
|
});
|
||
|
|
|
||
|
|
assert_eq!(status.issues.len(), 1);
|
||
|
|
assert_eq!(status.issues[0].severity, IssueSeverity::Warning);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_service_status_healthy() {
|
||
|
|
let service = ServiceStatus {
|
||
|
|
name: "orchestrator".to_string(),
|
||
|
|
running: true,
|
||
|
|
version: Some("1.0.0".to_string()),
|
||
|
|
health: HealthStatus::Healthy,
|
||
|
|
last_checked: "2025-10-10T00:00:00Z".to_string(),
|
||
|
|
};
|
||
|
|
|
||
|
|
assert!(service.running);
|
||
|
|
assert_eq!(service.health, HealthStatus::Healthy);
|
||
|
|
assert!(service.version.is_some());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_next_action_tool_suggest_with_critical_issue() {
|
||
|
|
let tool = NextActionTool::new(PathBuf::from("/test"));
|
||
|
|
let mut status = create_test_system_status();
|
||
|
|
|
||
|
|
// Add critical issue
|
||
|
|
status.issues.push(Issue {
|
||
|
|
severity: IssueSeverity::Critical,
|
||
|
|
category: "Prerequisites".to_string(),
|
||
|
|
description: "Nushell not installed".to_string(),
|
||
|
|
fix: Some("brew install nushell".to_string()),
|
||
|
|
docs_link: Some("docs/setup.md".to_string()),
|
||
|
|
});
|
||
|
|
|
||
|
|
let result = tool.suggest_next_action(&status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let action = result.unwrap();
|
||
|
|
assert_eq!(action.priority, 1);
|
||
|
|
assert!(action.command.contains("brew install nushell"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_next_action_tool_workspace_not_configured() {
|
||
|
|
let tool = NextActionTool::new(PathBuf::from("/test"));
|
||
|
|
let mut status = create_test_system_status();
|
||
|
|
status.workspace_configured = false;
|
||
|
|
|
||
|
|
let result = tool.suggest_next_action(&status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let action = result.unwrap();
|
||
|
|
assert_eq!(action.priority, 2);
|
||
|
|
assert!(action.command.contains("workspace init"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_next_action_tool_no_providers() {
|
||
|
|
let tool = NextActionTool::new(PathBuf::from("/test"));
|
||
|
|
let mut status = create_test_system_status();
|
||
|
|
status.providers.clear();
|
||
|
|
|
||
|
|
let result = tool.suggest_next_action(&status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let action = result.unwrap();
|
||
|
|
assert_eq!(action.priority, 3);
|
||
|
|
assert!(action.command.contains("discover providers"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_next_action_tool_orchestrator_not_running() {
|
||
|
|
let tool = NextActionTool::new(PathBuf::from("/test"));
|
||
|
|
let mut status = create_test_system_status();
|
||
|
|
status.orchestrator_running = false;
|
||
|
|
|
||
|
|
let result = tool.suggest_next_action(&status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let action = result.unwrap();
|
||
|
|
assert_eq!(action.priority, 4);
|
||
|
|
assert!(action.command.contains("orchestrator"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_troubleshooter_nushell_error() {
|
||
|
|
let tool = TroubleshooterTool::new(PathBuf::from("/test"));
|
||
|
|
let status = create_test_system_status();
|
||
|
|
|
||
|
|
let result = tool.diagnose_issue("command not found: nu", &status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let diagnosis = result.unwrap();
|
||
|
|
assert!(diagnosis.confidence > 0.9);
|
||
|
|
assert!(diagnosis.root_cause.contains("Nushell"));
|
||
|
|
assert!(!diagnosis.fixes.is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_troubleshooter_workspace_error() {
|
||
|
|
let tool = TroubleshooterTool::new(PathBuf::from("/test"));
|
||
|
|
let status = create_test_system_status();
|
||
|
|
|
||
|
|
let result = tool.diagnose_issue("workspace not found", &status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let diagnosis = result.unwrap();
|
||
|
|
assert!(diagnosis.confidence > 0.8);
|
||
|
|
assert!(diagnosis.fixes.iter().any(|f| f
|
||
|
|
.commands
|
||
|
|
.iter()
|
||
|
|
.any(|c: &String| c.contains("workspace init"))));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_troubleshooter_orchestrator_error() {
|
||
|
|
let tool = TroubleshooterTool::new(PathBuf::from("/test"));
|
||
|
|
let status = create_test_system_status();
|
||
|
|
|
||
|
|
let result = tool.diagnose_issue("connection refused to orchestrator", &status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let diagnosis = result.unwrap();
|
||
|
|
assert!(diagnosis.confidence > 0.8);
|
||
|
|
assert!(diagnosis
|
||
|
|
.fixes
|
||
|
|
.iter()
|
||
|
|
.any(|f| f.description.contains("orchestrator")));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_troubleshooter_provider_error() {
|
||
|
|
let tool = TroubleshooterTool::new(PathBuf::from("/test"));
|
||
|
|
let status = create_test_system_status();
|
||
|
|
|
||
|
|
let result = tool.diagnose_issue("provider aws not configured", &status);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let diagnosis = result.unwrap();
|
||
|
|
assert!(!diagnosis.fixes.is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_config_validator_nonexistent_file() {
|
||
|
|
let tool = ConfigValidatorTool::new(PathBuf::from("/test"));
|
||
|
|
let result = tool.validate_config("nonexistent.toml");
|
||
|
|
|
||
|
|
assert!(result.is_ok());
|
||
|
|
let validation = result.unwrap();
|
||
|
|
assert!(!validation.valid);
|
||
|
|
assert!(!validation.errors.is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_multiple_next_actions() {
|
||
|
|
let tool = NextActionTool::new(PathBuf::from("/test"));
|
||
|
|
let mut status = create_test_system_status();
|
||
|
|
|
||
|
|
// Add multiple issues
|
||
|
|
status.issues.push(Issue {
|
||
|
|
severity: IssueSeverity::Critical,
|
||
|
|
category: "Prerequisites".to_string(),
|
||
|
|
description: "Critical issue 1".to_string(),
|
||
|
|
fix: Some("fix 1".to_string()),
|
||
|
|
docs_link: None,
|
||
|
|
});
|
||
|
|
status.issues.push(Issue {
|
||
|
|
severity: IssueSeverity::Critical,
|
||
|
|
category: "Prerequisites".to_string(),
|
||
|
|
description: "Critical issue 2".to_string(),
|
||
|
|
fix: Some("fix 2".to_string()),
|
||
|
|
docs_link: None,
|
||
|
|
});
|
||
|
|
|
||
|
|
let result = tool.suggest_multiple_actions(&status, 3);
|
||
|
|
assert!(result.is_ok());
|
||
|
|
|
||
|
|
let actions = result.unwrap();
|
||
|
|
assert!(actions.len() <= 3);
|
||
|
|
assert!(actions.iter().all(|a| a.priority <= 2));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_system_paths_structure() {
|
||
|
|
let paths = SystemPaths {
|
||
|
|
provisioning_root: PathBuf::from("/usr/local/provisioning"),
|
||
|
|
workspace_dir: Some(PathBuf::from("/workspace")),
|
||
|
|
docs_dir: PathBuf::from("/usr/local/provisioning/docs"),
|
||
|
|
config_dir: PathBuf::from("/usr/local/provisioning/config"),
|
||
|
|
};
|
||
|
|
|
||
|
|
assert_eq!(
|
||
|
|
paths.provisioning_root,
|
||
|
|
PathBuf::from("/usr/local/provisioning")
|
||
|
|
);
|
||
|
|
assert!(paths.workspace_dir.is_some());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_fix_risk_levels() {
|
||
|
|
let low_risk_fix = Fix {
|
||
|
|
description: "Safe fix".to_string(),
|
||
|
|
commands: vec!["echo test".to_string()],
|
||
|
|
expected_outcome: "Success".to_string(),
|
||
|
|
risk: RiskLevel::Low,
|
||
|
|
};
|
||
|
|
|
||
|
|
let high_risk_fix = Fix {
|
||
|
|
description: "Dangerous fix".to_string(),
|
||
|
|
commands: vec!["rm -rf /".to_string()],
|
||
|
|
expected_outcome: "Disaster".to_string(),
|
||
|
|
risk: RiskLevel::High,
|
||
|
|
};
|
||
|
|
|
||
|
|
assert_eq!(low_risk_fix.risk, RiskLevel::Low);
|
||
|
|
assert_eq!(high_risk_fix.risk, RiskLevel::High);
|
||
|
|
assert_ne!(low_risk_fix.risk, high_risk_fix.risk);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_validation_with_warnings_only() {
|
||
|
|
let result = ValidationResult {
|
||
|
|
valid: true,
|
||
|
|
errors: Vec::new(),
|
||
|
|
warnings: vec![ValidationWarning {
|
||
|
|
message: "Consider updating".to_string(),
|
||
|
|
path: "config.yaml".to_string(),
|
||
|
|
recommendation: "Use newer format".to_string(),
|
||
|
|
}],
|
||
|
|
suggestions: vec!["Use TOML instead".to_string()],
|
||
|
|
docs: Vec::new(),
|
||
|
|
};
|
||
|
|
|
||
|
|
assert!(result.valid);
|
||
|
|
assert!(result.errors.is_empty());
|
||
|
|
assert_eq!(result.warnings.len(), 1);
|
||
|
|
assert_eq!(result.suggestions.len(), 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_serialization_system_status() {
|
||
|
|
let status = create_test_system_status();
|
||
|
|
let json = serde_json::to_string(&status);
|
||
|
|
assert!(json.is_ok());
|
||
|
|
|
||
|
|
let deserialized: Result<SystemStatus, _> = serde_json::from_str(&json.unwrap());
|
||
|
|
assert!(deserialized.is_ok());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_serialization_next_action() {
|
||
|
|
let action = NextAction {
|
||
|
|
command: "test command".to_string(),
|
||
|
|
description: "test description".to_string(),
|
||
|
|
docs_link: "docs/test.md".to_string(),
|
||
|
|
expected_outcome: "success".to_string(),
|
||
|
|
priority: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
let json = serde_json::to_string(&action);
|
||
|
|
assert!(json.is_ok());
|
||
|
|
|
||
|
|
let deserialized: Result<NextAction, _> = serde_json::from_str(&json.unwrap());
|
||
|
|
assert!(deserialized.is_ok());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_serialization_diagnosis() {
|
||
|
|
let diagnosis = Diagnosis {
|
||
|
|
error: "test error".to_string(),
|
||
|
|
root_cause: "test cause".to_string(),
|
||
|
|
fixes: Vec::new(),
|
||
|
|
related_docs: Vec::new(),
|
||
|
|
confidence: 0.5,
|
||
|
|
};
|
||
|
|
|
||
|
|
let json = serde_json::to_string(&diagnosis);
|
||
|
|
assert!(json.is_ok());
|
||
|
|
|
||
|
|
let deserialized: Result<Diagnosis, _> = serde_json::from_str(&json.unwrap());
|
||
|
|
assert!(deserialized.is_ok());
|
||
|
|
}
|
||
|
|
}
|