torrust_tracker_deployer_lib/domain/environment/state/
created.rs1use std::net::IpAddr;
11
12use serde::{Deserialize, Serialize};
13
14use crate::domain::environment::runtime_outputs::ProvisionMethod;
15use crate::domain::environment::state::{
16 AnyEnvironmentState, Provisioned, Provisioning, StateTypeError,
17};
18use crate::domain::environment::Environment;
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct Created;
28
29impl Environment<Created> {
31 #[must_use]
36 pub fn start_provisioning(self) -> Environment<Provisioning> {
37 self.with_state(Provisioning)
38 }
39
40 #[must_use]
57 pub fn register(self, instance_ip: IpAddr) -> Environment<Provisioned> {
58 self.with_instance_ip(instance_ip)
59 .with_provision_method(ProvisionMethod::Registered)
60 .with_state(Provisioned)
61 }
62}
63
64impl Environment<Created> {
66 #[must_use]
68 pub fn into_any(self) -> AnyEnvironmentState {
69 AnyEnvironmentState::Created(self)
70 }
71}
72
73impl AnyEnvironmentState {
75 pub fn try_into_created(self) -> Result<Environment<Created>, StateTypeError> {
81 match self {
82 Self::Created(env) => Ok(env),
83 other => Err(StateTypeError::UnexpectedState {
84 expected: "created",
85 actual: other.state_name().to_string(),
86 }),
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn it_should_create_created_state() {
97 let state = Created;
98 assert_eq!(state, Created);
99 }
100
101 #[test]
102 fn it_should_clone_created_state() {
103 let state = Created;
104 let cloned = state.clone();
105 assert_eq!(state, cloned);
106 }
107
108 #[test]
109 fn it_should_serialize_created_state_to_json() {
110 let state = Created;
111 let json = serde_json::to_string(&state).unwrap();
112 assert_eq!(json, "null");
113 }
114
115 #[test]
116 fn it_should_deserialize_created_state_from_json() {
117 let json = "null";
118 let state: Created = serde_json::from_str(json).unwrap();
119 assert_eq!(state, Created);
120 }
121
122 #[test]
123 fn it_should_debug_format_created_state() {
124 let created = format!("{Created:?}");
125 assert_eq!(created, "Created");
126 }
127
128 mod conversion_tests {
129 use super::*;
130 use crate::adapters::ssh::SshCredentials;
131 use crate::domain::environment::name::EnvironmentName;
132 use crate::domain::provider::{LxdConfig, ProviderConfig};
133 use crate::domain::ProfileName;
134 use crate::shared::Username;
135 use std::path::PathBuf;
136
137 fn default_lxd_provider_config(env_name: &EnvironmentName) -> ProviderConfig {
138 ProviderConfig::Lxd(LxdConfig {
139 profile_name: ProfileName::new(format!("lxd-{}", env_name.as_str())).unwrap(),
140 })
141 }
142
143 fn create_test_ssh_credentials() -> SshCredentials {
144 let username = Username::new("test-user".to_string()).unwrap();
145 SshCredentials::new(
146 PathBuf::from("/tmp/test_key"),
147 PathBuf::from("/tmp/test_key.pub"),
148 username,
149 )
150 }
151
152 fn create_test_environment_created() -> Environment<Created> {
153 let name = EnvironmentName::new("test-env".to_string()).unwrap();
154 let ssh_creds = create_test_ssh_credentials();
155 Environment::new(
156 name.clone(),
157 default_lxd_provider_config(&name),
158 ssh_creds,
159 22,
160 chrono::Utc::now(),
161 )
162 }
163
164 #[test]
165 fn it_should_convert_created_environment_into_any() {
166 let env = create_test_environment_created();
167 let any_env = env.into_any();
168 assert!(matches!(any_env, AnyEnvironmentState::Created(_)));
169 }
170
171 #[test]
172 fn it_should_convert_any_to_created_successfully() {
173 let env = create_test_environment_created();
174 let any_env = env.into_any();
175 let result = any_env.try_into_created();
176 assert!(result.is_ok());
177 }
178
179 #[test]
180 fn it_should_fail_converting_provisioning_to_created() {
181 let env = create_test_environment_created().start_provisioning();
182 let any_env = env.into_any();
183 let result = any_env.try_into_created();
184 assert!(result.is_err());
185 let err = result.unwrap_err();
186 assert!(matches!(err, StateTypeError::UnexpectedState { .. }));
187 assert!(err.to_string().contains("created"));
188 assert!(err.to_string().contains("provisioning"));
189 }
190
191 #[test]
192 fn it_should_preserve_data_in_round_trip_conversion() {
193 let original_name = "test-env";
194 let env = create_test_environment_created();
195
196 let any_env = env.into_any();
198 let env_restored = any_env.try_into_created().unwrap();
199 let name_after = env_restored.name().as_str();
200
201 assert_eq!(name_after, original_name);
202 }
203 }
204
205 mod state_transitions {
206 use super::super::*;
207 use crate::adapters::ssh::SshCredentials;
208 use crate::domain::environment::name::EnvironmentName;
209 use crate::domain::environment::state::Provisioning;
210 use crate::domain::provider::{LxdConfig, ProviderConfig};
211 use crate::domain::ProfileName;
212 use crate::shared::Username;
213 use std::path::PathBuf;
214
215 fn default_lxd_provider_config(env_name: &EnvironmentName) -> ProviderConfig {
216 ProviderConfig::Lxd(LxdConfig {
217 profile_name: ProfileName::new(format!("lxd-{}", env_name.as_str())).unwrap(),
218 })
219 }
220
221 fn create_test_ssh_credentials() -> SshCredentials {
222 let username = Username::new("test-user".to_string()).unwrap();
223 SshCredentials::new(
224 PathBuf::from("/tmp/test_key"),
225 PathBuf::from("/tmp/test_key.pub"),
226 username,
227 )
228 }
229
230 fn create_test_environment_created() -> Environment<Created> {
231 let name = EnvironmentName::new("test-env".to_string()).unwrap();
232 let ssh_creds = create_test_ssh_credentials();
233 Environment::new(
234 name.clone(),
235 default_lxd_provider_config(&name),
236 ssh_creds,
237 22,
238 chrono::Utc::now(),
239 )
240 }
241
242 #[test]
243 fn it_should_transition_from_created_to_provisioning() {
244 let env = create_test_environment_created();
245 let env = env.start_provisioning();
246
247 assert_eq!(*env.state(), Provisioning);
249 assert_eq!(env.name().as_str(), "test-env");
251 }
252
253 #[test]
254 fn it_should_register_existing_instance_and_transition_to_provisioned() {
255 use std::net::{IpAddr, Ipv4Addr};
256
257 use crate::domain::environment::state::Provisioned;
258
259 let env = create_test_environment_created();
260 let instance_ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100));
261 let env = env.register(instance_ip);
262
263 assert_eq!(*env.state(), Provisioned);
265 assert_eq!(env.instance_ip(), Some(instance_ip));
267 assert_eq!(env.name().as_str(), "test-env");
269 }
270
271 #[test]
272 fn it_should_register_with_ipv6_address() {
273 use std::net::{IpAddr, Ipv6Addr};
274
275 use crate::domain::environment::state::Provisioned;
276
277 let env = create_test_environment_created();
278 let instance_ip = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
279 let env = env.register(instance_ip);
280
281 assert_eq!(*env.state(), Provisioned);
283 assert_eq!(env.instance_ip(), Some(instance_ip));
285 }
286 }
287}