#![allow(dead_code, unused_imports, unused_variables, deprecated, clippy::all)]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::needless_raw_string_hashes,
clippy::duration_suboptimal_units,
clippy::branches_sharing_code,
clippy::used_underscore_binding,
clippy::single_char_pattern,
clippy::ignore_without_reason,
clippy::cloned_ref_to_slice_refs,
clippy::doc_overindented_list_items,
clippy::match_wildcard_for_single_variants,
clippy::ignored_unit_patterns,
clippy::needless_collect,
clippy::unnecessary_map_or,
clippy::manual_flatten,
clippy::manual_strip,
clippy::future_not_send,
clippy::unnested_or_patterns,
clippy::no_effect_underscore_binding,
clippy::literal_string_with_formatting_args
)]
use std::fs;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
use tera::{Context, Tera};
fn create_tera() -> Tera {
let mut tera_instance = Tera::default();
ggen_core::register::register_all(&mut tera_instance);
tera_instance
}
fn read_template(path: &str) -> String {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let full_path = manifest_dir.join("../../").join(path);
fs::read_to_string(&full_path)
.unwrap_or_else(|e| panic!("Failed to read template at {}: {}", full_path.display(), e))
}
fn create_context() -> Context {
let mut ctx = Context::new();
ctx.insert("ontology_path", "test.ttl");
ctx.insert("server_version", "0.1.0");
ctx.insert("handler_name", "TestMcpHandler");
ctx.insert("description", "Test server");
let empty_tools: Vec<serde_json::Value> = vec![];
ctx.insert("tools", &empty_tools);
let empty_resources: Vec<serde_json::Value> = vec![];
ctx.insert("resources", &empty_resources);
let empty_prompts: Vec<serde_json::Value> = vec![];
ctx.insert("prompts", &empty_prompts);
ctx.insert("use_zai", &false);
ctx.insert("use_a2a", &false);
ctx.insert("timestamp", "");
ctx
}
fn setup_temp_dir() -> Result<TempDir, Box<dyn std::error::Error>> {
TempDir::new().map_err(|e| format!("Failed to create temp dir: {}", e).into())
}
fn write_file(
temp_dir: &TempDir, path: &str, content: &str,
) -> Result<PathBuf, Box<dyn std::error::Error>> {
let file_path = temp_dir.path().join(path);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&file_path, content)?;
Ok(file_path)
}
fn create_minimal_ontology(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let ontology = r#"
@prefix mcp: <https://ggen.io/examples/mcp#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
mcp:TestServer a mcp:Server ;
mcp:serverName "test-mcp" ;
mcp:serverStruct "TestMcpServer" ;
mcp:description "Test MCP server" ;
mcp:version "0.1.0" .
mcp:EchoTool a mcp:Tool ;
mcp:serverOf mcp:TestServer ;
mcp:toolName "echo" ;
mcp:structName "EchoParams" ;
mcp:description "Echo back the input" ;
mcp:order 1 .
mcp:MessageParam a mcp:Parameter ;
mcp:toolOf mcp:EchoTool ;
mcp:paramName "message" ;
mcp:paramType "String" ;
mcp:required "required" ;
mcp:brief "Message to echo back" .
"#;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(path, ontology)?;
Ok(())
}
#[test]
fn test_mcp_server_template_is_valid() {
println!("🔍 CLI Test: MCP server template is valid");
let mut tera = create_tera();
let template_content = read_template("templates/mcp-server/stdio_server.rs.tera");
let mut ctx = create_context();
ctx.insert("server_name", "TestMcpServer");
ctx.insert("server_struct", "TestMcpServer");
ctx.insert("description", "Test server");
ctx.insert("version", "0.1.0");
let tools = vec![serde_json::json!({
"name": "echo",
"struct_name": "EchoParams",
"description": "Echo back input",
})];
ctx.insert("tools", &tools);
let result = tera.render_str(&template_content, &ctx);
assert!(result.is_ok(), "Template should render: {:?}", result.err());
let rendered = result.unwrap();
assert!(rendered.contains("TestMcpServer"));
println!("✅ CLI Test PASSED: MCP server template is valid");
}
#[test]
fn test_mcp_generate_output_structure() {
println!("🔍 CLI Test: Output directory structure validation");
let temp_dir = setup_temp_dir().expect("Failed to setup temp dir");
let output_dir = temp_dir.path().join("output");
let mut tera = create_tera();
let mut ctx = create_context();
ctx.insert("server_name", "TestMcpServer");
let template_content = read_template("templates/mcp-server/stdio_server.rs.tera");
let rendered = tera
.render_str(&template_content, &ctx)
.expect("Failed to render template");
fs::create_dir_all(&output_dir).expect("Failed to create output dir");
let main_rs = output_dir.join("main.rs");
fs::write(&main_rs, rendered).expect("Failed to write main.rs");
assert!(output_dir.exists());
assert!(output_dir.is_dir());
assert!(main_rs.exists());
let metadata = fs::metadata(&main_rs).expect("Failed to get metadata");
assert!(metadata.len() > 0);
println!("✅ CLI Test PASSED: Output directory structure is valid");
}
#[test]
fn test_mcp_generate_minimal_ontology() {
println!("🔍 CLI Test: Generation with minimal ontology");
let temp_dir = setup_temp_dir().expect("Failed to setup temp dir");
let ontology_path = temp_dir.path().join("minimal.ttl");
create_minimal_ontology(&ontology_path).expect("Failed to create minimal ontology");
let ontology_content = fs::read_to_string(&ontology_path).expect("Failed to read ontology");
assert!(ontology_content.contains("mcp:TestServer"));
assert!(ontology_content.contains("test-mcp"));
assert!(ontology_content.contains("TestMcpServer"));
assert!(ontology_content.contains("mcp:EchoTool"));
assert!(ontology_content.contains("echo"));
println!("✅ CLI Test PASSED: Minimal ontology structure is valid");
}
#[test]
fn test_mcp_generate_error_missing_template() {
println!("🔍 CLI Test: Error handling for missing template");
let mut tera = create_tera();
let result = tera.render_str("{{ non_existent_function() }}", &create_context());
assert!(result.is_err(), "Non-existent template should fail");
println!("✅ CLI Test PASSED: Missing template produces error");
}
#[test]
fn test_mcp_tool_handler_template_is_valid() {
println!("🔍 CLI Test: Tool handler template is valid");
let mut tera = create_tera();
let template_content = read_template("templates/mcp-server/tool_handler.rs.tera");
let mut ctx = create_context();
ctx.insert("error_type", "McpError");
ctx.insert("handler_context_type", "HandlerContext");
ctx.insert("stream_result_type", "Receiver<String>");
ctx.insert("server_impl_type", "TestMcpServer");
let tools = vec![serde_json::json!({
"name": "echo",
"description": "Echo back input",
"input_schema": "{}",
"input_type": "EchoParams",
"enable_streaming": false,
})];
ctx.insert("tools", &tools);
let result = tera.render_str(&template_content, &ctx);
assert!(result.is_ok(), "Template should render: {:?}", result.err());
let rendered = result.unwrap();
assert!(rendered.contains("EchoHandler"));
assert!(rendered.contains("echo"));
println!("✅ CLI Test PASSED: Tool handler template is valid");
}
#[test]
fn test_mcp_multiple_tools_template() {
println!("🔍 CLI Test: Multiple tools in template");
let mut tera = create_tera();
let template_content = read_template("templates/mcp-server/tool_handler.rs.tera");
let mut ctx = create_context();
ctx.insert("error_type", "McpError");
ctx.insert("handler_context_type", "HandlerContext");
ctx.insert("stream_result_type", "Receiver<String>");
ctx.insert("server_impl_type", "TestMcpServer");
let tools = vec![
serde_json::json!({"name": "tool1", "description": "First", "input_schema": "{}", "input_type": "Tool1Params", "enable_streaming": false}),
serde_json::json!({"name": "tool2", "description": "Second", "input_schema": "{}", "input_type": "Tool2Params", "enable_streaming": false}),
serde_json::json!({"name": "tool3", "description": "Third", "input_schema": "{}", "input_type": "Tool3Params", "enable_streaming": false}),
];
ctx.insert("tools", &tools);
let result = tera.render_str(&template_content, &ctx);
assert!(result.is_ok(), "Template should render: {:?}", result.err());
let rendered = result.unwrap();
assert!(rendered.contains("Tool1Handler"));
assert!(rendered.contains("Tool2Handler"));
assert!(rendered.contains("Tool3Handler"));
println!("✅ CLI Test PASSED: Multiple tools rendered correctly");
}
#[test]
fn test_mcp_server_name_validation() {
println!("🔍 CLI Test: Server name validation");
let valid_names = vec!["GgenMcpServer", "TestServer", "MyServer123", "server_v1"];
let _invalid_names = vec![
"test-server", "test server", "test@server", "123server", ];
for name in valid_names {
let is_valid = name.chars().all(|c| c.is_alphanumeric() || c == '_');
assert!(is_valid, "{} should be a valid Rust identifier", name);
}
println!("✅ CLI Test PASSED: Server name validation works");
}
#[test]
fn test_mcp_tool_order_validation() {
println!("🔍 CLI Test: Tool order validation");
let tools = [("tool1", 1), ("tool2", 2), ("tool3", 3)];
for (idx, (name, order)) in tools.iter().enumerate() {
assert_eq!(
idx + 1,
*order as usize,
"{} should be at order {}",
name,
order
);
}
println!("✅ CLI Test PASSED: Tool order validation works");
}
#[test]
fn test_mcp_template_context_completeness() {
println!("🔍 CLI Test: Template context completeness");
let mut tera = create_tera();
let template_content = read_template("templates/mcp-server/stdio_server.rs.tera");
let mut ctx = create_context();
ctx.insert("server_name", "TestMcpServer");
ctx.insert("server_struct", "TestMcpServer");
ctx.insert("description", "Test server");
ctx.insert("version", "0.1.0");
let tools = vec![serde_json::json!({
"name": "echo",
"struct_name": "EchoParams",
"description": "Echo",
})];
ctx.insert("tools", &tools);
let result = tera.render_str(&template_content, &ctx);
assert!(
result.is_ok(),
"Complete context should render successfully"
);
println!("✅ CLI Test PASSED: Template context is complete");
}
#[test]
fn test_mcp_generated_file_permissions() {
println!("🔍 CLI Test: Generated file permissions");
let temp_dir = setup_temp_dir().expect("Failed to setup temp dir");
let mut tera = create_tera();
let mut ctx = create_context();
ctx.insert("server_name", "TestMcpServer");
let template_content = read_template("templates/mcp-server/stdio_server.rs.tera");
let rendered = tera
.render_str(&template_content, &ctx)
.expect("Failed to render template");
let output_file = temp_dir.path().join("main.rs");
fs::write(&output_file, rendered).expect("Failed to write file");
let metadata = fs::metadata(&output_file).expect("Failed to get metadata");
assert!(metadata.is_file());
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let permissions = metadata.permissions();
let mode = permissions.mode();
assert!(mode & 0o400 != 0, "File should be readable by owner");
}
println!("✅ CLI Test PASSED: Generated file permissions are correct");
}