use crate::demo::{detect_repository, DemoReport, DemoRunner, DemoStep};
use crate::models::mcp::{McpError, McpRequest, McpResponse};
use crate::stateless_server::StatelessTemplateServer;
use serde_json::{json, Value};
use std::path::PathBuf;
use std::sync::Arc;
use tempfile::TempDir;
use tokio::fs;
#[tokio::test]
async fn test_demo_runner_creation() {
let server = Arc::new(StatelessTemplateServer::new().unwrap());
let runner = DemoRunner::new(server);
assert!(std::mem::size_of_val(&runner) > 0);
}
#[tokio::test]
async fn test_demo_step_structure() {
let step = DemoStep {
name: "test_analysis".to_string(),
capability: "analysis",
request: McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "analyze_complexity".to_string(),
params: Some(json!({"project_path": "./"})),
},
response: McpResponse {
jsonrpc: "2.0".to_string(),
id: json!(1),
result: Some(json!({"status": "success"})),
error: None,
},
elapsed_ms: 150,
success: true,
output: Some(json!({"files_analyzed": 42})),
};
assert_eq!(step.name, "test_analysis");
assert_eq!(step.capability, "analysis");
assert_eq!(step.elapsed_ms, 150);
assert!(step.success);
assert!(step.output.is_some());
}
#[tokio::test]
async fn test_demo_report_structure() {
let report = DemoReport {
repository: "/test/project".to_string(),
total_time_ms: 2500,
steps: vec![],
system_diagram: Some("graph TD; A --> B".to_string()),
analysis: crate::demo::runner::DemoAnalysisResult {
files_analyzed: 10,
functions_analyzed: 5,
avg_complexity: 3.2,
hotspot_functions: 1,
quality_score: 0.85,
tech_debt_hours: 2,
qa_verification: None,
language_stats: None,
complexity_metrics: None,
},
execution_time_ms: 2500,
};
assert_eq!(report.repository, "/test/project");
assert_eq!(report.total_time_ms, 2500);
assert_eq!(report.steps.len(), 0);
assert!(report.system_diagram.is_some());
assert_eq!(report.system_diagram.unwrap(), "graph TD; A --> B");
}
#[tokio::test]
async fn test_detect_repository_git_repo() {
let temp_dir = TempDir::new().unwrap();
let repo_path = temp_dir.path();
let git_dir = repo_path.join(".git");
fs::create_dir(&git_dir).await.unwrap();
fs::write(git_dir.join("HEAD"), "ref: refs/heads/master")
.await
.unwrap();
let src_dir = repo_path.join("src");
fs::create_dir(&src_dir).await.unwrap();
fs::write(src_dir.join("main.rs"), "fn main() {}")
.await
.unwrap();
fs::write(repo_path.join("Cargo.toml"), "[package]\nname = \"test\"")
.await
.unwrap();
let detected = detect_repository(Some(repo_path.to_path_buf())).unwrap();
assert_eq!(detected, repo_path);
}
#[tokio::test]
async fn test_detect_repository_cargo_project() {
let temp_dir = TempDir::new().unwrap();
let repo_path = temp_dir.path();
let git_dir = repo_path.join(".git");
fs::create_dir(&git_dir).await.unwrap();
fs::write(git_dir.join("HEAD"), "ref: refs/heads/master")
.await
.unwrap();
let src_dir = repo_path.join("src");
fs::create_dir(&src_dir).await.unwrap();
fs::write(src_dir.join("main.rs"), "fn main() {}")
.await
.unwrap();
fs::write(repo_path.join("Cargo.toml"), "[package]\nname = \"test\"")
.await
.unwrap();
let detected = detect_repository(Some(repo_path.to_path_buf())).unwrap();
assert_eq!(detected, repo_path);
}
#[tokio::test]
async fn test_detect_repository_nodejs_project() {
let temp_dir = TempDir::new().unwrap();
let repo_path = temp_dir.path();
let git_dir = repo_path.join(".git");
fs::create_dir(&git_dir).await.unwrap();
fs::write(git_dir.join("HEAD"), "ref: refs/heads/master")
.await
.unwrap();
fs::write(repo_path.join("package.json"), r#"{"name": "test"}"#)
.await
.unwrap();
fs::write(repo_path.join("index.js"), "console.log('hello');")
.await
.unwrap();
let detected = detect_repository(Some(repo_path.to_path_buf())).unwrap();
assert_eq!(detected, repo_path);
}
#[tokio::test]
async fn test_detect_repository_python_project() {
let temp_dir = TempDir::new().unwrap();
let repo_path = temp_dir.path();
let git_dir = repo_path.join(".git");
fs::create_dir(&git_dir).await.unwrap();
fs::write(git_dir.join("HEAD"), "ref: refs/heads/master")
.await
.unwrap();
fs::write(repo_path.join("setup.py"), "from setuptools import setup")
.await
.unwrap();
fs::write(repo_path.join("main.py"), "print('hello')")
.await
.unwrap();
let detected = detect_repository(Some(repo_path.to_path_buf())).unwrap();
assert_eq!(detected, repo_path);
}
#[tokio::test]
async fn test_detect_repository_pyproject_toml() {
let temp_dir = TempDir::new().unwrap();
let repo_path = temp_dir.path();
let git_dir = repo_path.join(".git");
fs::create_dir(&git_dir).await.unwrap();
fs::write(git_dir.join("HEAD"), "ref: refs/heads/master")
.await
.unwrap();
fs::write(
repo_path.join("pyproject.toml"),
"[build-system]\nrequires = [\"setuptools\"]",
)
.await
.unwrap();
fs::write(repo_path.join("main.py"), "print('hello')")
.await
.unwrap();
let detected = detect_repository(Some(repo_path.to_path_buf())).unwrap();
assert_eq!(detected, repo_path);
}
#[tokio::test]
async fn test_detect_repository_with_readme() {
let temp_dir = TempDir::new().unwrap();
let repo_path = temp_dir.path();
let git_dir = repo_path.join(".git");
fs::create_dir(&git_dir).await.unwrap();
fs::write(git_dir.join("HEAD"), "ref: refs/heads/master")
.await
.unwrap();
fs::write(repo_path.join("README.md"), "# Test Project")
.await
.unwrap();
fs::write(repo_path.join("script.py"), "print('hello')")
.await
.unwrap();
let detected = detect_repository(Some(repo_path.to_path_buf())).unwrap();
assert_eq!(detected, repo_path);
}
#[tokio::test]
async fn test_detect_repository_empty_directory() {
let temp_dir = TempDir::new().unwrap();
let repo_path = temp_dir.path();
assert!(repo_path.exists());
assert!(repo_path.is_dir());
assert!(!repo_path.join(".git").exists());
}
#[tokio::test]
async fn test_detect_repository_nonexistent_path() {
let nonexistent_path = PathBuf::from("/nonexistent/path/to/repo");
let result = tokio::time::timeout(
std::time::Duration::from_secs(5),
tokio::task::spawn_blocking({
let nonexistent_path = nonexistent_path.clone();
move || detect_repository(Some(nonexistent_path))
}),
)
.await;
match result {
Ok(Ok(detect_result)) => {
assert!(detect_result.is_err());
}
Ok(Err(_)) => {
panic!("detect_repository task panicked");
}
Err(_) => {
eprintln!("Warning: test_detect_repository_nonexistent_path timed out - skipping due to filesystem issues");
return;
}
}
}
#[tokio::test]
async fn test_demo_report_rendering_cli() {
use crate::cli::ExecutionMode;
let step = DemoStep {
name: "complexity_analysis".to_string(),
capability: "analysis",
request: McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "analyze_complexity".to_string(),
params: Some(json!({"project_path": "./"})),
},
response: McpResponse {
jsonrpc: "2.0".to_string(),
id: json!(1),
result: Some(json!({"median_complexity": 2.5})),
error: None,
},
elapsed_ms: 250,
success: true,
output: Some(json!({"files_analyzed": 15})),
};
let report = DemoReport {
repository: "/test/project".to_string(),
total_time_ms: 2500,
steps: vec![step],
system_diagram: Some("graph TD; A --> B".to_string()),
analysis: crate::demo::runner::DemoAnalysisResult {
files_analyzed: 10,
functions_analyzed: 5,
avg_complexity: 3.2,
hotspot_functions: 1,
quality_score: 0.85,
tech_debt_hours: 2,
qa_verification: None,
language_stats: None,
complexity_metrics: None,
},
execution_time_ms: 2500,
};
let rendered = report.render(ExecutionMode::Cli);
assert!(rendered.contains("/test/project"));
assert!(rendered.contains("analysis")); assert!(rendered.contains("250 ms")); assert!(rendered.contains("2500 ms")); }
#[tokio::test]
async fn test_demo_report_rendering_mcp() {
use crate::cli::ExecutionMode;
let step = DemoStep {
name: "ast_analysis".to_string(),
capability: "parsing",
request: McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "analyze_ast".to_string(),
params: Some(json!({"language": "rust"})),
},
response: McpResponse {
jsonrpc: "2.0".to_string(),
id: json!(1),
result: Some(json!({"functions_found": 42})),
error: None,
},
elapsed_ms: 180,
success: true,
output: Some(json!({"ast_nodes": 1250})),
};
let report = DemoReport {
repository: "/test/rust-project".to_string(),
total_time_ms: 1800,
steps: vec![step],
system_diagram: None,
analysis: crate::demo::runner::DemoAnalysisResult {
files_analyzed: 10,
functions_analyzed: 5,
avg_complexity: 3.2,
hotspot_functions: 1,
quality_score: 0.85,
tech_debt_hours: 2,
qa_verification: None,
language_stats: None,
complexity_metrics: None,
},
execution_time_ms: 1800,
};
let rendered = report.render(ExecutionMode::Mcp);
let parsed: Value = serde_json::from_str(&rendered).unwrap();
assert_eq!(parsed["repository"], "/test/rust-project");
assert_eq!(parsed["total_time_ms"], 1800);
assert_eq!(parsed["steps"].as_array().unwrap().len(), 1);
}
#[tokio::test]
async fn test_demo_step_error_handling() {
let error_step = DemoStep {
name: "failed_analysis".to_string(),
capability: "analysis",
request: McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "analyze_complexity".to_string(),
params: Some(json!({"invalid": "params"})),
},
response: McpResponse {
jsonrpc: "2.0".to_string(),
id: json!(1),
result: None,
error: Some(McpError {
code: -32602,
message: "Invalid params".to_string(),
data: None,
}),
},
elapsed_ms: 50,
success: false,
output: None,
};
assert!(!error_step.success);
assert!(error_step.output.is_none());
assert!(error_step.response.error.is_some());
assert_eq!(error_step.elapsed_ms, 50);
}
#[tokio::test]
async fn test_demo_report_with_multiple_steps() {
use crate::cli::ExecutionMode;
let steps = vec![
DemoStep {
name: "step1".to_string(),
capability: "analysis",
request: McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "analyze_complexity".to_string(),
params: Some(json!({})),
},
response: McpResponse {
jsonrpc: "2.0".to_string(),
id: json!(1),
result: Some(json!({"success": true})),
error: None,
},
elapsed_ms: 100,
success: true,
output: Some(json!({"files": 10})),
},
DemoStep {
name: "step2".to_string(),
capability: "parsing",
request: McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(2),
method: "analyze_ast".to_string(),
params: Some(json!({})),
},
response: McpResponse {
jsonrpc: "2.0".to_string(),
id: json!(2),
result: Some(json!({"success": true})),
error: None,
},
elapsed_ms: 150,
success: true,
output: Some(json!({"functions": 25})),
},
];
let report = DemoReport {
repository: "/multi/step/project".to_string(),
total_time_ms: 250,
steps,
system_diagram: None,
analysis: crate::demo::runner::DemoAnalysisResult {
files_analyzed: 10,
functions_analyzed: 5,
avg_complexity: 3.2,
hotspot_functions: 1,
quality_score: 0.85,
tech_debt_hours: 2,
qa_verification: None,
language_stats: None,
complexity_metrics: None,
},
execution_time_ms: 250,
};
assert_eq!(report.steps.len(), 2);
assert_eq!(report.total_time_ms, 250);
let rendered = report.render(ExecutionMode::Cli);
assert!(rendered.contains("analysis")); assert!(rendered.contains("parsing")); assert!(rendered.contains("100 ms")); assert!(rendered.contains("150 ms")); }
#[test]
fn test_demo_step_serialization() {
let step = DemoStep {
name: "test_step".to_string(),
capability: "testing",
request: McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "test_method".to_string(),
params: Some(json!({"param": "value"})),
},
response: McpResponse {
jsonrpc: "2.0".to_string(),
id: json!(1),
result: Some(json!({"result": "success"})),
error: None,
},
elapsed_ms: 200,
success: true,
output: Some(json!({"output": "data"})),
};
let serialized = serde_json::to_string(&step).unwrap();
assert!(serialized.contains("test_step"));
assert!(serialized.contains("testing"));
assert!(serialized.contains("200"));
let test_json = r#"{"name":"test_step","capability":"testing","request":{"jsonrpc":"2.0","id":1,"method":"test_method","params":{"param":"value"}},"response":{"jsonrpc":"2.0","id":1,"result":{"result":"success"},"error":null},"elapsed_ms":200,"success":true,"output":{"output":"data"}}"#;
let deserialized: DemoStep = serde_json::from_str(test_json).unwrap();
assert_eq!(deserialized.name, "test_step");
assert_eq!(deserialized.capability, "testing");
assert_eq!(deserialized.elapsed_ms, 200);
assert!(deserialized.success);
}
#[test]
fn test_demo_report_serialization() {
let report = DemoReport {
repository: "/test/project".to_string(),
total_time_ms: 1500,
steps: vec![],
system_diagram: Some("test diagram".to_string()),
analysis: crate::demo::runner::DemoAnalysisResult {
files_analyzed: 10,
functions_analyzed: 5,
avg_complexity: 3.2,
hotspot_functions: 1,
quality_score: 0.85,
tech_debt_hours: 2,
qa_verification: None,
language_stats: None,
complexity_metrics: None,
},
execution_time_ms: 1500,
};
let serialized = serde_json::to_string(&report).unwrap();
assert!(serialized.contains("/test/project"));
assert!(serialized.contains("1500"));
assert!(serialized.contains("test diagram"));
let parsed: Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(parsed["repository"], "/test/project");
assert_eq!(parsed["total_time_ms"], 1500);
assert!(parsed["steps"].is_array());
assert_eq!(parsed["system_diagram"], "test diagram");
}