#![cfg_attr(coverage_nightly, coverage(off))]
use crate::handlers::tools::handle_tool_call;
use crate::models::mcp::{McpRequest, ToolCallParams};
use crate::stateless_server::StatelessTemplateServer;
use serde_json::{json, Value};
use std::sync::Arc;
fn create_test_server() -> Arc<StatelessTemplateServer> {
Arc::new(StatelessTemplateServer::new().unwrap())
}
fn create_tool_request(tool_name: &str, arguments: Value) -> McpRequest {
let params = ToolCallParams {
name: tool_name.to_string(),
arguments,
};
McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "tools/call".to_string(),
params: Some(serde_json::to_value(params).unwrap()),
}
}
#[tokio::test]
async fn test_claude_code_rust_cli_workflow() {
let server = create_test_server();
let request = create_tool_request(
"list_templates",
json!({
"toolchain": "rust"
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.result.is_some());
assert!(response.error.is_none());
let result = response.result.unwrap();
let templates = result["templates"].as_array().unwrap();
assert_eq!(templates.len(), 3);
let request = create_tool_request(
"scaffold_project",
json!({
"toolchain": "rust",
"templates": ["makefile", "readme", "gitignore"],
"parameters": {
"project_name": "my-rust-cli",
"author_name": "Your Name",
"author_email": "your.email@example.com",
"description": "A Rust CLI application"
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.result.is_some());
assert!(response.error.is_none());
let result = response.result.unwrap();
let generated = result["generated"].as_array().unwrap();
assert_eq!(generated.len(), 3);
for file in generated {
let template_type = file["template"].as_str().unwrap();
let filename = file["filename"].as_str().unwrap();
let content = file["content"].as_str().unwrap();
match template_type {
"makefile" => {
assert_eq!(filename, "my-rust-cli/Makefile");
assert!(content.contains("my-rust-cli"));
assert!(content.contains("cargo build"));
}
"readme" => {
assert_eq!(filename, "my-rust-cli/README.md");
assert!(content.contains("my-rust-cli"));
assert!(content.contains("A Rust CLI application"));
}
"gitignore" => {
assert_eq!(filename, "my-rust-cli/.gitignore");
assert!(content.contains("target/") || content.contains("Build artifacts"));
}
_ => panic!("Unexpected template type: {template_type}"),
}
}
let request = create_tool_request(
"generate_template",
json!({
"resource_uri": "template://makefile/rust/cli",
"parameters": {
"project_name": "my-rust-cli"
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.result.is_some());
assert!(response.error.is_none());
let result = response.result.unwrap();
let content = result["content"][0]["text"].as_str().unwrap();
assert!(content.contains("my-rust-cli"));
assert!(content.contains("Rust CLI Binary Makefile"));
}
#[tokio::test]
async fn test_claude_code_all_languages_scaffold() {
let server = create_test_server();
let test_cases = create_scaffold_test_cases();
for (toolchain, project_name, description) in test_cases {
test_toolchain_scaffolding(&server, toolchain, project_name, description).await;
}
}
fn create_scaffold_test_cases() -> Vec<(&'static str, &'static str, &'static str)> {
vec![
("rust", "my-rust-project", "A Rust project"),
("deno", "my-deno-project", "A Deno TypeScript project"),
("python-uv", "my-python-project", "A Python UV project"),
]
}
async fn test_toolchain_scaffolding(
server: &Arc<StatelessTemplateServer>,
toolchain: &str,
project_name: &str,
description: &str,
) {
let request = create_scaffold_request(toolchain, project_name, description);
let response = handle_tool_call(server.clone(), request).await;
validate_scaffold_response(&response, toolchain);
let result = response.result.unwrap();
let generated = result["generated"].as_array().unwrap();
assert_eq!(generated.len(), 3, "Wrong number of files for {toolchain}");
verify_generated_files(generated, toolchain, project_name, description);
}
fn create_scaffold_request(toolchain: &str, project_name: &str, description: &str) -> McpRequest {
create_tool_request(
"scaffold_project",
json!({
"toolchain": toolchain,
"templates": ["makefile", "readme", "gitignore"],
"parameters": {
"project_name": project_name,
"description": description,
"author_name": "Test Author"
}
}),
)
}
fn validate_scaffold_response(response: &crate::models::mcp::McpResponse, toolchain: &str) {
assert!(
response.result.is_some(),
"Failed for toolchain: {toolchain}"
);
assert!(response.error.is_none(), "Error for toolchain: {toolchain}");
}
fn verify_generated_files(
generated: &[Value],
toolchain: &str,
project_name: &str,
description: &str,
) {
let mut file_flags = GeneratedFileFlags::new();
for file in generated {
process_generated_file(file, &mut file_flags, toolchain, project_name, description);
}
file_flags.assert_all_files_present(toolchain);
}
struct GeneratedFileFlags {
has_makefile: bool,
has_readme: bool,
has_gitignore: bool,
}
impl GeneratedFileFlags {
fn new() -> Self {
Self {
has_makefile: false,
has_readme: false,
has_gitignore: false,
}
}
fn assert_all_files_present(&self, toolchain: &str) {
assert!(self.has_makefile, "Missing Makefile for {toolchain}");
assert!(self.has_readme, "Missing README for {toolchain}");
assert!(self.has_gitignore, "Missing .gitignore for {toolchain}");
}
}
fn process_generated_file(
file: &Value,
flags: &mut GeneratedFileFlags,
toolchain: &str,
project_name: &str,
description: &str,
) {
match file["template"].as_str().unwrap() {
"makefile" => {
flags.has_makefile = true;
verify_makefile(file, toolchain, project_name);
}
"readme" => {
flags.has_readme = true;
verify_readme(file, project_name, description);
}
"gitignore" => {
flags.has_gitignore = true;
verify_gitignore(file, toolchain, project_name);
}
_ => {}
}
}
fn verify_makefile(file: &Value, toolchain: &str, project_name: &str) {
assert_eq!(
file["filename"].as_str().unwrap(),
&format!("{project_name}/Makefile")
);
let content = file["content"].as_str().unwrap();
assert!(content.contains(project_name));
verify_makefile_toolchain_specific(content, toolchain);
}
fn verify_makefile_toolchain_specific(content: &str, toolchain: &str) {
match toolchain {
"rust" => assert!(content.contains("cargo")),
"deno" => assert!(content.contains("deno")),
"python-uv" => assert!(content.contains("uv")),
_ => {}
}
}
fn verify_readme(file: &Value, project_name: &str, description: &str) {
assert_eq!(
file["filename"].as_str().unwrap(),
&format!("{project_name}/README.md")
);
let content = file["content"].as_str().unwrap();
assert!(content.contains(project_name));
assert!(content.contains(description));
}
fn verify_gitignore(file: &Value, toolchain: &str, project_name: &str) {
assert_eq!(
file["filename"].as_str().unwrap(),
&format!("{project_name}/.gitignore")
);
let content = file["content"].as_str().unwrap();
verify_gitignore_patterns(content, toolchain);
}
fn verify_gitignore_patterns(content: &str, toolchain: &str) {
match toolchain {
"rust" => assert!(content.contains("/target/")),
"deno" => assert!(content.contains("deno.lock")),
"python-uv" => assert!(content.contains("__pycache__")),
_ => {}
}
}
#[tokio::test]
async fn test_claude_code_error_scenarios() {
let server = create_test_server();
let request = create_tool_request(
"generate_template",
json!({
"resource_uri": "template://readme/rust/invalid-variant", "parameters": {
"project_name": "test-project"
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.error.is_some());
assert_eq!(response.error.unwrap().code, -32001);
let request = create_tool_request(
"generate_template",
json!({
"resource_uri": "template://makefile/rust/cli",
"parameters": {} }),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.error.is_some());
let request = create_tool_request(
"scaffold_project",
json!({
"toolchain": "invalid-toolchain",
"templates": ["makefile"],
"parameters": {
"project_name": "test"
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.result.is_some());
let result = response.result.unwrap();
let generated = result["generated"].as_array().unwrap();
assert!(generated.is_empty()); }
#[tokio::test]
async fn test_claude_code_search_templates() {
let server = create_test_server();
let request = create_tool_request(
"search_templates",
json!({
"query": "readme",
"toolchain": "rust"
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.result.is_some());
assert!(response.error.is_none());
let result = response.result.unwrap();
let templates = result["templates"].as_array().unwrap();
assert_eq!(templates.len(), 1); assert_eq!(
templates[0]["uri"].as_str().unwrap(),
"template://readme/rust/cli"
);
}
#[tokio::test]
async fn test_naming_convention_critical_requirement() {
let server = create_test_server();
let test_cases = vec![
("rust", "test-rust-project", "A Rust CLI project"),
("deno", "test-deno-project", "A Deno TypeScript project"),
("python-uv", "test-python-project", "A Python UV project"),
];
for (toolchain, project_name, description) in test_cases {
let request = create_tool_request(
"scaffold_project",
json!({
"toolchain": toolchain,
"templates": ["makefile", "readme", "gitignore"],
"parameters": {
"project_name": project_name,
"description": description,
"author_name": "Test Author",
"author_email": "test@example.com",
"github_username": "testuser"
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(
response.result.is_some(),
"Failed for toolchain: {toolchain}"
);
assert!(response.error.is_none(), "Error for toolchain: {toolchain}");
let result = response.result.unwrap();
let generated = result["generated"].as_array().unwrap();
for file in generated {
let content = file["content"].as_str().unwrap();
let filename = file["filename"].as_str().unwrap();
assert!(
!content.contains("mcp-agent-toolkit") || content.contains("pmat"),
"Found incorrect 'mcp-agent-toolkit' (without paiml- prefix) in {filename} for toolchain {toolchain}"
);
assert!(
!content.contains("paiml-agent-toolkit"),
"Found incorrect 'paiml-agent-toolkit' (missing -mcp-) in {filename} for toolchain {toolchain}"
);
assert!(
!content.contains("mcp_server_stateless"),
"Found old binary name 'mcp_server_stateless' in {filename} for toolchain {toolchain}"
);
assert!(
!content.contains("mcp-server-"),
"Found old artifact pattern 'mcp-server-' in {filename} for toolchain {toolchain}"
);
if file["template"].as_str().unwrap() == "readme" {
assert!(
content.contains("pmat")
|| content.contains("PAIML MCP Agent Toolkit")
|| !content.to_lowercase().contains("toolkit"), "README should use correct project name in {filename} for toolchain {toolchain}"
);
}
}
}
}
#[tokio::test]
async fn test_naming_convention_in_individual_templates() {
let server = create_test_server();
let template_uris = vec![
"template://makefile/rust/cli",
"template://makefile/deno/cli",
"template://makefile/python-uv/cli",
"template://readme/rust/cli",
"template://readme/deno/cli",
"template://readme/python-uv/cli",
"template://gitignore/rust/cli",
"template://gitignore/deno/cli",
"template://gitignore/python-uv/cli",
];
for uri in template_uris {
let request = create_tool_request(
"generate_template",
json!({
"resource_uri": uri,
"parameters": {
"project_name": "test-project",
"description": "Test project for naming validation",
"author_name": "Test Author",
"author_email": "test@example.com",
"github_username": "testuser"
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
if response.result.is_some() {
let result = response.result.unwrap();
let content = result["content"][0]["text"].as_str().unwrap();
assert!(
!content.contains("mcp-agent-toolkit") || content.contains("pmat"),
"Found incorrect 'mcp-agent-toolkit' in template {uri}"
);
assert!(
!content.contains("paiml-agent-toolkit"),
"Found incorrect 'paiml-agent-toolkit' in template {uri}"
);
assert!(
!content.contains("mcp_server_stateless"),
"Found old binary name in template {uri}"
);
assert!(
!content.contains("mcp-server-"),
"Found old artifact pattern in template {uri}"
);
}
}
}
#[tokio::test]
async fn test_server_info_naming_convention() {
let server = create_test_server();
let request = create_tool_request("get_server_info", json!({}));
let response = handle_tool_call(server.clone(), request).await;
if response.result.is_some() {
let result = response.result.unwrap();
let info_str = serde_json::to_string(&result).unwrap();
assert!(
!info_str.contains("mcp-agent-toolkit") || info_str.contains("pmat"),
"Server info contains incorrect 'mcp-agent-toolkit' naming"
);
assert!(
!info_str.contains("paiml-agent-toolkit"),
"Server info contains incorrect 'paiml-agent-toolkit' naming"
);
assert!(
!info_str.contains("mcp_server_stateless"),
"Server info contains old binary name"
);
}
}
#[tokio::test]
async fn test_ast_context_generation() {
let server = create_test_server();
let temp_dir = tempfile::tempdir().unwrap();
let temp_path = temp_dir.path();
let test_file_content = r#"
use std::collections::HashMap;
pub mod utils {
/// Helper.
pub fn helper() {}
}
#[derive(Debug, Clone)]
/// Test struct.
pub struct TestStruct {
field1: String,
field2: i32,
}
/// Test enum.
pub enum TestEnum {
Variant1,
Variant2(String),
}
/// Trait defining Test trait behavior.
pub trait TestTrait {
fn method(&self);
}
impl TestTrait for TestStruct {
fn method(&self) {
println!("Implementation");
}
}
pub async fn async_function() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
fn private_function() {
// Private function
}
"#;
std::fs::write(temp_path.join("test.rs"), test_file_content).unwrap();
let cargo_toml = r#"
[package]
name = "test-project"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = "1.0"
serde = "1.0"
"#;
std::fs::write(temp_path.join("Cargo.toml"), cargo_toml).unwrap();
let request = create_tool_request(
"generate_template",
json!({
"resource_uri": "template://context/rust/ast",
"parameters": {
"project_path": temp_path.to_str().unwrap()
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.result.is_some());
assert!(response.error.is_none());
let result = response.result.unwrap();
let content = result["content"][0]["text"].as_str().unwrap();
eprintln!("Generated content:\n{content}");
assert!(content.contains("# Project Context: rust Project"));
assert!(content.contains("## Summary"));
assert!(content.contains("Files analyzed: 1"));
assert!(content.contains("Functions: 2")); assert!(content.contains("Structs: 1")); assert!(content.contains("Enums: 1")); assert!(content.contains("Traits: 1")); assert!(content.contains("Implementations: 1"));
assert!(content.contains("## Dependencies"));
assert!(content.contains("- tokio"));
assert!(content.contains("- serde"));
assert!(content.contains("## Files"));
assert!(content.contains("test.rs"));
assert!(content.contains("**Modules:**"));
assert!(content.contains("`pub mod utils`"));
assert!(content.contains("**Structs:**"));
assert!(content.contains("`pub struct TestStruct` (2 fields)"));
assert!(content.contains("**Enums:**"));
assert!(content.contains("`pub enum TestEnum` (2 variants)"));
assert!(content.contains("**Traits:**"));
assert!(content.contains("`pub trait TestTrait`"));
assert!(content.contains("**Functions:**"));
assert!(content.contains("`pub async fn async_function`"));
assert!(content.contains("`private fn private_function`"));
assert!(content.contains("**Implementations:**"));
assert!(content.contains("`impl TestTrait for TestStruct`"));
assert_eq!(result["filename"].as_str().unwrap(), "CONTEXT.md");
let request = create_tool_request(
"generate_template",
json!({
"resource_uri": "template://context/invalid/ast",
"parameters": {
"project_path": temp_path.to_str().unwrap()
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.error.is_some());
let request = create_tool_request(
"generate_template",
json!({
"resource_uri": "template://context/rust/ast",
"parameters": {
"project_path": "/non/existent/path"
}
}),
);
let response = handle_tool_call(server.clone(), request).await;
assert!(response.result.is_some());
let result = response.result.unwrap();
let content = result["content"][0]["text"].as_str().unwrap();
assert!(content.contains("Files analyzed: 0"));
}