#[cfg(test)]
#[allow(clippy::module_inception)]
mod tests {
use super::super::*;
use std::path::PathBuf;
use tempfile::TempDir;
fn create_test_cargo_toml() -> types::CargoToml {
types::CargoToml {
package: Some(types::CargoPackage {
name: "test-package".to_string(),
version: "1.0.0".to_string(),
description: Some("Test description".to_string()),
authors: Some(vec!["Test Author <test@example.com>".to_string()]),
keywords: Some(vec!["test".to_string(), "packaging".to_string()]),
rust_version: None,
}),
lib: Some(types::CargoLib {
crate_type: Some(vec!["cdylib".to_string()]),
}),
dependencies: None,
}
}
fn create_mock_library_file() -> (TempDir, PathBuf) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let lib_path = temp_dir.path().join("libtest.so");
std::fs::write(&lib_path, b"mock library content").expect("Failed to write mock library");
(temp_dir, lib_path)
}
fn create_test_project() -> (TempDir, PathBuf) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let project_path = temp_dir.path().to_path_buf();
let src_dir = project_path.join("src");
std::fs::create_dir_all(&src_dir).expect("Failed to create src dir");
let lib_rs_content = r#"
use cloacina::{workflow, task, Context, TaskError};
#[workflow(name = "test_package")]
pub mod test_package {
use super::*;
#[task(id = "simple_task", dependencies = [])]
pub async fn simple_task(ctx: &mut Context<serde_json::Value>) -> Result<(), TaskError> {
Ok(())
}
}
"#;
std::fs::write(src_dir.join("lib.rs"), lib_rs_content).expect("Failed to write lib.rs");
(temp_dir, project_path)
}
#[test]
fn test_generate_manifest_basic() {
let cargo_toml = create_test_cargo_toml();
let (_temp_dir, lib_path) = create_mock_library_file();
let (_project_temp, project_path) = create_test_project();
let result = manifest::generate_manifest(&cargo_toml, &lib_path, &None, &project_path);
match result {
Ok(manifest) => {
assert_eq!(manifest.format_version, "2");
assert_eq!(manifest.package.name, "test-package");
assert_eq!(manifest.package.version, "1.0.0");
assert!(manifest.rust.is_some());
assert!(!manifest.rust.as_ref().unwrap().library_path.is_empty());
}
Err(e) => {
let error_msg = format!("{}", e);
assert!(
error_msg.contains("Failed to load library")
|| error_msg.contains("metadata")
|| error_msg.contains("symbol"),
"Error should be FFI-related: {}",
error_msg
);
}
}
}
#[test]
fn test_generate_manifest_with_target() {
let cargo_toml = create_test_cargo_toml();
let (_temp_dir, lib_path) = create_mock_library_file();
let (_project_temp, project_path) = create_test_project();
let target = Some("x86_64-unknown-linux-gnu".to_string());
let result = manifest::generate_manifest(&cargo_toml, &lib_path, &target, &project_path);
match result {
Ok(manifest) => {
assert!(manifest
.package
.targets
.contains(&"x86_64-unknown-linux-gnu".to_string()));
}
Err(_) => {
}
}
}
#[test]
fn test_generate_manifest_missing_package() {
let mut cargo_toml = create_test_cargo_toml();
cargo_toml.package = None;
let (_temp_dir, lib_path) = create_mock_library_file();
let (_project_temp, project_path) = create_test_project();
let result = manifest::generate_manifest(&cargo_toml, &lib_path, &None, &project_path);
assert!(result.is_err());
let error_msg = format!("{}", result.unwrap_err());
assert!(error_msg.contains("Missing package section"));
}
#[test]
fn test_extract_package_names_from_source() {
let (_temp_dir, project_path) = create_test_project();
let result = manifest::extract_package_names_from_source(&project_path);
match result {
Ok(package_names) => {
assert!(!package_names.is_empty());
assert!(package_names.contains(&"test_package".to_string()));
}
Err(e) => {
panic!("Should be able to extract package names: {}", e);
}
}
}
#[test]
fn test_extract_package_names_no_packages() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let project_path = temp_dir.path().to_path_buf();
let src_dir = project_path.join("src");
std::fs::create_dir_all(&src_dir).expect("Failed to create src dir");
let lib_rs_content = r#"
pub fn regular_function() -> String {
"not a packaged workflow".to_string()
}
"#;
std::fs::write(src_dir.join("lib.rs"), lib_rs_content).expect("Failed to write lib.rs");
let result = manifest::extract_package_names_from_source(&project_path);
match result {
Ok(package_names) => {
assert!(package_names.is_empty());
}
Err(e) => {
panic!("Should handle no packages gracefully: {}", e);
}
}
}
#[test]
fn test_extract_package_names_missing_src() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let project_path = temp_dir.path().to_path_buf();
let result = manifest::extract_package_names_from_source(&project_path);
assert!(result.is_err());
let error_msg = format!("{}", result.unwrap_err());
assert!(error_msg.contains("Failed to read src directory"));
}
#[test]
fn test_get_current_architecture() {
let arch = manifest::get_current_architecture();
assert!(!arch.is_empty());
assert!(
arch == "x86_64"
|| arch == "aarch64"
|| arch == "arm"
|| arch.starts_with("x86")
|| arch.starts_with("aarch")
|| arch.starts_with("arm")
);
}
#[test]
fn test_compile_options_builder_pattern() {
let options = CompileOptions {
target: Some("aarch64-apple-darwin".to_string()),
profile: "release".to_string(),
cargo_flags: vec!["--features".to_string(), "postgres".to_string()],
jobs: Some(8),
};
assert_eq!(options.target.as_ref().unwrap(), "aarch64-apple-darwin");
assert_eq!(options.profile, "release");
assert_eq!(options.cargo_flags.len(), 2);
assert_eq!(options.jobs.unwrap(), 8);
}
#[test]
fn test_manifest_schema_rust_package() {
use chrono::Utc;
let manifest = manifest_schema::Manifest {
format_version: "2".to_string(),
package: manifest_schema::PackageInfo {
name: "test-workflow".to_string(),
version: "2.1.0".to_string(),
description: Some("Test workflow package".to_string()),
fingerprint: "sha256:test".to_string(),
targets: vec!["linux-x86_64".to_string()],
},
language: manifest_schema::PackageLanguage::Rust,
python: None,
rust: Some(manifest_schema::RustRuntime {
library_path: "libworkflow.dylib".to_string(),
}),
tasks: vec![
manifest_schema::TaskDefinition {
id: "task1".to_string(),
function: "cloacina_execute_task".to_string(),
dependencies: vec![],
description: Some("First task".to_string()),
retries: 0,
timeout_seconds: None,
},
manifest_schema::TaskDefinition {
id: "task2".to_string(),
function: "cloacina_execute_task".to_string(),
dependencies: vec!["task1".to_string()],
description: Some("Second task".to_string()),
retries: 0,
timeout_seconds: None,
},
],
triggers: vec![],
created_at: Utc::now(),
signature: None,
};
assert_eq!(manifest.package.name, "test-workflow");
assert_eq!(manifest.package.version, "2.1.0");
assert!(manifest.rust.is_some());
assert_eq!(manifest.tasks.len(), 2);
assert_eq!(manifest.tasks[1].dependencies, vec!["task1"]);
let json = serde_json::to_string(&manifest).expect("Should serialize");
let deserialized: manifest_schema::Manifest =
serde_json::from_str(&json).expect("Should deserialize");
assert_eq!(deserialized.package.name, manifest.package.name);
assert_eq!(deserialized.tasks.len(), manifest.tasks.len());
}
#[test]
fn test_constants() {
assert_eq!(types::MANIFEST_FILENAME, "manifest.json");
assert!(!types::CLOACINA_VERSION.is_empty());
let version_parts: Vec<&str> = types::CLOACINA_VERSION.split('.').collect();
assert!(
version_parts.len() >= 2,
"Version should have at least major.minor"
);
for part in version_parts.iter().take(2) {
assert!(
part.parse::<u32>().is_ok(),
"Version parts should be numeric: {}",
part
);
}
}
#[test]
fn test_manifest_error_display() {
use super::super::manifest::ManifestError;
let err = ManifestError::LibraryError {
message: "test error".to_string(),
};
assert!(err.to_string().contains("test error"));
}
}