Skip to main content

torrust_tracker_deployer_lib/testing/integration/ssh_server/
mod.rs

1//! SSH Server Container for Integration Testing
2//!
3//! This module provides SSH server containers for testing SSH client functionality.
4//! Two implementations are available:
5//!
6//! - `MockSshServerContainer`: Fast mock for tests that don't need real SSH connectivity
7//! - `RealSshServerContainer`: Actual Docker SSH server for full integration tests
8//!
9//! Both implementations provide the same interface through the `SshServerContainer` trait,
10//! allowing for polymorphic usage in tests.
11
12use std::net::IpAddr;
13
14mod config;
15mod constants;
16mod debug;
17mod error;
18mod mock_container;
19mod real_container;
20
21pub use config::{SshServerConfig, SshServerConfigBuilder};
22pub use debug::{print_docker_debug_info, ContainerInfo, DockerDebugInfo};
23pub use error::SshServerError;
24pub use mock_container::MockSshServerContainer;
25pub use real_container::RealSshServerContainer;
26
27/// Common interface for SSH server containers (mock and real)
28///
29/// This trait defines the standard interface that all SSH server container
30/// implementations must provide. It enables polymorphic code that works with
31/// both mock and real containers.
32///
33/// # Example
34///
35/// ```rust
36/// use torrust_tracker_deployer_lib::testing::integration::ssh_server::{
37///     SshServerContainer, MockSshServerContainer, RealSshServerContainer
38/// };
39///
40/// async fn test_with_container<C: SshServerContainer>(container: &C) {
41///     let port = container.ssh_port();
42///     let ip = container.host_ip();
43///     println!("SSH available at {}:{}", ip, port);
44/// }
45/// ```
46pub trait SshServerContainer {
47    /// Get the SSH port mapped by the container
48    ///
49    /// Returns the host port that maps to the container's SSH port (22).
50    fn ssh_port(&self) -> u16;
51
52    /// Get the container's host IP address
53    ///
54    /// Returns the IP address to connect to the container from the host.
55    fn host_ip(&self) -> IpAddr;
56
57    /// Get the test username configured in the container
58    fn username(&self) -> &str;
59
60    /// Get the test password configured in the container
61    fn password(&self) -> &str;
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use std::net::{IpAddr, Ipv4Addr};
68
69    #[tokio::test]
70    async fn it_should_start_mock_ssh_server_container() {
71        let container = MockSshServerContainer::start();
72
73        match container {
74            Ok(ssh_container) => {
75                // Verify basic container properties
76                let port = ssh_container.ssh_port();
77                assert!(port > 0, "SSH port should be positive");
78
79                let host_ip = ssh_container.host_ip();
80                assert_eq!(host_ip, IpAddr::V4(Ipv4Addr::LOCALHOST));
81
82                assert_eq!(ssh_container.username(), "testuser");
83                assert_eq!(ssh_container.password(), "testpass");
84            }
85            Err(e) => {
86                panic!("Mock container should always start successfully: {e}");
87            }
88        }
89    }
90
91    #[tokio::test]
92    async fn it_should_start_real_ssh_server_container() {
93        let container = RealSshServerContainer::start().await;
94
95        match container {
96            Ok(ssh_container) => {
97                // Verify basic container properties
98                let port = ssh_container.ssh_port();
99                assert!(port > 0, "SSH port should be positive");
100
101                let host_ip = ssh_container.host_ip();
102                assert_eq!(host_ip, IpAddr::V4(Ipv4Addr::LOCALHOST));
103
104                assert_eq!(ssh_container.username(), "testuser");
105                assert_eq!(ssh_container.password(), "testpass");
106            }
107            Err(e) => {
108                // Real container start might fail in CI environments without Docker
109                // or if the SSH server image hasn't been built
110                println!("Real container start failed (expected in some environments): {e}");
111            }
112        }
113    }
114
115    #[tokio::test]
116    async fn it_should_work_with_trait_object() {
117        // Test that we can use the trait for polymorphic behavior
118        let mock = MockSshServerContainer::start()
119            .expect("Mock container should always start successfully");
120
121        // Use trait method through trait object
122        let container: &dyn SshServerContainer = &mock;
123        assert!(container.ssh_port() > 0);
124        assert_eq!(container.host_ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
125        assert_eq!(container.username(), "testuser");
126        assert_eq!(container.password(), "testpass");
127    }
128
129    #[tokio::test]
130    async fn it_should_enable_generic_code() {
131        // Helper function that works with any SshServerContainer
132        fn verify_container<C: SshServerContainer>(container: &C) {
133            assert!(container.ssh_port() > 0);
134            assert_eq!(container.username(), "testuser");
135        }
136
137        let mock = MockSshServerContainer::start()
138            .expect("Mock container should always start successfully");
139
140        // Can call generic function with concrete type
141        verify_container(&mock);
142    }
143}