Skip to content

Commit 3a89a0c

Browse files
committed
Fix when resource test returns '_inDesiredState', that takes precedence
1 parent 416c770 commit 3a89a0c

File tree

7 files changed

+111
-3
lines changed

7 files changed

+111
-3
lines changed

dsc/tests/dsc_config_test.tests.ps1

+27
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,31 @@ Describe 'dsc config test tests' {
3232
$out.results[0].result.differingProperties | Should -Contain 'resources'
3333
}
3434
}
35+
36+
It '_inDesiredState returned is used when: <inDesiredState>' -TestCases @(
37+
@{ inDesiredState = $true }
38+
@{ inDesiredState = $false }
39+
) {
40+
param($inDesiredState)
41+
42+
$configYaml = @"
43+
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
44+
resources:
45+
- name: Test
46+
type: Test/InDesiredState
47+
properties:
48+
_inDesiredState: $inDesiredState
49+
value: Hello
50+
"@
51+
52+
$out = dsc config test -i $configYaml | ConvertFrom-Json
53+
$LASTEXITCODE | Should -Be 0
54+
$out.results[0].result.inDesiredState | Should -Be $inDesiredState
55+
if ($inDesiredState) {
56+
$out.results[0].result.differingProperties | Should -BeNullOrEmpty
57+
}
58+
else {
59+
$out.results[0].result.differingProperties | Should -Contain 'value'
60+
}
61+
}
3562
}

dsc_lib/locales/en-us.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ setUnexpectedOutput = "Command did not return expected actual output"
9999
setUnexpectedDiff = "Command did not return expected diff output"
100100
invokeTest = "Invoking test for '%{resource}'"
101101
testSyntheticTest = "Resource '%{resource}' does not implement test, performing synthetic test"
102-
invokeTestUsing = "Invoking test on '%{resource}' using '{executable}'"
102+
invokeTestUsing = "Invoking test on '%{resource}' using '%{executable}'"
103103
testVerifyOutput = "Verifying output of test on '%{resource}' using '%{executable}'"
104104
testGroupTestResponse = "Import resource kind, returning group test response"
105105
testNoActualState = "No actual state returned"
@@ -129,6 +129,7 @@ validateJson = "Validating against JSON: %{json}"
129129
resourceInvalidJson = "Resource reported input JSON is not valid"
130130
invalidArrayKey = "Unsupported array value for key '%{key}'. Only string and number is supported."
131131
invalidKey = "Unsupported value for key '%{key}'. Only string, bool, number, and array is supported."
132+
inDesiredStateNotBool = "'_inDesiredState' is not a boolean"
132133

133134
[dscresources.dscresource]
134135
invokeGet = "Invoking get for '%{resource}'"

dsc_lib/src/dscresources/command_resource.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,29 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re
283283
return Err(DscError::Operation(t!("dscresources.commandResource.failedParseJson", executable = &test.executable, stdout = stdout, stderr = stderr, err = err).to_string()))
284284
}
285285
};
286-
let diff_properties = get_diff(&expected_value, &actual_value);
286+
// if actual state contains _inDesiredState, we use that to determine if the resource is in desired state
287+
let mut in_desired_state: Option<bool> = None;
288+
if let Some(in_desired_state_value) = actual_value.get("_inDesiredState") {
289+
if let Some(desired_state) = in_desired_state_value.as_bool() {
290+
in_desired_state = Some(desired_state);
291+
} else {
292+
return Err(DscError::Operation(t!("dscresources.commandResource.inDesiredStateNotBool").to_string()));
293+
}
294+
}
295+
296+
let mut diff_properties: Vec<String> = Vec::new();
297+
match in_desired_state {
298+
Some(true) => {
299+
// if _inDesiredState is true, we don't need to check for diff properties
300+
},
301+
Some(false) | None => {
302+
diff_properties = get_diff(&expected_value, &actual_value);
303+
}
304+
}
287305
Ok(TestResult::Resource(ResourceTestResponse {
288306
desired_state: expected_value,
289307
actual_state: actual_value,
290-
in_desired_state: diff_properties.is_empty(),
308+
in_desired_state: in_desired_state.unwrap_or(diff_properties.is_empty()),
291309
diff_properties,
292310
}))
293311
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json",
3+
"type": "Test/InDesiredState",
4+
"version": "0.1.0",
5+
"test": {
6+
"executable": "dsctest",
7+
"args": [
8+
"in-desired-state",
9+
{
10+
"jsonInputArg": "--input",
11+
"mandatory": true
12+
}
13+
],
14+
"return": "state"
15+
},
16+
"schema": {
17+
"command": {
18+
"executable": "dsctest",
19+
"args": [
20+
"schema",
21+
"-s",
22+
"in-desired-state"
23+
]
24+
}
25+
}
26+
}

tools/dsctest/src/args.rs

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub enum Schemas {
88
Delete,
99
Exist,
1010
ExitCode,
11+
InDesiredState,
1112
Sleep,
1213
Trace,
1314
WhatIf,
@@ -41,6 +42,12 @@ pub enum SubCommand {
4142
input: String,
4243
},
4344

45+
#[clap(name = "in-desired-state", about = "Specify if the resource is in the desired state")]
46+
InDesiredState {
47+
#[clap(name = "input", short, long, help = "The input to the in desired state command as JSON")]
48+
input: String,
49+
},
50+
4451
#[clap(name = "schema", about = "Get the JSON schema for a subcommand")]
4552
Schema {
4653
#[clap(name = "subcommand", short, long, help = "The subcommand to get the schema for")]

tools/dsctest/src/in_desired_state.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use schemars::JsonSchema;
5+
use serde::{Deserialize, Serialize};
6+
7+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
8+
#[serde(deny_unknown_fields)]
9+
pub struct InDesiredState {
10+
#[serde(rename = "_inDesiredState", skip_serializing_if = "Option::is_none")]
11+
pub in_desired_state: Option<bool>,
12+
pub value: String,
13+
}

tools/dsctest/src/main.rs

+16
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod args;
55
mod delete;
66
mod exist;
77
mod exit_code;
8+
mod in_desired_state;
89
mod sleep;
910
mod trace;
1011
mod whatif;
@@ -15,6 +16,7 @@ use schemars::schema_for;
1516
use crate::delete::Delete;
1617
use crate::exist::{Exist, State};
1718
use crate::exit_code::ExitCode;
19+
use crate::in_desired_state::InDesiredState;
1820
use crate::sleep::Sleep;
1921
use crate::trace::Trace;
2022
use crate::whatif::WhatIf;
@@ -65,6 +67,17 @@ fn main() {
6567
}
6668
input
6769
},
70+
SubCommand::InDesiredState { input } => {
71+
let mut in_desired_state = match serde_json::from_str::<in_desired_state::InDesiredState>(&input) {
72+
Ok(in_desired_state) => in_desired_state,
73+
Err(err) => {
74+
eprintln!("Error JSON does not match schema: {err}");
75+
std::process::exit(1);
76+
}
77+
};
78+
in_desired_state.value = "SomethingElse".to_string();
79+
serde_json::to_string(&in_desired_state).unwrap()
80+
},
6881
SubCommand::Schema { subcommand } => {
6982
let schema = match subcommand {
7083
Schemas::Delete => {
@@ -76,6 +89,9 @@ fn main() {
7689
Schemas::ExitCode => {
7790
schema_for!(ExitCode)
7891
},
92+
Schemas::InDesiredState => {
93+
schema_for!(InDesiredState)
94+
},
7995
Schemas::Sleep => {
8096
schema_for!(Sleep)
8197
},

0 commit comments

Comments
 (0)