Skip to main content

torrust_tracker_deployer_lib/domain/environment/state/
configured.rs

1//! Configured State
2//!
3//! Final state - Application configuration completed successfully
4//!
5//! All application configuration has been applied. The environment is ready
6//! for release preparation.
7//!
8//! **Valid Transitions:**
9//! - `Releasing` (start release process)
10
11use serde::{Deserialize, Serialize};
12
13use crate::domain::environment::state::{AnyEnvironmentState, Releasing, StateTypeError};
14use crate::domain::environment::Environment;
15
16/// Final state - Application configuration completed successfully
17///
18/// All application configuration has been applied. The environment is ready
19/// for release preparation.
20///
21/// **Valid Transitions:**
22/// - `Releasing` (start release process)
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct Configured;
25
26// State transition implementations
27impl Environment<Configured> {
28    /// Transitions from Configured to Releasing state
29    ///
30    /// This method indicates that release preparation has begun.
31    #[must_use]
32    pub fn start_releasing(self) -> Environment<Releasing> {
33        self.with_state(Releasing)
34    }
35}
36
37// Type Erasure: Typed → Runtime conversion (into_any)
38impl Environment<Configured> {
39    /// Converts typed `Environment<Configured>` into type-erased `AnyEnvironmentState`
40    #[must_use]
41    pub fn into_any(self) -> AnyEnvironmentState {
42        AnyEnvironmentState::Configured(self)
43    }
44}
45
46// Type Restoration: Runtime → Typed conversion (try_into_configured)
47impl AnyEnvironmentState {
48    /// Attempts to convert `AnyEnvironmentState` to `Environment<Configured>`
49    ///
50    /// # Errors
51    ///
52    /// Returns `StateTypeError::UnexpectedState` if the environment is not in `Configured` state.
53    pub fn try_into_configured(self) -> Result<Environment<Configured>, StateTypeError> {
54        match self {
55            Self::Configured(env) => Ok(env),
56            other => Err(StateTypeError::UnexpectedState {
57                expected: "configured",
58                actual: other.state_name().to_string(),
59            }),
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn it_should_create_configured_state() {
70        let state = Configured;
71        assert_eq!(state, Configured);
72    }
73
74    mod conversion_tests {
75        use super::*;
76        use crate::adapters::ssh::SshCredentials;
77        use crate::domain::environment::name::EnvironmentName;
78        use crate::domain::environment::runtime_outputs::ProvisionMethod;
79        use crate::domain::provider::{LxdConfig, ProviderConfig};
80        use crate::domain::ProfileName;
81        use crate::shared::Username;
82        use std::net::{IpAddr, Ipv4Addr};
83        use std::path::PathBuf;
84
85        fn default_lxd_provider_config(env_name: &EnvironmentName) -> ProviderConfig {
86            ProviderConfig::Lxd(LxdConfig {
87                profile_name: ProfileName::new(format!("lxd-{}", env_name.as_str())).unwrap(),
88            })
89        }
90
91        fn create_test_ssh_credentials() -> SshCredentials {
92            let username = Username::new("test-user".to_string()).unwrap();
93            SshCredentials::new(
94                PathBuf::from("/tmp/test_key"),
95                PathBuf::from("/tmp/test_key.pub"),
96                username,
97            )
98        }
99
100        fn create_test_environment_configured() -> Environment<Configured> {
101            let name = EnvironmentName::new("test-env".to_string()).unwrap();
102            let ssh_creds = create_test_ssh_credentials();
103            Environment::new(
104                name.clone(),
105                default_lxd_provider_config(&name),
106                ssh_creds,
107                22,
108                chrono::Utc::now(),
109            )
110            .start_provisioning()
111            .provisioned(
112                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
113                ProvisionMethod::Provisioned,
114            )
115            .start_configuring()
116            .configured()
117        }
118
119        #[test]
120        fn it_should_convert_configured_environment_into_any() {
121            let env = create_test_environment_configured();
122            let any_env = env.into_any();
123            assert!(matches!(any_env, AnyEnvironmentState::Configured(_)));
124        }
125
126        #[test]
127        fn it_should_convert_any_to_configured_successfully() {
128            let env = create_test_environment_configured();
129            let any_env = env.into_any();
130            let result = any_env.try_into_configured();
131            assert!(result.is_ok());
132        }
133    }
134
135    mod transition_tests {
136        use super::*;
137        use crate::adapters::ssh::SshCredentials;
138        use crate::domain::environment::name::EnvironmentName;
139        use crate::domain::environment::runtime_outputs::ProvisionMethod;
140        use crate::domain::environment::state::Releasing;
141        use crate::domain::provider::{LxdConfig, ProviderConfig};
142        use crate::domain::ProfileName;
143        use crate::shared::Username;
144        use std::net::{IpAddr, Ipv4Addr};
145        use std::path::PathBuf;
146
147        fn default_lxd_provider_config(env_name: &EnvironmentName) -> ProviderConfig {
148            ProviderConfig::Lxd(LxdConfig {
149                profile_name: ProfileName::new(format!("lxd-{}", env_name.as_str())).unwrap(),
150            })
151        }
152
153        fn create_test_environment() -> Environment<Configured> {
154            let env_name = EnvironmentName::new("test-state".to_string()).unwrap();
155            let ssh_username = Username::new("torrust".to_string()).unwrap();
156            let ssh_credentials = SshCredentials::new(
157                PathBuf::from("test_key"),
158                PathBuf::from("test_key.pub"),
159                ssh_username,
160            );
161            Environment::new(
162                env_name.clone(),
163                default_lxd_provider_config(&env_name),
164                ssh_credentials,
165                22,
166                chrono::Utc::now(),
167            )
168            .start_provisioning()
169            .provisioned(
170                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
171                ProvisionMethod::Provisioned,
172            )
173            .start_configuring()
174            .configured()
175        }
176
177        #[test]
178        fn it_should_transition_from_configured_to_releasing() {
179            let env = create_test_environment();
180            let env = env.start_releasing();
181
182            assert_eq!(*env.state(), Releasing);
183            assert_eq!(env.name().as_str(), "test-state");
184        }
185    }
186}