-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtemplate_integration.rs
More file actions
240 lines (197 loc) · 9.87 KB
/
template_integration.rs
File metadata and controls
240 lines (197 loc) · 9.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
//! Integration tests for the template system
//!
//! These tests verify that the template system works with real template files
//! and validates the complete workflow without actually provisioning infrastructure.
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 that the real inventory template renders correctly
#[test]
fn it_should_render_inventory_template_when_using_real_configuration() -> Result<()> {
// Use the actual inventory template
let template_path = PathBuf::from("templates/ansible/inventory.yml.tera");
// Skip test if template file doesn't exist (e.g., in CI without templates)
if !template_path.exists() {
println!(
"Skipping test: inventory template not found at {}",
template_path.display()
);
return Ok(());
}
// Read the template content
let template_content = std::fs::read_to_string(&template_path)?;
// Create temporary output directory
let temp_dir = TempDir::new()?;
let output_path = temp_dir.path().join("inventory.yml");
// Test with realistic values
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)?;
// Render the template
inventory.render(&output_path)?;
// Verify the output file exists and has the right content
assert!(output_path.exists());
let file_content = std::fs::read_to_string(&output_path)?;
// Verify variables were substituted
assert!(file_content.contains("ansible_host: 192.168.1.100"));
assert!(file_content.contains("ansible_ssh_private_key_file: /home/user/.ssh/testing_rsa"));
// Verify no template variables remain
assert!(!file_content.contains("{{ansible_host}}"));
assert!(!file_content.contains("{{ansible_ssh_private_key_file}}"));
// Verify it's valid YAML structure
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 variable validation with real template
#[test]
fn it_should_validate_template_variables_when_rendering_real_templates() -> Result<()> {
let template_path = PathBuf::from("templates/ansible/inventory.yml.tera");
// Skip test if template file doesn't exist
if !template_path.exists() {
println!("Skipping test: inventory template not found");
return Ok(());
}
// Read the template content
let Ok(template_content) = std::fs::read_to_string(&template_path) else {
println!("Skipping test: Could not read template file");
return Ok(());
};
// Test that valid variables are accepted
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);
// Construction should succeed with valid IP and SSH key path
assert!(result.is_ok());
// Test that invalid IP address is rejected
let result = AnsibleHost::from_str("invalid.ip.address");
assert!(result.is_err());
// Test that empty SSH key path is rejected
let result = SshPrivateKeyFile::new("");
assert!(result.is_err());
// Test that invalid template content fails
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());
// Static templates are now valid - they just don't use template variables
assert!(result.is_ok());
// Test that templates with undefined variables fail
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 that template rendering doesn't modify any files in the templates directory
#[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(());
}
// Read the original template content
let original_content = std::fs::read_to_string(&template_path)?;
// Create temporary output directory
let temp_dir = TempDir::new()?;
let output_path = temp_dir.path().join("inventory.yml");
// Render the template multiple times with different values
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)?;
}
// Verify the original template is unchanged
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 build directory workflow simulation
#[test]
fn it_should_execute_full_build_directory_workflow_when_generating_templates() -> Result<()> {
// Simulate the complete build directory workflow
let temp_dir = TempDir::new()?;
let build_root = temp_dir.path().join("build");
// Create build directory structure
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)?;
// Test that directories were created
assert!(build_ansible.exists());
assert!(build_tofu.exists());
// Simulate template rendering to build directory
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)?;
// Verify output in build directory
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(())
}
}