1
2pub mod TestControllerState;
28
29pub mod TestProviderState;
30
31pub mod TestResult;
32
33pub mod TestRun;
34
35pub mod TestRunStatus;
36
37use std::sync::Arc;
38
39use CommonLibrary::{
40 Environment::Requires::Requires,
41 Error::CommonError::CommonError,
42 IPC::{DTO::ProxyTarget::ProxyTarget, IPCProvider::IPCProvider, SkyEvent::SkyEvent},
43 Testing::TestController::TestController,
44};
45use async_trait::async_trait;
46use serde_json::{Value, json};
47use tauri::Emitter;
48use uuid::Uuid;
49
50use super::MountainEnvironment::MountainEnvironment;
51use crate::dev_log;
52
53#[async_trait]
54impl TestController for MountainEnvironment {
55 async fn RegisterTestController(&self, ControllerId:String, Label:String) -> Result<(), CommonError> {
56 dev_log!(
57 "extensions",
58 "[TestProvider] Registering test controller '{}' with label '{}'",
59 ControllerId,
60 Label
61 );
62
63 let SideCarIdentifier = Some("cocoon-main".to_string());
64
65 let ControllerState = TestControllerState::Struct {
66 ControllerIdentifier:ControllerId.clone(),
67
68 Label,
69
70 SideCarIdentifier,
71
72 IsActive:true,
73
74 SupportedTestTypes:vec!["unit".to_string(), "integration".to_string()],
75 };
76
77 let mut StateGuard = self.ApplicationState.TestProviderState.write().await;
78
79 StateGuard.Controllers.insert(ControllerId.clone(), ControllerState);
80
81 drop(StateGuard);
82
83 self.ApplicationHandle
84 .emit(
85 SkyEvent::TestRegistered.AsStr(),
86 json!({ "ControllerIdentifier": ControllerId }),
87 )
88 .map_err(|Error| {
89 CommonError::IPCError { Description:format!("Failed to emit test registration event: {}", Error) }
90 })?;
91
92 dev_log!(
93 "extensions",
94 "[TestProvider] Test controller '{}' registered successfully",
95 ControllerId
96 );
97
98 Ok(())
99 }
100
101 async fn RunTests(&self, ControllerIdentifier:String, TestRunRequest:Value) -> Result<(), CommonError> {
102 dev_log!(
103 "extensions",
104 "[TestProvider] Running tests for controller '{}': {:?}",
105 ControllerIdentifier,
106 TestRunRequest
107 );
108
109 let ControllerState = {
110 let StateGuard = self.ApplicationState.TestProviderState.read().await;
111
112 StateGuard.Controllers.get(&ControllerIdentifier).cloned().ok_or_else(|| {
113 CommonError::TestControllerNotFound { ControllerIdentifier:ControllerIdentifier.clone() }
114 })?
115 };
116
117 let RunIdentifier = Uuid::new_v4().to_string();
118
119 let TestRunRecord = TestRun::Struct {
120 RunIdentifier:RunIdentifier.clone(),
121
122 ControllerIdentifier:ControllerIdentifier.clone(),
123
124 Status:TestRunStatus::Enum::Queued,
125
126 StartedAt:std::time::Instant::now(),
127
128 Results:std::collections::HashMap::new(),
129 };
130
131 {
132 let mut StateGuard = self.ApplicationState.TestProviderState.write().await;
133
134 StateGuard.ActiveRuns.insert(RunIdentifier.clone(), TestRunRecord);
135 }
136
137 self.ApplicationHandle
138 .emit(
139 SkyEvent::TestRunStarted.AsStr(),
140 json!({ "RunIdentifier": RunIdentifier, "ControllerIdentifier": ControllerIdentifier }),
141 )
142 .map_err(|Error| {
143 CommonError::IPCError { Description:format!("Failed to emit test run started event: {}", Error) }
144 })?;
145
146 if let Some(SideCarIdentifier) = &ControllerState.SideCarIdentifier {
147 Self::RunProxiedTests(self, SideCarIdentifier, &RunIdentifier, TestRunRequest).await?;
148 } else {
149 dev_log!(
150 "extensions",
151 "warn: [TestProvider] Native test controllers not yet implemented for '{}'",
152 ControllerIdentifier
153 );
154
155 let _ = Self::UpdateRunStatus(self, &RunIdentifier, TestRunStatus::Enum::Skipped).await;
156 }
157
158 Ok(())
159 }
160}
161
162impl MountainEnvironment {
163 async fn RunProxiedTests(
164 &self,
165
166 SideCarIdentifier:&str,
167
168 RunIdentifier:&str,
169
170 TestRunRequest:Value,
171 ) -> Result<(), CommonError> {
172 dev_log!(
173 "extensions",
174 "[TestProvider] Running proxied tests for run '{}' on sidecar '{}'",
175 RunIdentifier,
176 SideCarIdentifier
177 );
178
179 let _ = Self::UpdateRunStatus(self, RunIdentifier, TestRunStatus::Enum::Running).await;
180
181 let IPCProviderHandle:Arc<dyn IPCProvider> = self.Require();
182
183 let RPCMethod = format!("{}$runTests", ProxyTarget::ExtHostTesting.GetTargetPrefix());
184
185 let RPCParams = json!({ "RunIdentifier": RunIdentifier, "TestRunRequest": TestRunRequest });
186
187 match IPCProviderHandle
188 .SendRequestToSideCar(SideCarIdentifier.to_string(), RPCMethod, RPCParams, 300000)
189 .await
190 {
191 Ok(Response) => {
192 if let Ok(Results) = serde_json::from_value::<Vec<TestResult::Struct>>(Response) {
193 let _ = Self::StoreTestResults(self, RunIdentifier, Results).await;
194
195 let FinalStatus = Self::CalculateRunStatus(self, RunIdentifier).await;
196
197 let _ = Self::UpdateRunStatus(self, RunIdentifier, FinalStatus).await;
198
199 dev_log!(
200 "extensions",
201 "[TestProvider] Test run '{}' completed with status {:?}",
202 RunIdentifier,
203 FinalStatus
204 );
205 } else {
206 dev_log!(
207 "extensions",
208 "error: [TestProvider] Failed to parse test results for run '{}'",
209 RunIdentifier
210 );
211
212 let _ = Self::UpdateRunStatus(self, RunIdentifier, TestRunStatus::Enum::Errored).await;
213 }
214
215 Ok(())
216 },
217
218 Err(Error) => {
219 dev_log!("extensions", "error: [TestProvider] Failed to run tests: {}", Error);
220
221 let _ = Self::UpdateRunStatus(self, RunIdentifier, TestRunStatus::Enum::Errored).await;
222
223 Err(Error)
224 },
225 }
226 }
227
228 async fn UpdateRunStatus(&self, RunIdentifier:&str, Status:TestRunStatus::Enum) -> Result<(), CommonError> {
229 let mut StateGuard = self.ApplicationState.TestProviderState.write().await;
230
231 if let Some(TestRunRecord) = StateGuard.ActiveRuns.get_mut(RunIdentifier) {
232 TestRunRecord.Status = Status;
233
234 drop(StateGuard);
235
236 self.ApplicationHandle
237 .emit(
238 SkyEvent::TestRunStatusChanged.AsStr(),
239 json!({ "RunIdentifier": RunIdentifier, "Status": Status }),
240 )
241 .map_err(|Error| {
242 CommonError::IPCError { Description:format!("Failed to emit test status change event: {}", Error) }
243 })?;
244
245 Ok(())
246 } else {
247 Err(CommonError::TestRunNotFound { RunIdentifier:RunIdentifier.to_string() })
248 }
249 }
250
251 async fn StoreTestResults(&self, RunIdentifier:&str, Results:Vec<TestResult::Struct>) -> Result<(), CommonError> {
252 let mut StateGuard = self.ApplicationState.TestProviderState.write().await;
253
254 if let Some(TestRunRecord) = StateGuard.ActiveRuns.get_mut(RunIdentifier) {
255 for Result in Results {
256 TestRunRecord.Results.insert(Result.TestIdentifier.clone(), Result);
257 }
258
259 Ok(())
260 } else {
261 Err(CommonError::TestRunNotFound { RunIdentifier:RunIdentifier.to_string() })
262 }
263 }
264
265 async fn CalculateRunStatus(&self, RunIdentifier:&str) -> TestRunStatus::Enum {
266 let StateGuard = self.ApplicationState.TestProviderState.read().await;
267
268 if let Some(TestRunRecord) = StateGuard.ActiveRuns.get(RunIdentifier) {
269 if TestRunRecord.Results.is_empty() {
270 TestRunStatus::Enum::Passed
271 } else {
272 let HasFailed = TestRunRecord.Results.values().any(|R| R.Status == TestRunStatus::Enum::Failed);
273
274 let HasErrored = TestRunRecord.Results.values().any(|R| R.Status == TestRunStatus::Enum::Errored);
275
276 if HasErrored {
277 TestRunStatus::Enum::Errored
278 } else if HasFailed {
279 TestRunStatus::Enum::Failed
280 } else {
281 TestRunStatus::Enum::Passed
282 }
283 }
284 } else {
285 TestRunStatus::Enum::Errored
286 }
287 }
288}