use anyhow::Result;
use std::path::PathBuf;
use std::str::FromStr;
use tempfile::TempDir;
use torrust_tracker_deployer_lib::domain::template::file::File;
use torrust_tracker_deployer_lib::infrastructure::templating::ansible::template::wrappers::inventory::{
AnsibleHost, AnsiblePort, InventoryContext, InventoryTemplate, SshPrivateKeyFile,
};
#[cfg(test)]
mod integration_tests {
use super::*;
#[test]
fn it_should_render_inventory_template_when_using_real_configuration() -> Result<()> {
let template_path = PathBuf::from("templates/ansible/inventory.yml.tera");
if !template_path.exists() {
println!(
"Skipping test: inventory template not found at {}",
template_path.display()
);
return Ok(());
}
let template_content = std::fs::read_to_string(&template_path)?;
let temp_dir = TempDir::new()?;
let output_path = temp_dir.path().join("inventory.yml");
let template_file = File::new("inventory.yml.tera", template_content.clone()).unwrap();
let host = AnsibleHost::from_str("192.168.1.100")?;
let ssh_key = SshPrivateKeyFile::new("/home/user/.ssh/testing_rsa")?;
let ssh_port = AnsiblePort::new(22)?;
let inventory_context = InventoryContext::builder()
.with_host(host)
.with_ssh_priv_key_path(ssh_key)
.with_ssh_port(ssh_port)
.with_ansible_user("torrust".to_string())
.build()?;
let inventory = InventoryTemplate::new(&template_file, inventory_context)?;
inventory.render(&output_path)?;
assert!(output_path.exists());
let file_content = std::fs::read_to_string(&output_path)?;
assert!(file_content.contains("ansible_host: 192.168.1.100"));
assert!(file_content.contains("ansible_ssh_private_key_file: /home/user/.ssh/testing_rsa"));
assert!(!file_content.contains("{{ansible_host}}"));
assert!(!file_content.contains("{{ansible_ssh_private_key_file}}"));
assert!(file_content.contains("all:"));
assert!(file_content.contains("torrust-tracker-vm:"));
assert!(file_content.contains("ansible_user: torrust"));
println!("✅ Real inventory template rendered successfully");
Ok(())
}
#[test]
fn it_should_validate_template_variables_when_rendering_real_templates() -> Result<()> {
let template_path = PathBuf::from("templates/ansible/inventory.yml.tera");
if !template_path.exists() {
println!("Skipping test: inventory template not found");
return Ok(());
}
let Ok(template_content) = std::fs::read_to_string(&template_path) else {
println!("Skipping test: Could not read template file");
return Ok(());
};
let template_file = File::new("inventory.yml.tera", template_content.clone()).unwrap();
let host = AnsibleHost::from_str("127.0.0.1")?;
let ssh_key = SshPrivateKeyFile::new("/path/to/key")?;
let ssh_port = AnsiblePort::new(22)?;
let inventory_context = InventoryContext::builder()
.with_host(host)
.with_ssh_priv_key_path(ssh_key)
.with_ssh_port(ssh_port)
.with_ansible_user("ubuntu".to_string())
.build()?;
let result = InventoryTemplate::new(&template_file, inventory_context);
assert!(result.is_ok());
let result = AnsibleHost::from_str("invalid.ip.address");
assert!(result.is_err());
let result = SshPrivateKeyFile::new("");
assert!(result.is_err());
let invalid_content = "invalid template content without required variables";
let invalid_template_file =
File::new("inventory.yml.tera", invalid_content.to_string()).unwrap();
let host = AnsibleHost::from_str("192.168.1.100")?;
let ssh_key = SshPrivateKeyFile::new("/path/to/key")?;
let ssh_port = AnsiblePort::new(22)?;
let inventory_context = InventoryContext::builder()
.with_host(host)
.with_ssh_priv_key_path(ssh_key)
.with_ssh_port(ssh_port)
.with_ansible_user("admin".to_string())
.build()?;
let result = InventoryTemplate::new(&invalid_template_file, inventory_context.clone());
assert!(result.is_ok());
let undefined_var_content = "server ansible_host={{undefined_variable}}\n";
let undefined_template_file =
File::new("inventory.yml.tera", undefined_var_content.to_string()).unwrap();
let result = InventoryTemplate::new(&undefined_template_file, inventory_context);
assert!(result.is_err());
println!("✅ Template with undefined variables correctly rejected");
Ok(())
}
#[test]
fn it_should_not_modify_template_directory_when_rendering_templates() -> Result<()> {
let template_path = PathBuf::from("templates/ansible/inventory.yml.tera");
if !template_path.exists() {
println!("Skipping test: inventory template not found");
return Ok(());
}
let original_content = std::fs::read_to_string(&template_path)?;
let temp_dir = TempDir::new()?;
let output_path = temp_dir.path().join("inventory.yml");
for i in 1..=3 {
let template_file = File::new("inventory.yml.tera", original_content.clone()).unwrap();
let host = AnsibleHost::from_str(&format!("192.168.1.{i}"))?;
let ssh_key = SshPrivateKeyFile::new(format!("/home/user{i}/.ssh/key"))?;
let ssh_port = AnsiblePort::new(22)?;
let inventory_context = InventoryContext::builder()
.with_host(host)
.with_ssh_priv_key_path(ssh_key)
.with_ssh_port(ssh_port)
.with_ansible_user(format!("user{i}"))
.build()?;
let inventory = InventoryTemplate::new(&template_file, inventory_context)?;
inventory.render(&output_path)?;
}
let current_content = std::fs::read_to_string(&template_path)?;
assert_eq!(original_content, current_content);
println!("✅ Template directory remains unmodified after multiple renderings");
Ok(())
}
#[test]
fn it_should_execute_full_build_directory_workflow_when_generating_templates() -> Result<()> {
let temp_dir = TempDir::new()?;
let build_root = temp_dir.path().join("build");
let build_ansible = build_root.join("ansible");
let build_tofu = build_root.join("tofu/lxd");
std::fs::create_dir_all(&build_ansible)?;
std::fs::create_dir_all(&build_tofu)?;
assert!(build_ansible.exists());
assert!(build_tofu.exists());
if PathBuf::from("templates/ansible/inventory.yml.tera").exists() {
let template_path = PathBuf::from("templates/ansible/inventory.yml.tera");
let template_content = std::fs::read_to_string(&template_path)?;
let template_file = File::new("inventory.yml.tera", template_content.clone()).unwrap();
let output_path = build_ansible.join("inventory.yml");
let host = AnsibleHost::from_str("10.0.0.100")?;
let ssh_key =
SshPrivateKeyFile::new(temp_dir.path().join("ssh_key").to_string_lossy().as_ref())?;
let ssh_port = AnsiblePort::new(22)?;
let inventory_context = InventoryContext::builder()
.with_host(host)
.with_ssh_priv_key_path(ssh_key)
.with_ssh_port(ssh_port)
.with_ansible_user("testuser".to_string())
.build()?;
let inventory = InventoryTemplate::new(&template_file, inventory_context)?;
inventory.render(&output_path)?;
assert!(output_path.exists());
let file_content = std::fs::read_to_string(&output_path)?;
assert!(file_content.contains("10.0.0.100"));
}
println!("✅ Build directory workflow completed successfully");
Ok(())
}
}