use std::fs;
use tauri_plugin_typegen::commands::{analyze_commands, generate_models};
use tauri_plugin_typegen::models::*;
use tempfile::TempDir;
fn create_test_project_with_commands() -> TempDir {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir_all(&src_dir).unwrap();
let commands_content = r#"
use serde::{Deserialize, Serialize};
use tauri::command;
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
pub id: i32,
pub name: String,
pub email: String,
}
#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
pub name: String,
pub email: String,
}
#[command]
pub async fn create_user(request: CreateUserRequest) -> Result<User, String> {
Ok(User {
id: 1,
name: request.name,
email: request.email,
})
}
#[command]
pub fn get_users() -> Result<Vec<User>, String> {
Ok(vec![])
}
#[command]
pub fn calculate(a: i32, b: i32) -> i32 {
a + b
}
"#;
fs::write(src_dir.join("commands.rs"), commands_content).unwrap();
let lib_content = r#"
pub mod commands;
"#;
fs::write(src_dir.join("lib.rs"), lib_content).unwrap();
temp_dir
}
#[tokio::test]
async fn test_analyze_commands_success() {
let temp_dir = create_test_project_with_commands();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let request = AnalyzeCommandsRequest { project_path };
let result = analyze_commands(request).await;
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.commands.len(), 3);
let command_names: Vec<String> = response.commands.iter().map(|c| c.name.clone()).collect();
assert!(command_names.contains(&"create_user".to_string()));
assert!(command_names.contains(&"get_users".to_string()));
assert!(command_names.contains(&"calculate".to_string()));
}
#[tokio::test]
async fn test_analyze_commands_invalid_path() {
let request = AnalyzeCommandsRequest {
project_path: "/nonexistent/path".to_string(),
};
let result = analyze_commands(request).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_analyze_commands_empty_project() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path().to_str().unwrap().to_string();
fs::create_dir_all(temp_dir.path().join("src")).unwrap();
fs::write(temp_dir.path().join("src/lib.rs"), "// Empty file").unwrap();
let request = AnalyzeCommandsRequest { project_path };
let result = analyze_commands(request).await;
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.commands.len(), 0);
}
#[tokio::test]
async fn test_generate_models_success() {
let temp_dir = create_test_project_with_commands();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let output_path = temp_dir
.path()
.join("generated")
.to_str()
.unwrap()
.to_string();
let request = GenerateModelsRequest {
project_path,
output_path: Some(output_path.clone()),
validation_library: Some("zod".to_string()),
};
let result = generate_models(request).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.commands_found > 0);
assert!(response.types_generated > 0);
assert_eq!(response.generated_files.len(), 3);
let output_dir = temp_dir.path().join("generated");
assert!(output_dir.join("types.ts").exists());
assert!(output_dir.join("commands.ts").exists());
assert!(output_dir.join("index.ts").exists());
}
#[tokio::test]
async fn test_generate_models_with_default_output_path() {
let temp_dir = create_test_project_with_commands();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let request = GenerateModelsRequest {
project_path: project_path.clone(),
output_path: None,
validation_library: Some("zod".to_string()),
};
let result = generate_models(request).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.commands_found > 0);
let default_output = temp_dir.path().join("generated");
assert!(default_output.join("types.ts").exists());
}
#[tokio::test]
async fn test_generate_models_without_validation() {
let temp_dir = create_test_project_with_commands();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let output_path = temp_dir
.path()
.join("generated")
.to_str()
.unwrap()
.to_string();
let request = GenerateModelsRequest {
project_path,
output_path: Some(output_path),
validation_library: Some("none".to_string()),
};
let result = generate_models(request).await;
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.generated_files.len(), 3);
assert!(response.generated_files.contains(&"types.ts".to_string()));
assert!(response
.generated_files
.contains(&"commands.ts".to_string()));
assert!(response.generated_files.contains(&"index.ts".to_string()));
assert!(!response.generated_files.contains(&"schemas.ts".to_string()));
}
#[tokio::test]
async fn test_generate_models_invalid_project_path() {
let request = GenerateModelsRequest {
project_path: "/nonexistent/path".to_string(),
output_path: None,
validation_library: None,
};
let result = generate_models(request).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_generate_models_counts_accurate() {
let temp_dir = create_test_project_with_commands();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let output_path = temp_dir
.path()
.join("generated")
.to_str()
.unwrap()
.to_string();
let request = GenerateModelsRequest {
project_path,
output_path: Some(output_path),
validation_library: Some("zod".to_string()),
};
let result = generate_models(request).await;
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.commands_found, 3);
assert_eq!(response.types_generated, 3);
}
#[tokio::test]
async fn test_generate_models_creates_output_directory() {
let temp_dir = create_test_project_with_commands();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let output_path = temp_dir
.path()
.join("deep/nested/output")
.to_str()
.unwrap()
.to_string();
assert!(!temp_dir.path().join("deep").exists());
let request = GenerateModelsRequest {
project_path,
output_path: Some(output_path.clone()),
validation_library: Some("zod".to_string()),
};
let result = generate_models(request).await;
assert!(result.is_ok());
assert!(temp_dir.path().join("deep/nested/output").exists());
assert!(temp_dir.path().join("deep/nested/output/types.ts").exists());
}
#[tokio::test]
async fn test_command_detail_extraction() {
let temp_dir = create_test_project_with_commands();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let request = AnalyzeCommandsRequest { project_path };
let result = analyze_commands(request).await;
assert!(result.is_ok());
let response = result.unwrap();
let create_user = response
.commands
.iter()
.find(|c| c.name == "create_user")
.expect("create_user command should be found");
assert!(create_user.is_async);
assert_eq!(create_user.parameters.len(), 1);
assert_eq!(create_user.parameters[0].name, "request");
assert_eq!(create_user.return_type, "User");
assert!(create_user.file_path.ends_with("commands.rs"));
let calculate = response
.commands
.iter()
.find(|c| c.name == "calculate")
.expect("calculate command should be found");
assert!(!calculate.is_async);
assert_eq!(calculate.parameters.len(), 2);
assert_eq!(calculate.parameters[0].name, "a");
assert_eq!(calculate.parameters[0].typescript_type, "number");
assert_eq!(calculate.parameters[1].name, "b");
assert_eq!(calculate.parameters[1].typescript_type, "number");
assert_eq!(calculate.return_type, "number");
}
#[tokio::test]
async fn test_generate_models_error_handling() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir_all(&src_dir).unwrap();
fs::write(src_dir.join("broken.rs"), "invalid rust syntax {{{").unwrap();
let project_path = temp_dir.path().to_str().unwrap().to_string();
let output_path = temp_dir
.path()
.join("generated")
.to_str()
.unwrap()
.to_string();
let request = GenerateModelsRequest {
project_path,
output_path: Some(output_path),
validation_library: Some("zod".to_string()),
};
let result = generate_models(request).await;
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.commands_found, 0);
}