#![cfg(feature = "demo")]
#![cfg(feature = "demo")]
use pmat::demo::server::EnhancedHotspot;
use pmat::demo::{DemoContent, LocalDemoServer};
use std::collections::HashMap;
use std::time::Duration;
use tokio::time::timeout;
fn create_test_demo_content() -> DemoContent {
DemoContent {
mermaid_diagram: "graph TD\n A --> B".to_string(),
system_diagram: None,
files_analyzed: 10,
functions_analyzed: 5,
avg_complexity: 5.5,
p90_complexity: 15,
hotspot_functions: 2,
quality_score: 0.85,
tech_debt_hours: 20,
hotspots: vec![],
language_stats: HashMap::new(),
ast_time_ms: 100,
complexity_time_ms: 150,
churn_time_ms: 200,
dag_time_ms: 250,
recommendations: vec![],
polyglot_analysis: None,
}
}
#[tokio::test]
async fn test_demo_server_startup_and_shutdown() {
let content = create_test_demo_content();
let (server, port) = LocalDemoServer::spawn(content.clone()).await.unwrap();
assert!(port > 0);
let client = reqwest::Client::new();
let response = client
.get(format!("http://127.0.0.1:{port}/"))
.send()
.await
.unwrap();
assert_eq!(response.status(), 200);
server.shutdown();
let result = timeout(Duration::from_secs(2), async {
loop {
if client
.get(format!("http://127.0.0.1:{port}/"))
.send()
.await
.is_err()
{
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
})
.await;
assert!(result.is_ok(), "Server did not shut down in time");
}
#[tokio::test]
async fn test_demo_server_api_endpoints() {
let mut content = create_test_demo_content();
content.mermaid_diagram = "graph TD\n A[Main] --> B[Utils]\n A --> C[Helpers]".to_string();
content.files_analyzed = 25;
content.avg_complexity = 8.3;
content.tech_debt_hours = 45;
content.hotspots = vec![
EnhancedHotspot {
function: "complex_algorithm".to_string(),
file: "src/complex.rs".to_string(),
path: "src/complex.rs".to_string(),
complexity: 15,
loc: 80,
language: "rust".to_string(),
churn_score: 80,
refactor_suggestion: "Consider breaking down this function".to_string(),
},
EnhancedHotspot {
function: "utility_helper".to_string(),
file: "src/utils.rs".to_string(),
path: "src/utils.rs".to_string(),
complexity: 12,
loc: 60,
language: "rust".to_string(),
churn_score: 60,
refactor_suggestion: "Extract common patterns".to_string(),
},
];
content.ast_time_ms = 120;
content.complexity_time_ms = 180;
content.churn_time_ms = 240;
content.dag_time_ms = 300;
let (server, port) = LocalDemoServer::spawn(content).await.unwrap();
let client = reqwest::Client::new();
let base_url = format!("http://127.0.0.1:{port}");
let response = client.get(&base_url).send().await.unwrap();
assert_eq!(response.status(), 200);
let html = response.text().await.unwrap();
assert!(html.contains("<!DOCTYPE html>"));
assert!(html.contains("PAIML MCP Agent Toolkit Demo"));
assert!(html.contains("25")); assert!(html.contains("8.3"));
let response = client
.get(format!("{base_url}/api/summary"))
.send()
.await
.unwrap();
assert_eq!(response.status(), 200);
let summary: serde_json::Value = response.json().await.unwrap();
assert_eq!(summary["files_analyzed"], 25);
assert_eq!(summary["avg_complexity"], 8.3);
assert_eq!(summary["tech_debt_hours"], 45);
assert_eq!(summary["time_context"], 100);
let response = client
.get(format!("{base_url}/api/metrics"))
.send()
.await
.unwrap();
assert_eq!(response.status(), 200);
let metrics: serde_json::Value = response.json().await.unwrap();
assert_eq!(metrics["files_analyzed"], 25);
let response = client
.get(format!("{base_url}/api/hotspots"))
.send()
.await
.unwrap();
assert_eq!(response.status(), 200);
let hotspots: Vec<serde_json::Value> = response.json().await.unwrap();
assert_eq!(hotspots.len(), 5);
let response = client
.get(format!("{base_url}/api/dag"))
.send()
.await
.unwrap();
assert_eq!(response.status(), 200);
let dag = response.text().await.unwrap();
assert!(dag.contains("graph TD"));
let response = client
.get(format!("{base_url}/nonexistent"))
.send()
.await
.unwrap();
assert_eq!(response.status(), 404);
server.shutdown();
}
#[tokio::test]
async fn test_demo_server_static_assets() {
let mut content = create_test_demo_content();
content.mermaid_diagram = "graph TD".to_string();
content.files_analyzed = 1;
content.avg_complexity = 1.0;
content.tech_debt_hours = 0;
let (server, port) = LocalDemoServer::spawn(content).await.unwrap();
let client = reqwest::Client::new();
let base_url = format!("http://127.0.0.1:{port}");
let vendor_paths = vec!["/vendor/gridjs.min.js", "/vendor/gridjs-mermaid.min.css"];
for path in vendor_paths {
let response = client
.get(format!("{base_url}{path}"))
.send()
.await
.unwrap();
assert!(
response.status() == 200 || response.status() == 404,
"Unexpected status for {}: {}",
path,
response.status()
);
}
server.shutdown();
}
#[tokio::test]
async fn test_demo_server_concurrent_requests() {
let mut content = create_test_demo_content();
content.mermaid_diagram = "graph TD\n A --> B".to_string();
content.files_analyzed = 100;
content.avg_complexity = std::f64::consts::PI;
content.tech_debt_hours = 8;
let (server, port) = LocalDemoServer::spawn(content).await.unwrap();
let base_url = format!("http://127.0.0.1:{port}");
let mut handles = vec![];
for i in 0..10 {
let url = base_url.clone();
let handle = tokio::spawn(async move {
let client = reqwest::Client::new();
let endpoint = match i % 4 {
0 => "/",
1 => "/api/summary",
2 => "/api/metrics",
_ => "/api/dag",
};
let response = client.get(format!("{url}{endpoint}")).send().await.unwrap();
assert_eq!(response.status(), 200);
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
server.shutdown();
}
#[tokio::test]
async fn test_demo_server_response_headers() {
let mut content = create_test_demo_content();
content.mermaid_diagram = "graph TD".to_string();
content.files_analyzed = 1;
content.avg_complexity = 1.0;
content.tech_debt_hours = 0;
let (server, port) = LocalDemoServer::spawn(content).await.unwrap();
let client = reqwest::Client::new();
let base_url = format!("http://127.0.0.1:{port}");
let response = client.get(&base_url).send().await.unwrap();
assert_eq!(
response.headers().get("content-type").unwrap(),
"text/html; charset=utf-8"
);
assert_eq!(response.headers().get("cache-control").unwrap(), "no-cache");
let response = client
.get(format!("{base_url}/api/summary"))
.send()
.await
.unwrap();
assert_eq!(
response.headers().get("content-type").unwrap(),
"application/json"
);
let response = client
.get(format!("{base_url}/api/dag"))
.send()
.await
.unwrap();
assert_eq!(
response.headers().get("content-type").unwrap(),
"text/plain"
);
server.shutdown();
}
#[tokio::test]
async fn test_demo_content_rendering() {
let mut content = create_test_demo_content();
content.mermaid_diagram =
"graph TD\n API[API Server] --> DB[Database]\n API --> Cache[Redis Cache]".to_string();
content.files_analyzed = 42;
content.avg_complexity = 6.78;
content.tech_debt_hours = 123;
content.hotspots = vec![
EnhancedHotspot {
function: "main".to_string(),
file: "src/main.rs".to_string(),
path: "src/main.rs".to_string(),
complexity: 25,
loc: 150,
language: "rust".to_string(),
churn_score: 95,
refactor_suggestion: "High complexity function needs refactoring".to_string(),
},
EnhancedHotspot {
function: "handle_api".to_string(),
file: "src/handlers/api.rs".to_string(),
path: "src/handlers/api.rs".to_string(),
complexity: 18,
loc: 120,
language: "rust".to_string(),
churn_score: 70,
refactor_suggestion: "Consider breaking into smaller functions".to_string(),
},
EnhancedHotspot {
function: "database_query".to_string(),
file: "src/services/database.rs".to_string(),
path: "src/services/database.rs".to_string(),
complexity: 15,
loc: 100,
language: "rust".to_string(),
churn_score: 50,
refactor_suggestion: "Extract query builder patterns".to_string(),
},
];
let (server, port) = LocalDemoServer::spawn(content).await.unwrap();
let client = reqwest::Client::new();
let response = client
.get(format!("http://127.0.0.1:{port}/"))
.send()
.await
.unwrap();
let html = response.text().await.unwrap();
assert!(html.contains("42")); assert!(html.contains("6.78"));
assert!(html.contains("100")); assert!(html.contains("150")); assert!(html.contains("200")); assert!(html.contains("250"));
server.shutdown();
}
#[cfg(test)]
mod demo_web_tests {
use super::*;
use pmat::models::dag::{DependencyGraph, NodeInfo, NodeType};
#[tokio::test]
async fn test_demo_server_starts() {
let mut content = create_test_demo_content();
content.mermaid_diagram = String::from("graph TD\n A[Test] --> B[Demo]");
content.files_analyzed = 10;
content.avg_complexity = 5.5;
content.tech_debt_hours = 12;
content.hotspots = vec![EnhancedHotspot {
function: "test_function".to_string(),
file: String::from("test.rs"),
path: String::from("test.rs"),
complexity: 15,
loc: 80,
language: "rust".to_string(),
churn_score: 80,
refactor_suggestion: "Test function needs optimization".to_string(),
}];
let (server, port) = LocalDemoServer::spawn(content)
.await
.expect("Failed to start demo server");
assert!(port > 0);
assert_eq!(server.port(), port);
}
#[test]
fn test_demo_content_from_analysis() {
let mut graph = DependencyGraph::new();
graph.add_node(NodeInfo {
id: "test".to_string(),
label: "Test Node".to_string(),
node_type: NodeType::Function,
file_path: String::new(),
line_number: 0,
complexity: 5,
metadata: rustc_hash::FxHashMap::default(),
});
let content =
DemoContent::from_analysis_results(&graph, 10, 5.5, 12, vec![], 100, 150, 200, 250);
assert_eq!(content.files_analyzed, 10);
assert_eq!(content.avg_complexity, 5.5);
assert_eq!(content.tech_debt_hours, 12);
assert!(content.mermaid_diagram.contains("graph TD"));
assert!(content.mermaid_diagram.contains("test[Test Node]"));
}
}