Invoke the test operation against a command resource. # Arguments `resource` - The resource manifest for the command resource. `expected` - The expected state of the resource in JSON. # Errors Error is returned if the underlying command returns a non-zero exit code.
(resource: &DscResource, expected: &str, target_resource: Option<&DscResource>)
| 342 | /// |
| 343 | /// Error is returned if the underlying command returns a non-zero exit code. |
| 344 | pub fn invoke_test(resource: &DscResource, expected: &str, target_resource: Option<&DscResource>) -> Result<TestResult, DscError> { |
| 345 | debug!("{}", t!("dscresources.commandResource.invokeTest", resource = &resource.type_name)); |
| 346 | let Some(manifest) = &resource.manifest else { |
| 347 | return Err(DscError::MissingManifest(resource.type_name.to_string())); |
| 348 | }; |
| 349 | let Some(test) = &manifest.test else { |
| 350 | info!("{}", t!("dscresources.commandResource.testSyntheticTest", resource = &resource.type_name)); |
| 351 | return invoke_synthetic_test(resource, expected, target_resource); |
| 352 | }; |
| 353 | |
| 354 | verify_json_from_manifest(resource, expected, target_resource)?; |
| 355 | |
| 356 | let resource_type = match target_resource { |
| 357 | Some(r) => r.type_name.clone(), |
| 358 | None => resource.type_name.clone(), |
| 359 | }; |
| 360 | validate_security_context(&test.require_security_context, &resource_type, "test")?; |
| 361 | let path = target_resource.map(|target_resource| target_resource.path.clone()); |
| 362 | let command_resource_info = CommandResourceInfo { |
| 363 | type_name: resource_type.clone(), |
| 364 | path, |
| 365 | }; |
| 366 | let args = process_get_args(test.args.as_ref(), expected, &command_resource_info); |
| 367 | let command_input = get_command_input(test.input.as_ref(), expected)?; |
| 368 | |
| 369 | info!("{}", t!("dscresources.commandResource.invokeTestUsing", resource = &resource.type_name, executable = &test.executable)); |
| 370 | let (exit_code, stdout, stderr) = invoke_command(&test.executable, args, command_input.stdin.as_deref(), Some(&resource.directory), command_input.env, manifest.exit_codes.as_ref())?; |
| 371 | |
| 372 | if resource.kind == Kind::Importer { |
| 373 | debug!("{}", t!("dscresources.commandResource.testGroupTestResponse")); |
| 374 | let group_test_response: Vec<ResourceTestResult> = serde_json::from_str(&stdout)?; |
| 375 | return Ok(TestResult::Group(group_test_response)); |
| 376 | } |
| 377 | |
| 378 | let mut expected_value: Value = serde_json::from_str(expected)?; |
| 379 | match test.returns { |
| 380 | Some(ReturnKind::State) => { |
| 381 | if resource.kind == Kind::Resource { |
| 382 | debug!("{}", t!("dscresources.commandResource.testVerifyOutput", resource = &resource.type_name, executable = &test.executable)); |
| 383 | verify_json_from_manifest(resource, &stdout, target_resource)?; |
| 384 | } |
| 385 | |
| 386 | let actual_value: Value = match serde_json::from_str(&stdout){ |
| 387 | Result::Ok(r) => {r}, |
| 388 | Result::Err(err) => { |
| 389 | return Err(DscError::Operation(t!("dscresources.commandResource.failedParseJson", executable = &test.executable, stdout = stdout, stderr = stderr, err = err).to_string())) |
| 390 | } |
| 391 | }; |
| 392 | let in_desired_state = get_desired_state(&actual_value)?; |
| 393 | let diff_properties = get_diff(&expected_value, &actual_value); |
| 394 | expected_value = redact(&expected_value); |
| 395 | Ok(TestResult::Resource(ResourceTestResponse { |
| 396 | desired_state: expected_value, |
| 397 | actual_state: actual_value, |
| 398 | in_desired_state: in_desired_state.unwrap_or(diff_properties.is_empty()), |
| 399 | diff_properties, |
| 400 | })) |
| 401 | }, |
no test coverage detected