ConfigurationHistoryTests.cs (16138B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="ConfigurationHistoryTests.cs" company="Microsoft Corporation"> 3 // Copyright (c) Microsoft Corporation. Licensed under the MIT License. 4 // </copyright> 5 // ----------------------------------------------------------------------------- 6 7 namespace Microsoft.Management.Configuration.UnitTests.Tests 8 { 9 using System; 10 using System.Collections.Generic; 11 using System.Diagnostics; 12 using System.Diagnostics.CodeAnalysis; 13 using System.Xml.Linq; 14 using Microsoft.Management.Configuration.Processor.Extensions; 15 using Microsoft.Management.Configuration.UnitTests.Fixtures; 16 using Microsoft.Management.Configuration.UnitTests.Helpers; 17 using Microsoft.VisualBasic; 18 using Xunit; 19 using Xunit.Abstractions; 20 21 /// <summary> 22 /// Unit tests for configuration history. 23 /// </summary> 24 [Collection("UnitTestCollection")] 25 [InProc] 26 public class ConfigurationHistoryTests : ConfigurationProcessorTestBase 27 { 28 /// <summary> 29 /// Initializes a new instance of the <see cref="ConfigurationHistoryTests"/> class. 30 /// </summary> 31 /// <param name="fixture">Unit test fixture.</param> 32 /// <param name="log">Log helper.</param> 33 public ConfigurationHistoryTests(UnitTestFixture fixture, ITestOutputHelper log) 34 : base(fixture, log) 35 { 36 } 37 38 /// <summary> 39 /// Checks that the history matches the applied set. 40 /// </summary> 41 [Fact] 42 [OutOfProc] 43 public void ApplySet_HistoryMatches_0_1() 44 { 45 this.RunApplyHistoryMatchTest( 46 @" 47 properties: 48 configurationVersion: 0.1 49 assertions: 50 - resource: Assert 51 id: AssertIdentifier1 52 directives: 53 module: Module 54 settings: 55 Setting1: '1' 56 Setting2: 2 57 - resource: Assert 58 id: AssertIdentifier2 59 dependsOn: 60 - AssertIdentifier1 61 directives: 62 module: Module 63 settings: 64 Setting1: 65 Setting2: 2 66 parameters: 67 - resource: Inform 68 id: InformIdentifier1 69 directives: 70 module: Module2 71 settings: 72 Setting1: 73 Setting2: 74 Setting3: 3 75 resources: 76 - resource: Apply 77 ", new string[] { "AssertIdentifier2" }); 78 } 79 80 /// <summary> 81 /// Checks that the history matches the applied set. 82 /// </summary> 83 [Fact] 84 [OutOfProc] 85 public void ApplySet_HistoryMatches_0_2() 86 { 87 this.RunApplyHistoryMatchTest( 88 @" 89 properties: 90 configurationVersion: 0.2 91 assertions: 92 - resource: Module/Assert 93 id: AssertIdentifier1 94 settings: 95 Setting1: '1' 96 Setting2: 2 97 - resource: Module/Assert 98 id: AssertIdentifier2 99 dependsOn: 100 - AssertIdentifier1 101 directives: 102 description: Describe! 103 settings: 104 Setting1: 105 Setting2: 2 106 parameters: 107 - resource: Module2/Inform 108 id: InformIdentifier1 109 settings: 110 Setting1: 111 Setting2: 112 Setting3: 3 113 resources: 114 - resource: Apply 115 ", new string[] { "AssertIdentifier2" }); 116 } 117 118 /// <summary> 119 /// Checks that the history matches the applied set. 120 /// </summary> 121 [Fact] 122 public void ApplySet_HistoryMatches_0_3() 123 { 124 this.RunApplyHistoryMatchTest( 125 @" 126 $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json 127 metadata: 128 a: 1 129 b: '2' 130 variables: 131 v1: var1 132 v2: 42 133 resources: 134 - name: Name 135 type: Module/Resource 136 metadata: 137 e: '5' 138 f: 6 139 properties: 140 c: 3 141 d: '4' 142 - name: Name2 143 type: Module/Resource2 144 dependsOn: 145 - Name 146 properties: 147 l: '10' 148 metadata: 149 i: '7' 150 j: 8 151 q: 42 152 - name: Group 153 type: Module2/Resource 154 metadata: 155 isGroup: true 156 properties: 157 resources: 158 - name: Child1 159 type: Module3/Resource 160 metadata: 161 e: '5' 162 f: 6 163 properties: 164 c: 3 165 d: '4' 166 - name: Child2 167 type: Module4/Resource2 168 properties: 169 l: '10' 170 metadata: 171 i: '7' 172 j: 8 173 q: 42 174 "); 175 } 176 177 /// <summary> 178 /// Applies a set, reads the history, changes the read set and reapplies it. 179 /// </summary> 180 [Fact] 181 [OutOfProc] 182 public void ApplySet_ChangeHistory() 183 { 184 string disabledIdentifier = "AssertIdentifier2"; 185 186 ConfigurationSet returnedSet = this.RunApplyHistoryMatchTest( 187 @" 188 properties: 189 configurationVersion: 0.2 190 assertions: 191 - resource: Module/Assert 192 id: AssertIdentifier1 193 settings: 194 Setting1: '1' 195 Setting2: 2 196 - resource: Module/Assert 197 id: AssertIdentifier2 198 dependsOn: 199 - AssertIdentifier1 200 directives: 201 description: Describe! 202 settings: 203 Setting1: 204 Setting2: 2 205 parameters: 206 - resource: Module2/Inform 207 id: InformIdentifier1 208 settings: 209 Setting1: 210 Setting2: 211 Setting3: 3 212 resources: 213 - resource: Apply 214 ", new string[] { disabledIdentifier }); 215 216 foreach (ConfigurationUnit unit in returnedSet.Units) 217 { 218 if (unit.Identifier == disabledIdentifier) 219 { 220 unit.IsActive = true; 221 } 222 } 223 224 TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); 225 ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); 226 227 ApplyConfigurationSetResult result = processor.ApplySet(returnedSet, ApplyConfigurationSetFlags.None); 228 Assert.NotNull(result); 229 Assert.Null(result.ResultCode); 230 231 ConfigurationSet? historySet = null; 232 233 foreach (ConfigurationSet set in processor.GetConfigurationHistory()) 234 { 235 if (set.InstanceIdentifier == returnedSet.InstanceIdentifier) 236 { 237 historySet = set; 238 } 239 } 240 241 this.AssertSetsEqual(returnedSet, historySet); 242 } 243 244 /// <summary> 245 /// Applies a set, reads the history and removes it. 246 /// </summary> 247 [Fact] 248 [OutOfProc] 249 public void ApplySet_RemoveHistory() 250 { 251 ConfigurationSet returnedSet = this.RunApplyHistoryMatchTest( 252 @" 253 properties: 254 configurationVersion: 0.2 255 assertions: 256 - resource: Module/Assert 257 id: AssertIdentifier1 258 settings: 259 Setting1: '1' 260 Setting2: 2 261 - resource: Module/Assert 262 id: AssertIdentifier2 263 dependsOn: 264 - AssertIdentifier1 265 directives: 266 description: Describe! 267 settings: 268 Setting1: 269 Setting2: 2 270 parameters: 271 - resource: Module2/Inform 272 id: InformIdentifier1 273 settings: 274 Setting1: 275 Setting2: 276 Setting3: 3 277 resources: 278 - resource: Apply 279 "); 280 281 returnedSet.Remove(); 282 283 TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); 284 ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); 285 286 ConfigurationSet? historySet = null; 287 288 foreach (ConfigurationSet set in processor.GetConfigurationHistory()) 289 { 290 if (set.InstanceIdentifier == returnedSet.InstanceIdentifier) 291 { 292 historySet = set; 293 } 294 } 295 296 Assert.Null(historySet); 297 } 298 299 [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2927")] 300 private ConfigurationSet RunApplyHistoryMatchTest(string contents, string[]? inactiveIdentifiers = null) 301 { 302 TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); 303 ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); 304 305 OpenConfigurationSetResult configurationSetResult = processor.OpenConfigurationSet(this.CreateStream(contents)); 306 ConfigurationSet configurationSet = configurationSetResult.Set; 307 Assert.NotNull(configurationSet); 308 309 TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); 310 setProcessor.EnableDefaultGroupProcessorCreation = true; 311 312 configurationSet.Name = "Test Name"; 313 configurationSet.Origin = "Test Origin"; 314 configurationSet.Path = "Test Path"; 315 316 if (inactiveIdentifiers != null) 317 { 318 foreach (string identifier in inactiveIdentifiers) 319 { 320 foreach (ConfigurationUnit unit in configurationSet.Units) 321 { 322 if (unit.Identifier == identifier) 323 { 324 unit.IsActive = false; 325 } 326 } 327 } 328 } 329 330 ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); 331 Assert.NotNull(result); 332 Assert.Null(result.ResultCode); 333 334 ConfigurationSet? historySet = null; 335 336 foreach (ConfigurationSet set in processor.GetConfigurationHistory()) 337 { 338 if (set.InstanceIdentifier == configurationSet.InstanceIdentifier) 339 { 340 historySet = set; 341 } 342 } 343 344 this.AssertSetsEqual(configurationSet, historySet); 345 this.AssertResultsEqual(result, historySet); 346 return historySet; 347 } 348 349 private void AssertSetsEqual(ConfigurationSet expectedSet, [NotNull] ConfigurationSet? actualSet) 350 { 351 Assert.NotNull(actualSet); 352 Assert.Equal(expectedSet.Name, actualSet.Name); 353 Assert.Equal(expectedSet.Origin, actualSet.Origin); 354 Assert.Equal(expectedSet.Path, actualSet.Path); 355 356 Assert.Equal(ConfigurationSetState.Completed, actualSet.State); 357 358 this.AssertTimeNotZero(actualSet.FirstApply); 359 this.AssertTimeNotZero(actualSet.ApplyBegun); 360 this.AssertTimeNotZero(actualSet.ApplyEnded); 361 Assert.True(actualSet.FirstApply <= actualSet.ApplyBegun); 362 Assert.True(actualSet.ApplyBegun <= actualSet.ApplyEnded); 363 364 Assert.Equal(expectedSet.SchemaVersion, actualSet.SchemaVersion); 365 Assert.Equal(expectedSet.SchemaUri, actualSet.SchemaUri); 366 Assert.True(expectedSet.Metadata.ContentEquals(actualSet.Metadata)); 367 368 this.AssertUnitsListEqual(expectedSet.Units, actualSet.Units); 369 } 370 371 private void AssertTimeNotZero(DateTimeOffset actualTime) 372 { 373 Assert.NotEqual(DateTimeOffset.UnixEpoch, actualTime); 374 Assert.NotEqual(DateTimeOffset.MinValue, actualTime); 375 } 376 377 private void AssertUnitsListEqual(IList<ConfigurationUnit> expectedUnits, IList<ConfigurationUnit> actualUnits) 378 { 379 Assert.Equal(expectedUnits.Count, actualUnits.Count); 380 381 foreach (ConfigurationUnit expectedUnit in expectedUnits) 382 { 383 ConfigurationUnit? actualUnit = null; 384 foreach (ConfigurationUnit historyUnit in actualUnits) 385 { 386 if (historyUnit.InstanceIdentifier == expectedUnit.InstanceIdentifier) 387 { 388 actualUnit = historyUnit; 389 } 390 } 391 392 this.AssertUnitsEqual(expectedUnit, actualUnit); 393 } 394 } 395 396 private void AssertUnitsEqual(ConfigurationUnit expectedUnit, ConfigurationUnit? actualUnit) 397 { 398 Assert.NotNull(actualUnit); 399 Assert.Equal(expectedUnit.Type, actualUnit.Type); 400 Assert.Equal(expectedUnit.Identifier, actualUnit.Identifier); 401 Assert.Equal(expectedUnit.Intent, actualUnit.Intent); 402 Assert.Equal(expectedUnit.Dependencies, actualUnit.Dependencies); 403 Assert.True(expectedUnit.Metadata.ContentEquals(actualUnit.Metadata), $"Metadata not equal: {expectedUnit.Identifier}\n---expected---:\n{expectedUnit.Metadata.ToYaml()}\n---actual---:\n{actualUnit.Metadata.ToYaml()}"); 404 Assert.True(expectedUnit.Settings.ContentEquals(actualUnit.Settings)); 405 Assert.Equal(expectedUnit.IsActive, actualUnit.IsActive); 406 Assert.Equal(expectedUnit.IsGroup, actualUnit.IsGroup); 407 408 if (expectedUnit.IsGroup) 409 { 410 this.AssertUnitsListEqual(expectedUnit.Units, actualUnit.Units); 411 } 412 } 413 414 private void AssertResultsEqual(ApplyConfigurationSetResult expected, ConfigurationSet actualSet) 415 { 416 List<ConfigurationUnit> actualUnitList = new List<ConfigurationUnit>(); 417 418 foreach (ConfigurationUnit unit in actualSet.Units) 419 { 420 this.AccumulateUnits(actualUnitList, unit); 421 } 422 423 foreach (ApplyConfigurationUnitResult expectedUnitResult in expected.UnitResults) 424 { 425 ConfigurationUnit? actualUnit = null; 426 foreach (ConfigurationUnit historyUnit in actualUnitList) 427 { 428 if (historyUnit.InstanceIdentifier == expectedUnitResult.Unit.InstanceIdentifier) 429 { 430 actualUnit = historyUnit; 431 } 432 } 433 434 this.AssertUnitResultsEqual(expectedUnitResult, actualUnit); 435 } 436 } 437 438 private void AccumulateUnits(List<ConfigurationUnit> unitList, ConfigurationUnit unit) 439 { 440 unitList.Add(unit); 441 if (unit.IsGroup) 442 { 443 foreach (ConfigurationUnit child in unit.Units) 444 { 445 this.AccumulateUnits(unitList, child); 446 } 447 } 448 } 449 450 private void AssertUnitResultsEqual(ApplyConfigurationUnitResult expectedResult, ConfigurationUnit? actualUnit) 451 { 452 Assert.NotNull(actualUnit); 453 Assert.Equal(expectedResult.State, actualUnit.State); 454 455 var expectedResultInformation = expectedResult.ResultInformation; 456 if (expectedResultInformation != null) 457 { 458 var actualResultInformation = actualUnit.ResultInformation; 459 Assert.NotNull(actualResultInformation); 460 461 Assert.Equal(expectedResultInformation.ResultCode == null, actualResultInformation.ResultCode == null); 462 if (expectedResultInformation.ResultCode != null) 463 { 464 Assert.Equal(expectedResultInformation.ResultCode.HResult, actualResultInformation.ResultCode!.HResult); 465 } 466 467 Assert.Equal(expectedResultInformation.Description, actualResultInformation.Description); 468 Assert.Equal(expectedResultInformation.Details, actualResultInformation.Details); 469 Assert.Equal(expectedResultInformation.ResultSource, actualResultInformation.ResultSource); 470 } 471 } 472 } 473 }