1use serde::{Deserialize, Serialize};
44use thiserror::Error;
45
46use crate::domain::environment::runtime_outputs::{ProvisionMethod, ServiceEndpoints};
47use crate::shared::domain_name::DomainName;
48
49mod common;
51mod configure_failed;
52mod configured;
53mod configuring;
54mod created;
55mod destroy_failed;
56mod destroyed;
57mod destroying;
58mod provision_failed;
59mod provisioned;
60mod provisioning;
61mod release_failed;
62mod released;
63mod releasing;
64mod run_failed;
65mod running;
66
67pub use common::BaseFailureContext;
69pub use configure_failed::{ConfigureFailed, ConfigureFailureContext, ConfigureStep};
70pub use configured::Configured;
71pub use configuring::Configuring;
72pub use created::Created;
73pub use destroy_failed::{DestroyFailed, DestroyFailureContext, DestroyStep};
74pub use destroyed::Destroyed;
75pub use destroying::Destroying;
76pub use provision_failed::{ProvisionFailed, ProvisionFailureContext, ProvisionStep};
77pub use provisioned::Provisioned;
78pub use provisioning::Provisioning;
79pub use release_failed::{ReleaseFailed, ReleaseFailureContext, ReleaseStep};
80pub use released::Released;
81pub use releasing::Releasing;
82pub use run_failed::{RunFailed, RunFailureContext, RunStep};
83pub use running::Running;
84
85#[derive(Debug, Clone, Error)]
101pub enum StateTypeError {
102 #[error("Expected state '{expected}', but found '{actual}'")]
104 UnexpectedState {
105 expected: &'static str,
107 actual: String,
109 },
110}
111
112use crate::domain::environment::{Environment, EnvironmentName};
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
156pub enum AnyEnvironmentState {
157 Created(Environment<Created>),
159
160 Provisioning(Environment<Provisioning>),
162
163 Provisioned(Environment<Provisioned>),
165
166 Configuring(Environment<Configuring>),
168
169 Configured(Environment<Configured>),
171
172 Releasing(Environment<Releasing>),
174
175 Released(Environment<Released>),
177
178 Running(Environment<Running>),
180
181 Destroying(Environment<Destroying>),
183
184 ProvisionFailed(Environment<ProvisionFailed>),
186
187 ConfigureFailed(Environment<ConfigureFailed>),
189
190 ReleaseFailed(Environment<ReleaseFailed>),
192
193 RunFailed(Environment<RunFailed>),
195
196 DestroyFailed(Environment<DestroyFailed>),
198
199 Destroyed(Environment<Destroyed>),
201}
202
203impl AnyEnvironmentState {
205 fn context(&self) -> &crate::domain::environment::EnvironmentContext {
218 match self {
219 Self::Created(env) => env.context(),
220 Self::Provisioning(env) => env.context(),
221 Self::Provisioned(env) => env.context(),
222 Self::Configuring(env) => env.context(),
223 Self::Configured(env) => env.context(),
224 Self::Releasing(env) => env.context(),
225 Self::Released(env) => env.context(),
226 Self::Running(env) => env.context(),
227 Self::Destroying(env) => env.context(),
228 Self::ProvisionFailed(env) => env.context(),
229 Self::ConfigureFailed(env) => env.context(),
230 Self::ReleaseFailed(env) => env.context(),
231 Self::RunFailed(env) => env.context(),
232 Self::DestroyFailed(env) => env.context(),
233 Self::Destroyed(env) => env.context(),
234 }
235 }
236
237 #[must_use]
246 pub fn name(&self) -> &EnvironmentName {
247 self.context().user_inputs.name()
248 }
249
250 #[must_use]
259 pub fn state_name(&self) -> &'static str {
260 match self {
261 Self::Created(_) => "created",
262 Self::Provisioning(_) => "provisioning",
263 Self::Provisioned(_) => "provisioned",
264 Self::Configuring(_) => "configuring",
265 Self::Configured(_) => "configured",
266 Self::Releasing(_) => "releasing",
267 Self::Released(_) => "released",
268 Self::Running(_) => "running",
269 Self::Destroying(_) => "destroying",
270 Self::ProvisionFailed(_) => "provision_failed",
271 Self::ConfigureFailed(_) => "configure_failed",
272 Self::ReleaseFailed(_) => "release_failed",
273 Self::RunFailed(_) => "run_failed",
274 Self::DestroyFailed(_) => "destroy_failed",
275 Self::Destroyed(_) => "destroyed",
276 }
277 }
278
279 #[must_use]
289 pub fn state_display_name(&self) -> &'static str {
290 match self {
291 Self::Created(_) => "Created",
292 Self::Provisioning(_) => "Provisioning",
293 Self::Provisioned(_) => "Provisioned",
294 Self::Configuring(_) => "Configuring",
295 Self::Configured(_) => "Configured",
296 Self::Releasing(_) => "Releasing",
297 Self::Released(_) => "Released",
298 Self::Running(_) => "Running",
299 Self::Destroying(_) => "Destroying",
300 Self::ProvisionFailed(_) => "Provision Failed",
301 Self::ConfigureFailed(_) => "Configure Failed",
302 Self::ReleaseFailed(_) => "Release Failed",
303 Self::RunFailed(_) => "Run Failed",
304 Self::DestroyFailed(_) => "Destroy Failed",
305 Self::Destroyed(_) => "Destroyed",
306 }
307 }
308
309 #[must_use]
319 pub fn is_success_state(&self) -> bool {
320 matches!(
321 self,
322 Self::Created(_)
323 | Self::Provisioning(_)
324 | Self::Provisioned(_)
325 | Self::Configuring(_)
326 | Self::Configured(_)
327 | Self::Releasing(_)
328 | Self::Released(_)
329 | Self::Running(_)
330 | Self::Destroying(_)
331 | Self::Destroyed(_)
332 )
333 }
334
335 #[must_use]
344 pub fn is_error_state(&self) -> bool {
345 matches!(
346 self,
347 Self::ProvisionFailed(_)
348 | Self::ConfigureFailed(_)
349 | Self::ReleaseFailed(_)
350 | Self::RunFailed(_)
351 | Self::DestroyFailed(_)
352 )
353 }
354
355 #[must_use]
365 pub fn is_terminal_state(&self) -> bool {
366 matches!(
367 self,
368 Self::Running(_)
369 | Self::Destroyed(_)
370 | Self::ProvisionFailed(_)
371 | Self::ConfigureFailed(_)
372 | Self::ReleaseFailed(_)
373 | Self::RunFailed(_)
374 | Self::DestroyFailed(_)
375 )
376 }
377
378 #[must_use]
388 pub fn error_details(&self) -> Option<&str> {
389 match self {
390 Self::ProvisionFailed(env) => Some(&env.state().context.base.error_summary),
391 Self::ConfigureFailed(env) => Some(&env.state().context.base.error_summary),
392 Self::ReleaseFailed(env) => Some(&env.state().context.base.error_summary),
393 Self::RunFailed(env) => Some(&env.state().context.base.error_summary),
394 Self::DestroyFailed(env) => Some(&env.state().context.base.error_summary),
395 _ => None,
396 }
397 }
398
399 #[must_use]
408 pub fn instance_name(&self) -> &crate::domain::environment::InstanceName {
409 self.context().user_inputs.instance_name()
410 }
411
412 #[must_use]
425 pub fn profile_name(&self) -> &crate::domain::environment::ProfileName {
426 &self
427 .context()
428 .user_inputs
429 .provider_config()
430 .as_lxd()
431 .expect("profile_name() called on non-LXD environment")
432 .profile_name
433 }
434
435 #[must_use]
444 pub fn ssh_credentials(&self) -> &crate::adapters::ssh::SshCredentials {
445 self.context().user_inputs.ssh_credentials()
446 }
447
448 #[must_use]
457 pub fn ssh_port(&self) -> u16 {
458 self.context().user_inputs.ssh_port()
459 }
460
461 #[must_use]
470 pub fn provider_name(&self) -> &'static str {
471 self.context().user_inputs.provider_config().provider_name()
472 }
473
474 #[must_use]
483 pub fn provider_display_name(&self) -> &'static str {
484 self.context()
485 .user_inputs
486 .provider_config()
487 .provider_display_name()
488 }
489
490 #[must_use]
499 pub fn tracker_config(&self) -> &crate::domain::tracker::TrackerConfig {
500 self.context().user_inputs.tracker()
501 }
502
503 #[must_use]
513 pub fn instance_ip(&self) -> Option<std::net::IpAddr> {
514 self.context().runtime_outputs.instance_ip()
515 }
516
517 #[must_use]
526 pub fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
527 self.context().created_at
528 }
529
530 #[must_use]
541 pub fn provision_method(&self) -> Option<ProvisionMethod> {
542 self.context().runtime_outputs.provision_method()
543 }
544
545 #[must_use]
555 pub fn service_endpoints(&self) -> Option<&ServiceEndpoints> {
556 self.context().runtime_outputs.service_endpoints()
557 }
558
559 #[must_use]
569 pub fn prometheus_config(&self) -> Option<&crate::domain::prometheus::PrometheusConfig> {
570 self.context().user_inputs.prometheus()
571 }
572
573 #[must_use]
583 pub fn grafana_config(&self) -> Option<&crate::domain::grafana::GrafanaConfig> {
584 self.context().user_inputs.grafana()
585 }
586
587 #[must_use]
597 pub fn https_config(&self) -> Option<&crate::domain::https::HttpsConfig> {
598 self.context().user_inputs.https()
599 }
600
601 #[must_use]
611 pub fn is_registered(&self) -> bool {
612 matches!(self.provision_method(), Some(ProvisionMethod::Registered))
613 }
614
615 #[must_use]
629 pub fn collect_tls_domains(&self) -> Vec<DomainName> {
630 let tracker_config = self.tracker_config();
631 let mut domains = Vec::new();
632
633 if let Some(domain) = tracker_config.http_api().tls_domain() {
635 domains.push(domain.clone());
636 }
637
638 for http_tracker in tracker_config.http_trackers() {
640 if let Some(domain) = http_tracker.tls_domain() {
641 domains.push(domain.clone());
642 }
643 }
644
645 if let Some(domain_str) = tracker_config.health_check_api().tls_domain() {
647 if let Ok(domain_name) = DomainName::new(domain_str) {
648 domains.push(domain_name);
649 }
650 }
651
652 if let Some(grafana_config) = self.grafana_config() {
654 if let Some(domain_str) = grafana_config.tls_domain() {
655 if let Ok(domain_name) = DomainName::new(domain_str) {
656 domains.push(domain_name);
657 }
658 }
659 }
660
661 domains
662 }
663
664 pub fn destroy(self) -> Result<Environment<Destroyed>, StateTypeError> {
680 match self {
681 Self::Created(env) => Ok(env.destroy()),
682 Self::Provisioning(env) => Ok(env.destroy()),
683 Self::Provisioned(env) => Ok(env.destroy()),
684 Self::Configuring(env) => Ok(env.destroy()),
685 Self::Configured(env) => Ok(env.destroy()),
686 Self::Releasing(env) => Ok(env.destroy()),
687 Self::Released(env) => Ok(env.destroy()),
688 Self::Running(env) => Ok(env.destroy()),
689 Self::Destroying(env) => Ok(env.destroy()),
690 Self::ProvisionFailed(env) => Ok(env.destroy()),
691 Self::ConfigureFailed(env) => Ok(env.destroy()),
692 Self::ReleaseFailed(env) => Ok(env.destroy()),
693 Self::RunFailed(env) => Ok(env.destroy()),
694 Self::DestroyFailed(env) => Ok(env.destroy()),
695 Self::Destroyed(_) => Err(StateTypeError::UnexpectedState {
696 expected: "any state except destroyed",
697 actual: "destroyed".to_string(),
698 }),
699 }
700 }
701
702 #[must_use]
716 pub fn tofu_build_dir(&self) -> std::path::PathBuf {
717 self.context().tofu_build_dir()
718 }
719}
720
721impl std::fmt::Display for AnyEnvironmentState {
733 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
734 write!(
735 f,
736 "Environment '{}' is in state: {}",
737 self.name().as_str(),
738 self.state_name()
739 )?;
740
741 if let Some(error_details) = self.error_details() {
742 write!(f, " (failed at: {error_details})")?;
743 }
744
745 Ok(())
746 }
747}
748
749#[cfg(test)]
750mod tests {
751 use super::*;
752 use crate::adapters::ssh::SshCredentials;
753 use crate::domain::environment::name::EnvironmentName;
754 use crate::domain::provider::{LxdConfig, ProviderConfig};
755 use crate::domain::ProfileName;
756 use crate::shared::Username;
757 use std::path::PathBuf;
758
759 fn default_lxd_provider_config(env_name: &EnvironmentName) -> ProviderConfig {
760 ProviderConfig::Lxd(LxdConfig {
761 profile_name: ProfileName::new(format!("lxd-{}", env_name.as_str())).unwrap(),
762 })
763 }
764
765 fn create_test_ssh_credentials() -> SshCredentials {
767 let username = Username::new("test-user".to_string()).unwrap();
768 SshCredentials::new(
769 PathBuf::from("/tmp/test_key"),
770 PathBuf::from("/tmp/test_key.pub"),
771 username,
772 )
773 }
774
775 fn create_test_environment_created() -> Environment<Created> {
777 let name = EnvironmentName::new("test-env".to_string()).unwrap();
778 let ssh_creds = create_test_ssh_credentials();
779 Environment::new(
780 name.clone(),
781 default_lxd_provider_config(&name),
782 ssh_creds,
783 22,
784 chrono::Utc::now(),
785 )
786 }
787
788 fn create_test_provision_context(error_message: &str) -> ProvisionFailureContext {
790 use crate::domain::environment::TraceId;
791 use crate::shared::ErrorKind;
792 use chrono::Utc;
793 use std::time::Duration;
794
795 ProvisionFailureContext {
796 failed_step: ProvisionStep::OpenTofuApply,
797 error_kind: ErrorKind::InfrastructureOperation,
798 base: BaseFailureContext {
799 error_summary: error_message.to_string(),
800 failed_at: Utc::now(),
801 execution_started_at: Utc::now(),
802 execution_duration: Duration::from_secs(0),
803 trace_id: TraceId::default(),
804 trace_file_path: None,
805 },
806 }
807 }
808
809 fn create_test_configure_context(error_message: &str) -> ConfigureFailureContext {
811 use crate::domain::environment::TraceId;
812 use crate::shared::ErrorKind;
813 use chrono::Utc;
814 use std::time::Duration;
815
816 ConfigureFailureContext {
817 failed_step: ConfigureStep::InstallDocker,
818 error_kind: ErrorKind::CommandExecution,
819 base: BaseFailureContext {
820 error_summary: error_message.to_string(),
821 failed_at: Utc::now(),
822 execution_started_at: Utc::now(),
823 execution_duration: Duration::from_secs(0),
824 trace_id: TraceId::default(),
825 trace_file_path: None,
826 },
827 }
828 }
829
830 fn create_test_release_context(error_message: &str) -> ReleaseFailureContext {
832 use crate::domain::environment::TraceId;
833 use crate::shared::ErrorKind;
834 use chrono::Utc;
835 use std::time::Duration;
836
837 ReleaseFailureContext {
838 failed_step: ReleaseStep::DeployComposeFilesToRemote,
839 error_kind: ErrorKind::InfrastructureOperation,
840 base: BaseFailureContext {
841 error_summary: error_message.to_string(),
842 failed_at: Utc::now(),
843 execution_started_at: Utc::now(),
844 execution_duration: Duration::from_secs(0),
845 trace_id: TraceId::default(),
846 trace_file_path: None,
847 },
848 }
849 }
850
851 fn create_test_run_context(error_message: &str) -> RunFailureContext {
853 use crate::domain::environment::TraceId;
854 use crate::shared::ErrorKind;
855 use chrono::Utc;
856 use std::time::Duration;
857
858 RunFailureContext {
859 failed_step: RunStep::StartServices,
860 error_kind: ErrorKind::InfrastructureOperation,
861 base: BaseFailureContext {
862 error_summary: error_message.to_string(),
863 failed_at: Utc::now(),
864 execution_started_at: Utc::now(),
865 execution_duration: Duration::from_secs(0),
866 trace_id: TraceId::default(),
867 trace_file_path: None,
868 },
869 }
870 }
871
872 #[test]
878 fn it_should_create_provisioning_state() {
879 let state = Provisioning;
880 assert_eq!(state, Provisioning);
881 }
882
883 #[test]
884 fn it_should_create_provisioned_state() {
885 let state = Provisioned;
886 assert_eq!(state, Provisioned);
887 }
888
889 #[test]
890 fn it_should_create_configuring_state() {
891 let state = Configuring;
892 assert_eq!(state, Configuring);
893 }
894
895 #[test]
896 fn it_should_create_configured_state() {
897 let state = Configured;
898 assert_eq!(state, Configured);
899 }
900
901 #[test]
902 fn it_should_create_releasing_state() {
903 let state = Releasing;
904 assert_eq!(state, Releasing);
905 }
906
907 #[test]
908 fn it_should_create_released_state() {
909 let state = Released;
910 assert_eq!(state, Released);
911 }
912
913 #[test]
914 fn it_should_create_running_state() {
915 let state = Running;
916 assert_eq!(state, Running);
917 }
918
919 #[test]
920 fn it_should_create_provision_failed_state_with_context() {
921 let state = ProvisionFailed {
922 context: create_test_provision_context("cloud_init_execution"),
923 };
924 assert_eq!(state.context.base.error_summary, "cloud_init_execution");
925 }
926
927 #[test]
928 fn it_should_clone_provision_failed_state() {
929 let state = ProvisionFailed {
930 context: create_test_provision_context("cloud_init_execution"),
931 };
932 let cloned = state.clone();
933 assert_eq!(state, cloned);
934 }
935
936 #[test]
937 fn it_should_create_configure_failed_state_with_context() {
938 let state = ConfigureFailed {
939 context: create_test_configure_context("ansible_playbook_execution"),
940 };
941 assert_eq!(
942 state.context.base.error_summary,
943 "ansible_playbook_execution"
944 );
945 }
946
947 #[test]
948 fn it_should_create_release_failed_state_with_context() {
949 let context = create_test_release_context("build_artifacts");
950 let state = ReleaseFailed { context };
951 assert_eq!(state.context.base.error_summary, "build_artifacts");
952 }
953
954 #[test]
955 fn it_should_create_run_failed_state_with_context() {
956 let context = create_test_run_context("application_startup");
957 let state = RunFailed { context };
958 assert_eq!(state.context.base.error_summary, "application_startup");
959 }
960
961 #[test]
962 fn it_should_create_destroyed_state() {
963 let state = Destroyed;
964 assert_eq!(state, Destroyed);
965 }
966
967 #[test]
968 fn it_should_serialize_provision_failed_state_to_json() {
969 let state = ProvisionFailed {
970 context: create_test_provision_context("cloud_init"),
971 };
972 let json = serde_json::to_string(&state).unwrap();
973 assert!(json.contains("cloud_init"));
974 assert!(json.contains("context"));
975 }
976
977 #[test]
978 fn it_should_deserialize_provision_failed_state_from_json() {
979 let context = create_test_provision_context("cloud_init");
981 let state = ProvisionFailed {
982 context: context.clone(),
983 };
984 let json = serde_json::to_string(&state).unwrap();
985 let deserialized: ProvisionFailed = serde_json::from_str(&json).unwrap();
986 assert_eq!(deserialized.context.base.error_summary, "cloud_init");
987 }
988
989 #[test]
990 fn it_should_serialize_configure_failed_state_to_json() {
991 let state = ConfigureFailed {
992 context: create_test_configure_context("ansible_playbook"),
993 };
994 let json = serde_json::to_string(&state).unwrap();
995 assert!(json.contains("InstallDocker"));
996 assert!(json.contains("CommandExecution"));
997 let deserialized: ConfigureFailed = serde_json::from_str(&json).unwrap();
998 assert_eq!(deserialized.context.base.error_summary, "ansible_playbook");
999 }
1000
1001 #[test]
1002 fn it_should_deserialize_configure_failed_state_from_json() {
1003 let context = create_test_configure_context("ansible_playbook");
1005 let state = ConfigureFailed {
1006 context: context.clone(),
1007 };
1008 let json = serde_json::to_string(&state).unwrap();
1009 let deserialized: ConfigureFailed = serde_json::from_str(&json).unwrap();
1010 assert_eq!(deserialized.context.base.error_summary, "ansible_playbook");
1011 }
1012
1013 mod any_environment_state_tests {
1015 use std::net::{IpAddr, Ipv4Addr};
1016
1017 use super::*;
1018
1019 #[test]
1023 fn it_should_create_any_environment_state_with_created_variant() {
1024 let env = super::create_test_environment_created();
1025 let any_env = AnyEnvironmentState::Created(env);
1026 assert!(matches!(any_env, AnyEnvironmentState::Created(_)));
1027 }
1028
1029 #[test]
1034 fn it_should_clone_any_environment_state() {
1035 let env = super::create_test_environment_created();
1036 let any_env = AnyEnvironmentState::Created(env);
1037 let cloned = any_env.clone();
1038 assert!(matches!(cloned, AnyEnvironmentState::Created(_)));
1039 }
1040
1041 #[test]
1042 fn it_should_debug_format_any_environment_state() {
1043 let env = super::create_test_environment_created();
1044 let any_env = AnyEnvironmentState::Created(env);
1045 let debug_str = format!("{any_env:?}");
1046 assert!(debug_str.contains("Created"));
1047 }
1048
1049 #[test]
1050 fn it_should_serialize_any_environment_state_to_json() {
1051 let env = super::create_test_environment_created();
1052 let any_env = AnyEnvironmentState::Created(env);
1053 let json = serde_json::to_string(&any_env).unwrap();
1054 assert!(json.contains("Created"));
1055 }
1056
1057 #[test]
1062 fn it_should_convert_provisioning_environment_into_any() {
1063 let env = super::create_test_environment_created().start_provisioning();
1064 let any_env = env.into_any();
1065 assert!(matches!(any_env, AnyEnvironmentState::Provisioning(_)));
1066 }
1067
1068 #[test]
1069 fn it_should_convert_provisioned_environment_into_any() {
1070 let env = super::create_test_environment_created()
1071 .start_provisioning()
1072 .provisioned(
1073 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1074 ProvisionMethod::Provisioned,
1075 );
1076 let any_env = env.into_any();
1077 assert!(matches!(any_env, AnyEnvironmentState::Provisioned(_)));
1078 }
1079
1080 #[test]
1081 fn it_should_convert_configuring_environment_into_any() {
1082 let env = super::create_test_environment_created()
1083 .start_provisioning()
1084 .provisioned(
1085 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1086 ProvisionMethod::Provisioned,
1087 )
1088 .start_configuring();
1089 let any_env = env.into_any();
1090 assert!(matches!(any_env, AnyEnvironmentState::Configuring(_)));
1091 }
1092
1093 #[test]
1094 fn it_should_convert_configured_environment_into_any() {
1095 let env = super::create_test_environment_created()
1096 .start_provisioning()
1097 .provisioned(
1098 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1099 ProvisionMethod::Provisioned,
1100 )
1101 .start_configuring()
1102 .configured();
1103 let any_env = env.into_any();
1104 assert!(matches!(any_env, AnyEnvironmentState::Configured(_)));
1105 }
1106
1107 #[test]
1108 fn it_should_convert_releasing_environment_into_any() {
1109 let env = super::create_test_environment_created()
1110 .start_provisioning()
1111 .provisioned(
1112 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1113 ProvisionMethod::Provisioned,
1114 )
1115 .start_configuring()
1116 .configured()
1117 .start_releasing();
1118 let any_env = env.into_any();
1119 assert!(matches!(any_env, AnyEnvironmentState::Releasing(_)));
1120 }
1121
1122 #[test]
1123 fn it_should_convert_released_environment_into_any() {
1124 let env = super::create_test_environment_created()
1125 .start_provisioning()
1126 .provisioned(
1127 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1128 ProvisionMethod::Provisioned,
1129 )
1130 .start_configuring()
1131 .configured()
1132 .start_releasing()
1133 .released();
1134 let any_env = env.into_any();
1135 assert!(matches!(any_env, AnyEnvironmentState::Released(_)));
1136 }
1137
1138 #[test]
1139 fn it_should_convert_running_environment_into_any() {
1140 let env = super::create_test_environment_created()
1141 .start_provisioning()
1142 .provisioned(
1143 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1144 ProvisionMethod::Provisioned,
1145 )
1146 .start_configuring()
1147 .configured()
1148 .start_releasing()
1149 .released()
1150 .start_running();
1151 let any_env = env.into_any();
1152 assert!(matches!(any_env, AnyEnvironmentState::Running(_)));
1153 }
1154
1155 #[test]
1156 fn it_should_convert_provision_failed_environment_into_any() {
1157 let env = super::create_test_environment_created()
1158 .start_provisioning()
1159 .provision_failed(super::create_test_provision_context("infrastructure error"));
1160 let any_env = env.into_any();
1161 assert!(matches!(any_env, AnyEnvironmentState::ProvisionFailed(_)));
1162 }
1163
1164 #[test]
1165 fn it_should_convert_configure_failed_environment_into_any() {
1166 let env = super::create_test_environment_created()
1167 .start_provisioning()
1168 .provisioned(
1169 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1170 ProvisionMethod::Provisioned,
1171 )
1172 .start_configuring()
1173 .configure_failed(super::create_test_configure_context("ansible error"));
1174 let any_env = env.into_any();
1175 assert!(matches!(any_env, AnyEnvironmentState::ConfigureFailed(_)));
1176 }
1177
1178 #[test]
1179 fn it_should_convert_release_failed_environment_into_any() {
1180 let env = super::create_test_environment_created()
1181 .start_provisioning()
1182 .provisioned(
1183 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1184 ProvisionMethod::Provisioned,
1185 )
1186 .start_configuring()
1187 .configured()
1188 .start_releasing()
1189 .release_failed(super::create_test_release_context("release error"));
1190 let any_env = env.into_any();
1191 assert!(matches!(any_env, AnyEnvironmentState::ReleaseFailed(_)));
1192 }
1193
1194 #[test]
1195 fn it_should_convert_run_failed_environment_into_any() {
1196 let env = super::create_test_environment_created()
1197 .start_provisioning()
1198 .provisioned(
1199 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1200 ProvisionMethod::Provisioned,
1201 )
1202 .start_configuring()
1203 .configured()
1204 .start_releasing()
1205 .released()
1206 .start_running()
1207 .run_failed(super::create_test_run_context("runtime error"));
1208 let any_env = env.into_any();
1209 assert!(matches!(any_env, AnyEnvironmentState::RunFailed(_)));
1210 }
1211
1212 #[test]
1213 fn it_should_convert_destroyed_environment_into_any() {
1214 let env = super::create_test_environment_created().destroy();
1215 let any_env = env.into_any();
1216 assert!(matches!(any_env, AnyEnvironmentState::Destroyed(_)));
1217 }
1218
1219 #[test]
1222 fn it_should_convert_any_to_provisioning_successfully() {
1223 let env = super::create_test_environment_created().start_provisioning();
1224 let any_env = env.into_any();
1225 let result = any_env.try_into_provisioning();
1226 assert!(result.is_ok());
1227 }
1228
1229 #[test]
1230 fn it_should_convert_any_to_provisioned_successfully() {
1231 let env = super::create_test_environment_created()
1232 .start_provisioning()
1233 .provisioned(
1234 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1235 ProvisionMethod::Provisioned,
1236 );
1237 let any_env = env.into_any();
1238 let result = any_env.try_into_provisioned();
1239 assert!(result.is_ok());
1240 }
1241
1242 #[test]
1243 fn it_should_convert_any_to_configuring_successfully() {
1244 let env = super::create_test_environment_created()
1245 .start_provisioning()
1246 .provisioned(
1247 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1248 ProvisionMethod::Provisioned,
1249 )
1250 .start_configuring();
1251 let any_env = env.into_any();
1252 let result = any_env.try_into_configuring();
1253 assert!(result.is_ok());
1254 }
1255
1256 #[test]
1257 fn it_should_convert_any_to_configured_successfully() {
1258 let env = super::create_test_environment_created()
1259 .start_provisioning()
1260 .provisioned(
1261 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1262 ProvisionMethod::Provisioned,
1263 )
1264 .start_configuring()
1265 .configured();
1266 let any_env = env.into_any();
1267 let result = any_env.try_into_configured();
1268 assert!(result.is_ok());
1269 }
1270
1271 #[test]
1272 fn it_should_convert_any_to_releasing_successfully() {
1273 let env = super::create_test_environment_created()
1274 .start_provisioning()
1275 .provisioned(
1276 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1277 ProvisionMethod::Provisioned,
1278 )
1279 .start_configuring()
1280 .configured()
1281 .start_releasing();
1282 let any_env = env.into_any();
1283 let result = any_env.try_into_releasing();
1284 assert!(result.is_ok());
1285 }
1286
1287 #[test]
1288 fn it_should_convert_any_to_released_successfully() {
1289 let env = super::create_test_environment_created()
1290 .start_provisioning()
1291 .provisioned(
1292 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1293 ProvisionMethod::Provisioned,
1294 )
1295 .start_configuring()
1296 .configured()
1297 .start_releasing()
1298 .released();
1299 let any_env = env.into_any();
1300 let result = any_env.try_into_released();
1301 assert!(result.is_ok());
1302 }
1303
1304 #[test]
1305 fn it_should_convert_any_to_running_successfully() {
1306 let env = super::create_test_environment_created()
1307 .start_provisioning()
1308 .provisioned(
1309 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1310 ProvisionMethod::Provisioned,
1311 )
1312 .start_configuring()
1313 .configured()
1314 .start_releasing()
1315 .released()
1316 .start_running();
1317 let any_env = env.into_any();
1318 let result = any_env.try_into_running();
1319 assert!(result.is_ok());
1320 }
1321
1322 #[test]
1323 fn it_should_convert_any_to_provision_failed_successfully() {
1324 let env = super::create_test_environment_created()
1325 .start_provisioning()
1326 .provision_failed(super::create_test_provision_context("test error"));
1327 let any_env = env.into_any();
1328 let result = any_env.try_into_provision_failed();
1329 assert!(result.is_ok());
1330 }
1331
1332 #[test]
1333 fn it_should_convert_any_to_configure_failed_successfully() {
1334 let env = super::create_test_environment_created()
1335 .start_provisioning()
1336 .provisioned(
1337 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1338 ProvisionMethod::Provisioned,
1339 )
1340 .start_configuring()
1341 .configure_failed(super::create_test_configure_context("test error"));
1342 let any_env = env.into_any();
1343 let result = any_env.try_into_configure_failed();
1344 assert!(result.is_ok());
1345 }
1346
1347 #[test]
1348 fn it_should_convert_any_to_release_failed_successfully() {
1349 let env = super::create_test_environment_created()
1350 .start_provisioning()
1351 .provisioned(
1352 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1353 ProvisionMethod::Provisioned,
1354 )
1355 .start_configuring()
1356 .configured()
1357 .start_releasing()
1358 .release_failed(super::create_test_release_context("test error"));
1359 let any_env = env.into_any();
1360 let result = any_env.try_into_release_failed();
1361 assert!(result.is_ok());
1362 }
1363
1364 #[test]
1365 fn it_should_convert_any_to_run_failed_successfully() {
1366 let env = super::create_test_environment_created()
1367 .start_provisioning()
1368 .provisioned(
1369 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1370 ProvisionMethod::Provisioned,
1371 )
1372 .start_configuring()
1373 .configured()
1374 .start_releasing()
1375 .released()
1376 .start_running()
1377 .run_failed(super::create_test_run_context("test error"));
1378 let any_env = env.into_any();
1379 let result = any_env.try_into_run_failed();
1380 assert!(result.is_ok());
1381 }
1382
1383 #[test]
1384 fn it_should_convert_any_to_destroyed_successfully() {
1385 let env = super::create_test_environment_created().destroy();
1386 let any_env = env.into_any();
1387 let result = any_env.try_into_destroyed();
1388 assert!(result.is_ok());
1389 }
1390
1391 #[test]
1394 fn it_should_fail_converting_created_to_provisioning() {
1395 let env = super::create_test_environment_created();
1396 let any_env = env.into_any();
1397 let result = any_env.try_into_provisioning();
1398 assert!(result.is_err());
1399 let err = result.unwrap_err();
1400 assert!(err.to_string().contains("provisioning"));
1401 assert!(err.to_string().contains("created"));
1402 }
1403
1404 #[test]
1405 fn it_should_fail_converting_provision_failed_to_provisioned() {
1406 let env = super::create_test_environment_created()
1407 .start_provisioning()
1408 .provision_failed(super::create_test_provision_context("error"));
1409 let any_env = env.into_any();
1410 let result = any_env.try_into_provisioned();
1411 assert!(result.is_err());
1412 let err = result.unwrap_err();
1413 assert!(err.to_string().contains("provisioned"));
1414 assert!(err.to_string().contains("provision_failed"));
1415 }
1416
1417 #[test]
1418 fn it_should_fail_converting_destroyed_to_running() {
1419 let env = super::create_test_environment_created().destroy();
1420 let any_env = env.into_any();
1421 let result = any_env.try_into_running();
1422 assert!(result.is_err());
1423 let err = result.unwrap_err();
1424 assert!(err.to_string().contains("running"));
1425 assert!(err.to_string().contains("destroyed"));
1426 }
1427
1428 #[test]
1431 fn it_should_preserve_error_details_in_failed_states() {
1432 let error_message = "infrastructure deployment failed";
1433 let context = super::create_test_provision_context(error_message);
1434 let env = super::create_test_environment_created()
1435 .start_provisioning()
1436 .provision_failed(context.clone());
1437
1438 let any_env = env.into_any();
1440 let env_restored = any_env.try_into_provision_failed().unwrap();
1441
1442 assert_eq!(
1443 env_restored.state().context.base.error_summary,
1444 error_message
1445 );
1446 }
1447
1448 #[test]
1451 fn it_should_destroy_environment_from_any_state() {
1452 let env = super::create_test_environment_created();
1453 let any_env = AnyEnvironmentState::Created(env);
1454
1455 let destroyed = any_env.destroy().unwrap();
1456 let destroyed_any = destroyed.into_any();
1458 assert_eq!(destroyed_any.state_name(), "destroyed");
1459 }
1460
1461 #[test]
1462 fn it_should_get_tofu_build_dir_from_any_state() {
1463 let env = super::create_test_environment_created();
1464 let any_env = AnyEnvironmentState::Created(env);
1465
1466 let build_dir = any_env.tofu_build_dir();
1467 assert!(build_dir.ends_with("lxd"));
1468 }
1469
1470 #[test]
1471 fn it_should_error_when_destroying_already_destroyed_environment() {
1472 let env = super::create_test_environment_created().destroy();
1473 let any_env = AnyEnvironmentState::Destroyed(env);
1474
1475 let result = any_env.destroy();
1477 assert!(result.is_err());
1478 let error = result.unwrap_err();
1479 assert_eq!(
1480 error.to_string(),
1481 "Expected state 'any state except destroyed', but found 'destroyed'"
1482 );
1483 }
1484
1485 #[test]
1486 fn it_should_get_tofu_build_dir_from_destroyed_environment() {
1487 let env = super::create_test_environment_created().destroy();
1488 let any_env = AnyEnvironmentState::Destroyed(env);
1489
1490 let build_dir = any_env.tofu_build_dir();
1492 assert!(build_dir.ends_with("lxd"));
1493 }
1494 }
1495
1496 mod introspection_tests {
1497 use super::{
1498 create_test_configure_context, create_test_environment_created,
1499 create_test_provision_context,
1500 };
1501
1502 mod name {
1503 use super::super::EnvironmentName;
1504
1505 #[test]
1506 fn it_should_return_environment_name_for_created_state() {
1507 let any_env = super::create_test_environment_created().into_any();
1508 let env_name = EnvironmentName::new("test-env".to_string()).unwrap();
1509
1510 assert_eq!(any_env.name(), &env_name);
1511 assert_eq!(any_env.name().as_str(), "test-env");
1512 }
1513
1514 #[test]
1515 fn it_should_return_same_name_for_provisioning_state() {
1516 let any_env = super::create_test_environment_created()
1517 .start_provisioning()
1518 .into_any();
1519 let env_name = EnvironmentName::new("test-env".to_string()).unwrap();
1520
1521 assert_eq!(any_env.name(), &env_name);
1522 }
1523
1524 #[test]
1525 fn it_should_return_same_name_for_error_states() {
1526 let any_env = super::create_test_environment_created()
1527 .start_provisioning()
1528 .provision_failed(super::create_test_provision_context("error"))
1529 .into_any();
1530 let env_name = EnvironmentName::new("test-env".to_string()).unwrap();
1531
1532 assert_eq!(any_env.name(), &env_name);
1533 }
1534 }
1535
1536 mod state_name {
1537 use std::net::{IpAddr, Ipv4Addr};
1538
1539 use super::super::ProvisionMethod;
1540
1541 #[test]
1542 fn it_should_return_created_for_created_state() {
1543 let any_env = super::create_test_environment_created().into_any();
1544 assert_eq!(any_env.state_name(), "created");
1545 }
1546
1547 #[test]
1548 fn it_should_return_provisioning_for_provisioning_state() {
1549 let any_env = super::create_test_environment_created()
1550 .start_provisioning()
1551 .into_any();
1552 assert_eq!(any_env.state_name(), "provisioning");
1553 }
1554
1555 #[test]
1556 fn it_should_return_provisioned_for_provisioned_state() {
1557 let any_env = super::create_test_environment_created()
1558 .start_provisioning()
1559 .provisioned(
1560 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1561 ProvisionMethod::Provisioned,
1562 )
1563 .into_any();
1564 assert_eq!(any_env.state_name(), "provisioned");
1565 }
1566
1567 #[test]
1568 fn it_should_return_configuring_for_configuring_state() {
1569 let any_env = super::create_test_environment_created()
1570 .start_provisioning()
1571 .provisioned(
1572 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1573 ProvisionMethod::Provisioned,
1574 )
1575 .start_configuring()
1576 .into_any();
1577 assert_eq!(any_env.state_name(), "configuring");
1578 }
1579
1580 #[test]
1581 fn it_should_return_configured_for_configured_state() {
1582 let any_env = super::create_test_environment_created()
1583 .start_provisioning()
1584 .provisioned(
1585 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1586 ProvisionMethod::Provisioned,
1587 )
1588 .start_configuring()
1589 .configured()
1590 .into_any();
1591 assert_eq!(any_env.state_name(), "configured");
1592 }
1593
1594 #[test]
1595 fn it_should_return_releasing_for_releasing_state() {
1596 let any_env = super::create_test_environment_created()
1597 .start_provisioning()
1598 .provisioned(
1599 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1600 ProvisionMethod::Provisioned,
1601 )
1602 .start_configuring()
1603 .configured()
1604 .start_releasing()
1605 .into_any();
1606 assert_eq!(any_env.state_name(), "releasing");
1607 }
1608
1609 #[test]
1610 fn it_should_return_released_for_released_state() {
1611 let any_env = super::create_test_environment_created()
1612 .start_provisioning()
1613 .provisioned(
1614 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1615 ProvisionMethod::Provisioned,
1616 )
1617 .start_configuring()
1618 .configured()
1619 .start_releasing()
1620 .released()
1621 .into_any();
1622 assert_eq!(any_env.state_name(), "released");
1623 }
1624
1625 #[test]
1626 fn it_should_return_running_for_running_state() {
1627 let any_env = super::create_test_environment_created()
1628 .start_provisioning()
1629 .provisioned(
1630 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1631 ProvisionMethod::Provisioned,
1632 )
1633 .start_configuring()
1634 .configured()
1635 .start_releasing()
1636 .released()
1637 .start_running()
1638 .into_any();
1639 assert_eq!(any_env.state_name(), "running");
1640 }
1641
1642 #[test]
1643 fn it_should_return_provision_failed_for_provision_failed_state() {
1644 let any_env = super::create_test_environment_created()
1645 .start_provisioning()
1646 .provision_failed(super::create_test_provision_context("error"))
1647 .into_any();
1648 assert_eq!(any_env.state_name(), "provision_failed");
1649 }
1650
1651 #[test]
1652 fn it_should_return_configure_failed_for_configure_failed_state() {
1653 let any_env = super::create_test_environment_created()
1654 .start_provisioning()
1655 .provisioned(
1656 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1657 ProvisionMethod::Provisioned,
1658 )
1659 .start_configuring()
1660 .configure_failed(super::create_test_configure_context("error"))
1661 .into_any();
1662 assert_eq!(any_env.state_name(), "configure_failed");
1663 }
1664
1665 #[test]
1666 fn it_should_return_release_failed_for_release_failed_state() {
1667 let any_env = super::create_test_environment_created()
1668 .start_provisioning()
1669 .provisioned(
1670 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1671 ProvisionMethod::Provisioned,
1672 )
1673 .start_configuring()
1674 .configured()
1675 .start_releasing()
1676 .release_failed(super::super::create_test_release_context("error"))
1677 .into_any();
1678 assert_eq!(any_env.state_name(), "release_failed");
1679 }
1680
1681 #[test]
1682 fn it_should_return_run_failed_for_run_failed_state() {
1683 let any_env = super::create_test_environment_created()
1684 .start_provisioning()
1685 .provisioned(
1686 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1687 ProvisionMethod::Provisioned,
1688 )
1689 .start_configuring()
1690 .configured()
1691 .start_releasing()
1692 .released()
1693 .start_running()
1694 .run_failed(super::super::create_test_run_context("error"))
1695 .into_any();
1696 assert_eq!(any_env.state_name(), "run_failed");
1697 }
1698
1699 #[test]
1700 fn it_should_return_destroyed_for_destroyed_state() {
1701 let any_env = super::create_test_environment_created()
1702 .destroy()
1703 .into_any();
1704 assert_eq!(any_env.state_name(), "destroyed");
1705 }
1706 }
1707
1708 mod is_success_state {
1709 use std::net::{IpAddr, Ipv4Addr};
1710
1711 use super::super::ProvisionMethod;
1712
1713 #[test]
1714 fn it_should_return_true_for_created_state() {
1715 let any_env = super::create_test_environment_created().into_any();
1716 assert!(any_env.is_success_state());
1717 }
1718
1719 #[test]
1720 fn it_should_return_true_for_provisioning_state() {
1721 let any_env = super::create_test_environment_created()
1722 .start_provisioning()
1723 .into_any();
1724 assert!(any_env.is_success_state());
1725 }
1726
1727 #[test]
1728 fn it_should_return_true_for_provisioned_state() {
1729 let any_env = super::create_test_environment_created()
1730 .start_provisioning()
1731 .provisioned(
1732 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1733 ProvisionMethod::Provisioned,
1734 )
1735 .into_any();
1736 assert!(any_env.is_success_state());
1737 }
1738
1739 #[test]
1740 fn it_should_return_true_for_configuring_state() {
1741 let any_env = super::create_test_environment_created()
1742 .start_provisioning()
1743 .provisioned(
1744 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1745 ProvisionMethod::Provisioned,
1746 )
1747 .start_configuring()
1748 .into_any();
1749 assert!(any_env.is_success_state());
1750 }
1751
1752 #[test]
1753 fn it_should_return_true_for_configured_state() {
1754 let any_env = super::create_test_environment_created()
1755 .start_provisioning()
1756 .provisioned(
1757 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1758 ProvisionMethod::Provisioned,
1759 )
1760 .start_configuring()
1761 .configured()
1762 .into_any();
1763 assert!(any_env.is_success_state());
1764 }
1765
1766 #[test]
1767 fn it_should_return_true_for_releasing_state() {
1768 let any_env = super::create_test_environment_created()
1769 .start_provisioning()
1770 .provisioned(
1771 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1772 ProvisionMethod::Provisioned,
1773 )
1774 .start_configuring()
1775 .configured()
1776 .start_releasing()
1777 .into_any();
1778 assert!(any_env.is_success_state());
1779 }
1780
1781 #[test]
1782 fn it_should_return_true_for_released_state() {
1783 let any_env = super::create_test_environment_created()
1784 .start_provisioning()
1785 .provisioned(
1786 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1787 ProvisionMethod::Provisioned,
1788 )
1789 .start_configuring()
1790 .configured()
1791 .start_releasing()
1792 .released()
1793 .into_any();
1794 assert!(any_env.is_success_state());
1795 }
1796
1797 #[test]
1798 fn it_should_return_true_for_running_state() {
1799 let any_env = super::create_test_environment_created()
1800 .start_provisioning()
1801 .provisioned(
1802 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1803 ProvisionMethod::Provisioned,
1804 )
1805 .start_configuring()
1806 .configured()
1807 .start_releasing()
1808 .released()
1809 .start_running()
1810 .into_any();
1811 assert!(any_env.is_success_state());
1812 }
1813
1814 #[test]
1815 fn it_should_return_true_for_destroyed_state() {
1816 let any_env = super::create_test_environment_created()
1817 .destroy()
1818 .into_any();
1819 assert!(any_env.is_success_state());
1820 }
1821
1822 #[test]
1823 fn it_should_return_false_for_provision_failed_state() {
1824 let any_env = super::create_test_environment_created()
1825 .start_provisioning()
1826 .provision_failed(super::create_test_provision_context("error"))
1827 .into_any();
1828 assert!(!any_env.is_success_state());
1829 }
1830
1831 #[test]
1832 fn it_should_return_false_for_configure_failed_state() {
1833 let any_env = super::create_test_environment_created()
1834 .start_provisioning()
1835 .provisioned(
1836 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1837 ProvisionMethod::Provisioned,
1838 )
1839 .start_configuring()
1840 .configure_failed(super::create_test_configure_context("error"))
1841 .into_any();
1842 assert!(!any_env.is_success_state());
1843 }
1844
1845 #[test]
1846 fn it_should_return_false_for_release_failed_state() {
1847 let any_env = super::create_test_environment_created()
1848 .start_provisioning()
1849 .provisioned(
1850 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1851 ProvisionMethod::Provisioned,
1852 )
1853 .start_configuring()
1854 .configured()
1855 .start_releasing()
1856 .release_failed(super::super::create_test_release_context("error"))
1857 .into_any();
1858 assert!(!any_env.is_success_state());
1859 }
1860
1861 #[test]
1862 fn it_should_return_false_for_run_failed_state() {
1863 let any_env = super::create_test_environment_created()
1864 .start_provisioning()
1865 .provisioned(
1866 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1867 ProvisionMethod::Provisioned,
1868 )
1869 .start_configuring()
1870 .configured()
1871 .start_releasing()
1872 .released()
1873 .start_running()
1874 .run_failed(super::super::create_test_run_context("error"))
1875 .into_any();
1876 assert!(!any_env.is_success_state());
1877 }
1878 }
1879
1880 mod is_error_state {
1881 use std::net::{IpAddr, Ipv4Addr};
1882
1883 use super::super::ProvisionMethod;
1884
1885 #[test]
1886 fn it_should_return_false_for_success_states() {
1887 let success_states = vec![
1888 super::create_test_environment_created().into_any(),
1889 super::create_test_environment_created()
1890 .start_provisioning()
1891 .into_any(),
1892 super::create_test_environment_created()
1893 .start_provisioning()
1894 .provisioned(
1895 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1896 ProvisionMethod::Provisioned,
1897 )
1898 .into_any(),
1899 super::create_test_environment_created()
1900 .start_provisioning()
1901 .provisioned(
1902 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1903 ProvisionMethod::Provisioned,
1904 )
1905 .start_configuring()
1906 .into_any(),
1907 super::create_test_environment_created()
1908 .start_provisioning()
1909 .provisioned(
1910 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1911 ProvisionMethod::Provisioned,
1912 )
1913 .start_configuring()
1914 .configured()
1915 .into_any(),
1916 super::create_test_environment_created()
1917 .start_provisioning()
1918 .provisioned(
1919 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1920 ProvisionMethod::Provisioned,
1921 )
1922 .start_configuring()
1923 .configured()
1924 .start_releasing()
1925 .into_any(),
1926 super::create_test_environment_created()
1927 .start_provisioning()
1928 .provisioned(
1929 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1930 ProvisionMethod::Provisioned,
1931 )
1932 .start_configuring()
1933 .configured()
1934 .start_releasing()
1935 .released()
1936 .into_any(),
1937 super::create_test_environment_created()
1938 .start_provisioning()
1939 .provisioned(
1940 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1941 ProvisionMethod::Provisioned,
1942 )
1943 .start_configuring()
1944 .configured()
1945 .start_releasing()
1946 .released()
1947 .start_running()
1948 .into_any(),
1949 super::create_test_environment_created()
1950 .destroy()
1951 .into_any(),
1952 ];
1953
1954 for state in success_states {
1955 assert!(!state.is_error_state());
1956 }
1957 }
1958
1959 #[test]
1960 fn it_should_return_true_for_provision_failed_state() {
1961 let any_env = super::create_test_environment_created()
1962 .start_provisioning()
1963 .provision_failed(super::create_test_provision_context("error"))
1964 .into_any();
1965 assert!(any_env.is_error_state());
1966 }
1967
1968 #[test]
1969 fn it_should_return_true_for_configure_failed_state() {
1970 let any_env = super::create_test_environment_created()
1971 .start_provisioning()
1972 .provisioned(
1973 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1974 ProvisionMethod::Provisioned,
1975 )
1976 .start_configuring()
1977 .configure_failed(super::create_test_configure_context("error"))
1978 .into_any();
1979 assert!(any_env.is_error_state());
1980 }
1981
1982 #[test]
1983 fn it_should_return_true_for_release_failed_state() {
1984 let any_env = super::create_test_environment_created()
1985 .start_provisioning()
1986 .provisioned(
1987 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
1988 ProvisionMethod::Provisioned,
1989 )
1990 .start_configuring()
1991 .configured()
1992 .start_releasing()
1993 .release_failed(super::super::create_test_release_context("error"))
1994 .into_any();
1995 assert!(any_env.is_error_state());
1996 }
1997
1998 #[test]
1999 fn it_should_return_true_for_run_failed_state() {
2000 let any_env = super::create_test_environment_created()
2001 .start_provisioning()
2002 .provisioned(
2003 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2004 ProvisionMethod::Provisioned,
2005 )
2006 .start_configuring()
2007 .configured()
2008 .start_releasing()
2009 .released()
2010 .start_running()
2011 .run_failed(super::super::create_test_run_context("error"))
2012 .into_any();
2013 assert!(any_env.is_error_state());
2014 }
2015 }
2016
2017 mod is_terminal_state {
2018 use std::net::{IpAddr, Ipv4Addr};
2019
2020 use super::super::ProvisionMethod;
2021
2022 #[test]
2023 fn it_should_return_false_for_transient_states() {
2024 let transient_states = vec![
2025 super::create_test_environment_created().into_any(),
2026 super::create_test_environment_created()
2027 .start_provisioning()
2028 .into_any(),
2029 super::create_test_environment_created()
2030 .start_provisioning()
2031 .provisioned(
2032 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2033 ProvisionMethod::Provisioned,
2034 )
2035 .into_any(),
2036 super::create_test_environment_created()
2037 .start_provisioning()
2038 .provisioned(
2039 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2040 ProvisionMethod::Provisioned,
2041 )
2042 .start_configuring()
2043 .into_any(),
2044 super::create_test_environment_created()
2045 .start_provisioning()
2046 .provisioned(
2047 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2048 ProvisionMethod::Provisioned,
2049 )
2050 .start_configuring()
2051 .configured()
2052 .into_any(),
2053 super::create_test_environment_created()
2054 .start_provisioning()
2055 .provisioned(
2056 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2057 ProvisionMethod::Provisioned,
2058 )
2059 .start_configuring()
2060 .configured()
2061 .start_releasing()
2062 .into_any(),
2063 super::create_test_environment_created()
2064 .start_provisioning()
2065 .provisioned(
2066 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2067 ProvisionMethod::Provisioned,
2068 )
2069 .start_configuring()
2070 .configured()
2071 .start_releasing()
2072 .released()
2073 .into_any(),
2074 ];
2075
2076 for state in transient_states {
2077 assert!(!state.is_terminal_state());
2078 }
2079 }
2080
2081 #[test]
2082 fn it_should_return_true_for_running_state() {
2083 let any_env = super::create_test_environment_created()
2084 .start_provisioning()
2085 .provisioned(
2086 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2087 ProvisionMethod::Provisioned,
2088 )
2089 .start_configuring()
2090 .configured()
2091 .start_releasing()
2092 .released()
2093 .start_running()
2094 .into_any();
2095 assert!(any_env.is_terminal_state());
2096 }
2097
2098 #[test]
2099 fn it_should_return_true_for_destroyed_state() {
2100 let any_env = super::create_test_environment_created()
2101 .destroy()
2102 .into_any();
2103 assert!(any_env.is_terminal_state());
2104 }
2105
2106 #[test]
2107 fn it_should_return_true_for_provision_failed_state() {
2108 let any_env = super::create_test_environment_created()
2109 .start_provisioning()
2110 .provision_failed(super::create_test_provision_context("error"))
2111 .into_any();
2112 assert!(any_env.is_terminal_state());
2113 }
2114
2115 #[test]
2116 fn it_should_return_true_for_configure_failed_state() {
2117 let any_env = super::create_test_environment_created()
2118 .start_provisioning()
2119 .provisioned(
2120 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2121 ProvisionMethod::Provisioned,
2122 )
2123 .start_configuring()
2124 .configure_failed(super::create_test_configure_context("error"))
2125 .into_any();
2126 assert!(any_env.is_terminal_state());
2127 }
2128
2129 #[test]
2130 fn it_should_return_true_for_release_failed_state() {
2131 let any_env = super::create_test_environment_created()
2132 .start_provisioning()
2133 .provisioned(
2134 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2135 ProvisionMethod::Provisioned,
2136 )
2137 .start_configuring()
2138 .configured()
2139 .start_releasing()
2140 .release_failed(super::super::create_test_release_context("error"))
2141 .into_any();
2142 assert!(any_env.is_terminal_state());
2143 }
2144
2145 #[test]
2146 fn it_should_return_true_for_run_failed_state() {
2147 let any_env = super::create_test_environment_created()
2148 .start_provisioning()
2149 .provisioned(
2150 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2151 ProvisionMethod::Provisioned,
2152 )
2153 .start_configuring()
2154 .configured()
2155 .start_releasing()
2156 .released()
2157 .start_running()
2158 .run_failed(super::super::create_test_run_context("error"))
2159 .into_any();
2160 assert!(any_env.is_terminal_state());
2161 }
2162 }
2163
2164 mod error_details {
2165 use std::net::{IpAddr, Ipv4Addr};
2166
2167 use super::super::ProvisionMethod;
2168
2169 #[test]
2170 fn it_should_return_none_for_success_states() {
2171 let success_states = vec![
2172 super::create_test_environment_created().into_any(),
2173 super::create_test_environment_created()
2174 .start_provisioning()
2175 .into_any(),
2176 super::create_test_environment_created()
2177 .start_provisioning()
2178 .provisioned(
2179 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2180 ProvisionMethod::Provisioned,
2181 )
2182 .into_any(),
2183 super::create_test_environment_created()
2184 .start_provisioning()
2185 .provisioned(
2186 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2187 ProvisionMethod::Provisioned,
2188 )
2189 .start_configuring()
2190 .into_any(),
2191 super::create_test_environment_created()
2192 .start_provisioning()
2193 .provisioned(
2194 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2195 ProvisionMethod::Provisioned,
2196 )
2197 .start_configuring()
2198 .configured()
2199 .into_any(),
2200 super::create_test_environment_created()
2201 .start_provisioning()
2202 .provisioned(
2203 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2204 ProvisionMethod::Provisioned,
2205 )
2206 .start_configuring()
2207 .configured()
2208 .start_releasing()
2209 .into_any(),
2210 super::create_test_environment_created()
2211 .start_provisioning()
2212 .provisioned(
2213 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2214 ProvisionMethod::Provisioned,
2215 )
2216 .start_configuring()
2217 .configured()
2218 .start_releasing()
2219 .released()
2220 .into_any(),
2221 super::create_test_environment_created()
2222 .start_provisioning()
2223 .provisioned(
2224 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2225 ProvisionMethod::Provisioned,
2226 )
2227 .start_configuring()
2228 .configured()
2229 .start_releasing()
2230 .released()
2231 .start_running()
2232 .into_any(),
2233 super::create_test_environment_created()
2234 .destroy()
2235 .into_any(),
2236 ];
2237
2238 for state in success_states {
2239 assert!(state.error_details().is_none());
2240 }
2241 }
2242
2243 #[test]
2244 fn it_should_return_error_message_for_provision_failed_state() {
2245 let error_message = "network timeout during provisioning";
2246 let any_env = super::create_test_environment_created()
2247 .start_provisioning()
2248 .provision_failed(super::create_test_provision_context(error_message))
2249 .into_any();
2250
2251 assert_eq!(any_env.error_details(), Some(error_message));
2252 }
2253
2254 #[test]
2255 fn it_should_return_error_message_for_configure_failed_state() {
2256 let error_message = "ansible playbook failed";
2257 let any_env = super::create_test_environment_created()
2258 .start_provisioning()
2259 .provisioned(
2260 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2261 ProvisionMethod::Provisioned,
2262 )
2263 .start_configuring()
2264 .configure_failed(super::create_test_configure_context(error_message))
2265 .into_any();
2266
2267 assert_eq!(any_env.error_details(), Some(error_message));
2268 }
2269
2270 #[test]
2271 fn it_should_return_error_message_for_release_failed_state() {
2272 let error_message = "release process error";
2273 let any_env = super::create_test_environment_created()
2274 .start_provisioning()
2275 .provisioned(
2276 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2277 ProvisionMethod::Provisioned,
2278 )
2279 .start_configuring()
2280 .configured()
2281 .start_releasing()
2282 .release_failed(super::super::create_test_release_context(error_message))
2283 .into_any();
2284
2285 assert_eq!(any_env.error_details(), Some(error_message));
2286 }
2287
2288 #[test]
2289 fn it_should_return_error_message_for_run_failed_state() {
2290 let error_message = "application crash";
2291 let any_env = super::create_test_environment_created()
2292 .start_provisioning()
2293 .provisioned(
2294 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2295 ProvisionMethod::Provisioned,
2296 )
2297 .start_configuring()
2298 .configured()
2299 .start_releasing()
2300 .released()
2301 .start_running()
2302 .run_failed(super::super::create_test_run_context(error_message))
2303 .into_any();
2304
2305 assert_eq!(any_env.error_details(), Some(error_message));
2306 }
2307 }
2308
2309 mod display {
2310 use std::net::{IpAddr, Ipv4Addr};
2311
2312 use super::super::ProvisionMethod;
2313
2314 #[test]
2315 fn it_should_format_success_state_without_error_details() {
2316 let any_env = super::create_test_environment_created()
2317 .start_provisioning()
2318 .into_any();
2319
2320 let output = format!("{any_env}");
2321 assert_eq!(output, "Environment 'test-env' is in state: provisioning");
2322 }
2323
2324 #[test]
2325 fn it_should_format_running_state() {
2326 let any_env = super::create_test_environment_created()
2327 .start_provisioning()
2328 .provisioned(
2329 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2330 ProvisionMethod::Provisioned,
2331 )
2332 .start_configuring()
2333 .configured()
2334 .start_releasing()
2335 .released()
2336 .start_running()
2337 .into_any();
2338
2339 let output = format!("{any_env}");
2340 assert_eq!(output, "Environment 'test-env' is in state: running");
2341 }
2342
2343 #[test]
2344 fn it_should_format_error_state_with_error_details() {
2345 let error_message = "network timeout";
2346 let any_env = super::create_test_environment_created()
2347 .start_provisioning()
2348 .provision_failed(super::create_test_provision_context(error_message))
2349 .into_any();
2350
2351 let output = format!("{any_env}");
2352 assert_eq!(
2353 output,
2354 format!("Environment 'test-env' is in state: provision_failed (failed at: {error_message})")
2355 );
2356 }
2357
2358 #[test]
2359 fn it_should_format_configure_failed_with_error_details() {
2360 let error_message = "ansible error";
2361 let any_env = super::create_test_environment_created()
2362 .start_provisioning()
2363 .provisioned(
2364 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
2365 ProvisionMethod::Provisioned,
2366 )
2367 .start_configuring()
2368 .configure_failed(super::create_test_configure_context(error_message))
2369 .into_any();
2370
2371 let output = format!("{any_env}");
2372 assert_eq!(
2373 output,
2374 format!("Environment 'test-env' is in state: configure_failed (failed at: {error_message})")
2375 );
2376 }
2377
2378 #[test]
2379 fn it_should_format_destroyed_state() {
2380 let any_env = super::create_test_environment_created()
2381 .destroy()
2382 .into_any();
2383
2384 let output = format!("{any_env}");
2385 assert_eq!(output, "Environment 'test-env' is in state: destroyed");
2386 }
2387
2388 #[test]
2389 fn it_should_work_with_println_macro() {
2390 let any_env = super::create_test_environment_created().into_any();
2391
2392 let output = format!("{any_env}");
2395 assert!(output.contains("test-env"));
2396 assert!(output.contains("created"));
2397 }
2398 }
2399 }
2400}