-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcommand_execution_tests.rs
More file actions
278 lines (254 loc) · 9.94 KB
/
command_execution_tests.rs
File metadata and controls
278 lines (254 loc) · 9.94 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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
//! SSH Command Execution Tests
//!
//! Tests for SSH remote command execution functionality including:
//! - Basic command execution (echo, whoami)
//! - Output capture and validation
//! - Multiple command execution on same connection
//! - End-to-end SSH functionality verification
use super::*;
// =============================================================================
// REAL SSH SERVER COMMAND EXECUTION TESTS
// =============================================================================
/// Test remote command execution using a real Docker SSH server
///
/// This test uses `RealSshServerContainer` to execute actual commands via SSH.
/// It verifies:
/// 1. SSH connectivity and authentication with key-based auth
/// 2. Remote command execution (`echo` command)
/// 3. Output capture and validation
/// 4. End-to-end SSH functionality
///
/// ## SSH Authentication Setup
///
/// - **SSH Keys**: Uses test keys from `fixtures/testing_rsa` (private) and `fixtures/testing_rsa.pub` (public)
/// - **Docker Image**: The SSH server Dockerfile has the test public key hardcoded for authentication
/// - **User**: Connects as `testuser` which is preconfigured in the Docker image
///
/// ## Requirements
///
/// - Docker must be running
/// - SSH server image must be built: `docker build -t torrust-ssh-server:latest docker/ssh-server/`
/// - The Docker image includes the hardcoded test public key for authentication
///
/// The test will skip gracefully if Docker is not available or the image is not built.
#[tokio::test]
async fn it_should_execute_remote_command_on_real_ssh_server() {
// Arrange: Set up real SSH server container and client
let ssh_container = match RealSshServerContainer::start().await {
Ok(container) => container,
Err(e) => {
// Skip test if Docker is not available or image is not built
println!("Skipping real SSH command execution test - Docker/image not available: {e}");
return;
}
};
let client = SshTestBuilder::new()
.with_real_container(&ssh_container)
.build_client();
// Ensure SSH connectivity before command execution
match client.wait_for_connectivity().await {
Ok(()) => {
println!("SSH connectivity established successfully");
}
Err(e) => {
println!("SSH connectivity failed - skipping command execution test: {e}");
return;
}
}
// Act: Execute commands via SSH
let test_message = "Hello SSH Integration Test";
let echo_command = format!("echo '{test_message}'");
let echo_result = client.execute(&echo_command);
let whoami_result = client.execute("whoami");
// Assert: Verify command execution results
match echo_result {
Ok(output) => {
let trimmed_output = output.trim();
assert_eq!(
trimmed_output, test_message,
"Echo command output should match expected message"
);
}
Err(e) => {
panic!("Echo command execution should succeed with real server: {e}");
}
}
match whoami_result {
Ok(output) => {
let username = output.trim();
assert_eq!(
username,
ssh_container.username(),
"whoami should return the test username"
);
}
Err(e) => {
panic!("whoami command should succeed: {e}");
}
}
}
// =============================================================================
// SSH OPTION OVERRIDE TESTS
// =============================================================================
/// Test that user options can override defaults
///
/// The SSH client now filters out defaults when users provide conflicting options:
/// - User-provided options are added first (SSH uses first-occurrence-wins)
/// - Default options are skipped if the user already provided that option key
/// - This allows users full control while providing sensible automation defaults
///
/// This test verifies that user-provided timeouts override the default.
#[tokio::test]
async fn it_should_allow_users_to_override_default_options() {
// Arrange: Set up real SSH server
let ssh_container = match RealSshServerContainer::start().await {
Ok(container) => container,
Err(e) => {
println!("Skipping SSH option override test - Docker/image not available: {e}");
return;
}
};
let client = SshTestBuilder::new()
.with_real_container(&ssh_container)
.build_client();
// Wait for connectivity
match client.wait_for_connectivity().await {
Ok(()) => {
println!("SSH connectivity established for option override test");
}
Err(e) => {
println!("SSH connectivity failed - skipping option override test: {e}");
return;
}
}
// Act & Assert: Test with override ConnectTimeout option
// Default is 5 seconds, user overrides with 10 seconds
// User option should take precedence (default is filtered out)
let result = client.execute_with_options(
"echo 'testing option override'",
&["ConnectTimeout=10"], // Override default timeout
);
match result {
Ok(output) => {
let trimmed = output.trim();
assert_eq!(
trimmed, "testing option override",
"Command should execute successfully with user-provided timeout"
);
println!("✓ User options override defaults - custom ConnectTimeout was used");
}
Err(e) => {
panic!("Command should succeed with user-provided options: {e}");
}
}
}
/// Test explicit override of `StrictHostKeyChecking` option
///
/// This test demonstrates that users can override even critical automation settings.
/// When a user provides `StrictHostKeyChecking=yes`, it overrides the default `no`.
/// The default is filtered out, so only the user-provided value is sent to SSH.
#[tokio::test]
async fn it_should_allow_users_to_override_strict_host_key_checking() {
// Arrange: Set up real SSH server
let ssh_container = match RealSshServerContainer::start().await {
Ok(container) => container,
Err(e) => {
println!("Skipping default precedence test - Docker/image not available: {e}");
return;
}
};
let client = SshTestBuilder::new()
.with_real_container(&ssh_container)
.build_client();
// Wait for connectivity
match client.wait_for_connectivity().await {
Ok(()) => {
println!("SSH connectivity established for precedence test");
}
Err(e) => {
println!("SSH connectivity failed - skipping precedence test: {e}");
return;
}
}
// Act: Override StrictHostKeyChecking (defaults to 'no', user sets to 'yes')
// NOTE: This test may fail if the SSH server's host key is not in known_hosts
// and StrictHostKeyChecking=yes is enforced. However, in our test environment,
// the command should still work because we've already established connectivity
// with the default 'no' setting, and the host key was added to known_hosts.
let result = client.execute_with_options(
"echo 'testing strict host key override'",
&["StrictHostKeyChecking=yes"], // Override to 'yes'
);
// Assert: Command execution result
match result {
Ok(output) => {
let trimmed = output.trim();
assert_eq!(
trimmed, "testing strict host key override",
"Command executes with user-provided StrictHostKeyChecking=yes"
);
println!("✓ User options override defaults - StrictHostKeyChecking=yes was used");
}
Err(e) => {
// This might fail in strict environments - that's expected behavior
println!(
"Note: Command may fail with StrictHostKeyChecking=yes in some environments: {e}"
);
println!("This demonstrates that user overrides are respected by SSH");
}
}
}
/// Test that users can add new options that don't conflict with defaults
///
/// This test verifies that users can safely add SSH options that are not
/// part of the default set, such as:
/// - `ServerAliveInterval`
/// - `ServerAliveCountMax`
/// - Compression
/// - etc.
#[tokio::test]
async fn it_should_allow_users_to_add_non_conflicting_ssh_options() {
// Arrange: Set up real SSH server
let ssh_container = match RealSshServerContainer::start().await {
Ok(container) => container,
Err(e) => {
println!("Skipping non-conflicting options test - Docker/image not available: {e}");
return;
}
};
let client = SshTestBuilder::new()
.with_real_container(&ssh_container)
.build_client();
// Wait for connectivity
match client.wait_for_connectivity().await {
Ok(()) => {
println!("SSH connectivity established for non-conflicting options test");
}
Err(e) => {
println!("SSH connectivity failed - skipping non-conflicting options test: {e}");
return;
}
}
// Act: Add options that don't conflict with defaults
let result = client.execute_with_options(
"echo 'testing additional options'",
&[
"ServerAliveInterval=60", // Keep connection alive
"ServerAliveCountMax=3", // Max keepalive attempts
],
);
// Assert: Command succeeds with additional options
match result {
Ok(output) => {
let trimmed = output.trim();
assert_eq!(
trimmed, "testing additional options",
"Command should succeed with additional non-conflicting options"
);
println!("✓ Non-conflicting options work correctly");
}
Err(e) => {
panic!("Command should succeed with non-conflicting options: {e}");
}
}
}