ConfigureCommand.cs (20076B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="ConfigureCommand.cs" company="Microsoft Corporation"> 3 // Copyright (c) Microsoft Corporation. Licensed under the MIT License. 4 // </copyright> 5 // ----------------------------------------------------------------------------- 6 7 namespace AppInstallerCLIE2ETests 8 { 9 using System; 10 using System.IO; 11 using AppInstallerCLIE2ETests.Helpers; 12 using NUnit.Framework; 13 14 /// <summary> 15 /// `Configure` command tests. 16 /// </summary> 17 public class ConfigureCommand 18 { 19 private const string CommandAndAgreementsAndVerbose = "configure --accept-configuration-agreements --verbose"; 20 21 /// <summary> 22 /// Ensures that the test resources manifests are present. 23 /// </summary> 24 public static void EnsureTestResourcePresence() 25 { 26 DSCv3ResourceTestBase.EnsureTestResourcePresence(); 27 } 28 29 /// <summary> 30 /// Setup done once before all the tests here. 31 /// </summary> 32 [OneTimeSetUp] 33 public void OneTimeSetup() 34 { 35 this.DeleteResourceArtifacts(); 36 EnsureTestResourcePresence(); 37 TestCommon.SetupTestSource(false); 38 } 39 40 /// <summary> 41 /// Teardown done once after all the tests here. 42 /// </summary> 43 [OneTimeTearDown] 44 public void OneTimeTeardown() 45 { 46 this.DeleteResourceArtifacts(); 47 TestCommon.TearDownTestSource(); 48 } 49 50 /// <summary> 51 /// Simple test to confirm that a resource without a module specified can be discovered in the PSGallery. 52 /// Intentionally has no settings to force a failure, but only after acquiring the module. 53 /// </summary> 54 [Test] 55 [Ignore("PS Gallery tests are unreliable.")] 56 public void ConfigureFromGallery() 57 { 58 TestCommon.EnsureModuleState(Constants.GalleryTestModuleName, present: false); 59 60 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\PSGallery_NoModule_NoSettings.yml"), timeOut: 120000); 61 Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); 62 Assert.True(result.StdOut.Contains("The configuration unit failed while attempting to test the current system state.")); 63 } 64 65 /// <summary> 66 /// Simple test to confirm that a resource with a module specified can be discovered in a local repository that doesn't support resource discovery. 67 /// </summary> 68 [Test] 69 public void ConfigureFromTestRepo() 70 { 71 TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); 72 73 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); 74 Assert.AreEqual(0, result.ExitCode); 75 76 // The configuration creates a file next to itself with the given contents 77 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"); 78 FileAssert.Exists(targetFilePath); 79 Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); 80 81 Assert.True(Directory.Exists( 82 Path.Combine( 83 TestCommon.GetExpectedModulePath(TestCommon.TestModuleLocation.Default), 84 Constants.SimpleTestModuleName))); 85 } 86 87 /// <summary> 88 /// Simple test to confirm that the module was installed to the location specified in the DefaultModuleRoot settings. 89 /// </summary> 90 [Test] 91 public void ConfigureFromTestRepo_DefaultModuleRootSetting() 92 { 93 TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); 94 string moduleTestDir = TestCommon.GetRandomTestDir(); 95 WinGetSettingsHelper.ConfigureConfigureBehavior(Constants.DefaultModuleRoot, moduleTestDir); 96 97 string args = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo_Location.yml"); 98 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args); 99 100 WinGetSettingsHelper.ConfigureConfigureBehavior(Constants.DefaultModuleRoot, string.Empty); 101 bool moduleExists = Directory.Exists(Path.Combine(moduleTestDir, Constants.SimpleTestModuleName)); 102 if (moduleExists) 103 { 104 // Clean test directory to avoid impacting other tests. 105 Directory.Delete(moduleTestDir, true); 106 } 107 108 Assert.AreEqual(0, result.ExitCode); 109 Assert.True(moduleExists); 110 } 111 112 /// <summary> 113 /// Simple test to confirm that the module was installed in the right location. 114 /// </summary> 115 /// <param name="location">Location to pass.</param> 116 [TestCase(TestCommon.TestModuleLocation.CurrentUser)] 117 [TestCase(TestCommon.TestModuleLocation.AllUsers)] 118 [TestCase(TestCommon.TestModuleLocation.WinGetModulePath)] 119 [TestCase(TestCommon.TestModuleLocation.Custom)] 120 [TestCase(TestCommon.TestModuleLocation.Default)] 121 public void ConfigureFromTestRepo_Location(TestCommon.TestModuleLocation location) 122 { 123 TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); 124 125 string args = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo_Location.yml"); 126 if (location == TestCommon.TestModuleLocation.CurrentUser) 127 { 128 args += " --module-path currentuser"; 129 } 130 else if (location == TestCommon.TestModuleLocation.AllUsers) 131 { 132 args += " --module-path allusers"; 133 } 134 else if (location == TestCommon.TestModuleLocation.Default) 135 { 136 args += " --module-path default"; 137 } 138 else if (location == TestCommon.TestModuleLocation.Custom) 139 { 140 args += " --module-path " + TestCommon.GetExpectedModulePath(location); 141 } 142 143 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args); 144 Assert.AreEqual(0, result.ExitCode); 145 146 Assert.True(Directory.Exists(Path.Combine( 147 TestCommon.GetExpectedModulePath(location), 148 Constants.SimpleTestModuleName))); 149 } 150 151 /// <summary> 152 /// One resource fails, but the other is not dependent and should be executed. 153 /// </summary> 154 [Test] 155 public void IndependentResourceWithSingleFailure() 156 { 157 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\IndependentResources_OneFailure.yml")); 158 Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); 159 160 // The configuration creates a file next to itself with the given contents 161 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\IndependentResources_OneFailure.txt"); 162 FileAssert.Exists(targetFilePath); 163 Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); 164 } 165 166 /// <summary> 167 /// One resource fails, and the dependent resource should not be executed. 168 /// </summary> 169 [Test] 170 public void DependentResourceWithFailure() 171 { 172 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\DependentResources_Failure.yml")); 173 Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); 174 175 // The configuration creates a file next to itself with the given contents 176 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\DependentResources_Failure.txt"); 177 FileAssert.DoesNotExist(targetFilePath); 178 } 179 180 /// <summary> 181 /// The configuration server unexpectedly exits. Winget should continue to operate properly. 182 /// </summary> 183 [Test] 184 public void ConfigServerUnexpectedExit() 185 { 186 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\ConfigServerUnexpectedExit.yml")); 187 Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); 188 189 // The configuration creates a file next to itself with the given contents 190 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\ConfigServerUnexpectedExit.txt"); 191 FileAssert.DoesNotExist(targetFilePath); 192 } 193 194 /// <summary> 195 /// Resource name case-insensitive test. 196 /// </summary> 197 [Test] 198 public void ResourceCaseInsensitive() 199 { 200 TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); 201 202 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\ResourceCaseInsensitive.yml")); 203 Assert.AreEqual(0, result.ExitCode); 204 205 // The configuration creates a file next to itself with the given contents 206 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\ResourceCaseInsensitive.txt"); 207 FileAssert.Exists(targetFilePath); 208 Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); 209 } 210 211 /// <summary> 212 /// Simple test to configure from an https configuration file. 213 /// </summary> 214 [Test] 215 public void ConfigureFromHttpsConfigurationFile() 216 { 217 string args = $"{Constants.TestSourceUrl}/TestData/Configuration/Configure_TestRepo_Location.yml"; 218 219 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args); 220 Assert.AreEqual(0, result.ExitCode); 221 } 222 223 /// <summary> 224 /// Runs a configuration, then changes the state and runs it again from history. 225 /// </summary> 226 [Test] 227 public void ConfigureFromHistory() 228 { 229 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); 230 Assert.AreEqual(0, result.ExitCode); 231 232 // The configuration creates a file next to itself with the given contents 233 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"); 234 FileAssert.Exists(targetFilePath); 235 Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); 236 237 File.WriteAllText(targetFilePath, "Changed contents!"); 238 239 string guid = TestCommon.GetConfigurationInstanceIdentifierFor("Configure_TestRepo.yml"); 240 result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, $"-h {guid}"); 241 Assert.AreEqual(0, result.ExitCode); 242 243 FileAssert.Exists(targetFilePath); 244 Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); 245 } 246 247 /// <summary> 248 /// Specifies the module path to an "elevated" server. 249 /// </summary> 250 [Test] 251 public void SpecifyModulePathToHighIntegrityServer() 252 { 253 string configFile = TestCommon.GetTestDataFile("Configuration\\GetPSModulePath.yml"); 254 string testDirectory = TestCommon.GetRandomTestDir(); 255 256 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, $"{configFile} --module-path \"{testDirectory}\""); 257 Assert.AreEqual(0, result.ExitCode); 258 259 string testFile = Path.Join(TestCommon.GetTestDataFile("Configuration"), "PSModulePath.txt"); 260 Assert.True(File.Exists(testFile)); 261 string testFileContents = File.ReadAllText(testFile); 262 Assert.True(testFileContents.StartsWith(testDirectory)); 263 } 264 265 /// <summary> 266 /// Runs a DSCv3 configuration, then changes the state and runs it again from history. 267 /// </summary> 268 [Test] 269 public void ConfigureThroughHistory_DSCv3() 270 { 271 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\ShowDetails_DSCv3.yml")); 272 Assert.AreEqual(0, result.ExitCode); 273 274 // The configuration creates a file next to itself with the given contents 275 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\ShowDetails_DSCv3.txt"); 276 FileAssert.Exists(targetFilePath); 277 Assert.AreEqual("DSCv3 Contents!", File.ReadAllText(targetFilePath)); 278 279 File.WriteAllText(targetFilePath, "Changed contents!"); 280 281 string guid = TestCommon.GetConfigurationInstanceIdentifierFor("ShowDetails_DSCv3.yml"); 282 result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, $"-h {guid}"); 283 Assert.AreEqual(0, result.ExitCode); 284 285 FileAssert.Exists(targetFilePath); 286 Assert.AreEqual("DSCv3 Contents!", File.ReadAllText(targetFilePath)); 287 } 288 289 /// <summary> 290 /// Ensures that the test file resource schema function works. 291 /// </summary> 292 [Test] 293 public void TestFileResourceSchema() 294 { 295 var result = TestCommon.RunAICLICommand("dscv3 test-file", "--schema"); 296 Assert.AreEqual(0, result.ExitCode); 297 298 var lines = result.StdOut.Split("\r\n", StringSplitOptions.RemoveEmptyEntries); 299 Assert.AreEqual(1, lines.Length); 300 } 301 302 /// <summary> 303 /// Export all with specific package id. 304 /// </summary> 305 [Test] 306 public void DSCv3_Export() 307 { 308 // Reset state 309 var result = TestCommon.RunAICLICommand("dscv3 test-json", "--delete"); 310 Assert.AreEqual(0, result.ExitCode); 311 312 // Configure properties 313 string propertyName1 = "prop1"; 314 string propertyName2 = "prop2"; 315 string propertyValue1 = "val1"; 316 string propertyValue2 = "val2"; 317 318 string propertySetFormatString = "{{ \"property\": \"{0}\", \"value\": \"{1}\" }}"; 319 320 result = TestCommon.RunAICLICommand("dscv3 test-json", "--set", string.Format(propertySetFormatString, propertyName1, propertyValue1)); 321 Assert.AreEqual(0, result.ExitCode); 322 323 result = TestCommon.RunAICLICommand("dscv3 test-json", "--set", string.Format(propertySetFormatString, propertyName2, propertyValue2)); 324 Assert.AreEqual(0, result.ExitCode); 325 326 // Export 327 var exportDir = TestCommon.GetRandomTestDir(); 328 var exportFile = Path.Combine(exportDir, "exported.yml"); 329 330 result = TestCommon.RunAICLICommand("test config-export-units", $"-o {exportFile} --resource Microsoft.WinGet.Dev/TestJSON --verbose"); 331 Assert.AreEqual(0, result.ExitCode); 332 333 Assert.True(File.Exists(exportFile)); 334 string exportText = File.ReadAllText(exportFile); 335 Assert.True(exportText.Contains("Microsoft.WinGet.Dev/TestJSON")); 336 Assert.True(exportText.Contains(propertyName1)); 337 Assert.True(exportText.Contains(propertyName2)); 338 Assert.True(exportText.Contains(propertyValue1)); 339 Assert.True(exportText.Contains(propertyValue2)); 340 } 341 342 /// <summary> 343 /// Simple test to confirm that a resource with a module specified can be discovered in a local repository that doesn't support resource discovery. 344 /// </summary> 345 [Test] 346 public void ConfigureFromTestRepo_DSCv3() 347 { 348 TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: true, repository: Constants.TestRepoName); 349 this.DeleteResourceArtifacts(); 350 351 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo_DSCv3.yml"), timeOut: 300000); 352 Assert.AreEqual(0, result.ExitCode); 353 354 // The configuration creates a file next to itself with the given contents 355 string targetFilePath = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"); 356 FileAssert.Exists(targetFilePath); 357 Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); 358 } 359 360 /// <summary> 361 /// Find unit processors tests. 362 /// </summary> 363 [Test] 364 public void ConfigureFindUnitProcessors() 365 { 366 // Find all unit processors. 367 var result = TestCommon.RunAICLICommand("test config-find-unit-processors", string.Empty); 368 Assert.AreEqual(0, result.ExitCode); 369 Assert.True(result.StdOut.Contains("Microsoft/OSInfo")); 370 371 // Setup TestExeInstaller with dsc resources. 372 var installDir = TestCommon.GetRandomTestDir(); 373 result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --override \"/InstallDir {installDir} /GenerateDscResourceFiles\""); 374 Assert.AreEqual(0, result.ExitCode); 375 376 // Find unit processors filtering to install location. 377 result = TestCommon.RunAICLICommand("test config-find-unit-processors", $"-l {installDir}"); 378 Assert.AreEqual(0, result.ExitCode); 379 Assert.False(result.StdOut.Contains("Microsoft/OSInfo")); 380 Assert.True(result.StdOut.Contains("AppInstallerTest/TestResource")); 381 382 // Find unit processors filtering to unknown location. 383 var unknownDir = TestCommon.GetRandomTestDir(); 384 result = TestCommon.RunAICLICommand("test config-find-unit-processors", $"-l {unknownDir}"); 385 Assert.AreEqual(0, result.ExitCode); 386 Assert.True(result.StdOut.Contains("No unit processors found.")); 387 388 // Clean up 389 result = TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestExeInstaller"); 390 Assert.AreEqual(0, result.ExitCode); 391 } 392 393 /// <summary> 394 /// RunCommandOnSet test. 395 /// </summary> 396 [Test] 397 public void RunCommandOnSetResourceTest() 398 { 399 var testDir = TestCommon.GetRandomTestDir(); 400 var testConfigFile = Path.Combine(testDir, "RunCommandOnSet.yml"); 401 File.Copy(TestCommon.GetTestDataFile("Configuration\\RunCommandOnSet.yml"), testConfigFile); 402 403 var content = File.ReadAllText(testConfigFile); 404 content = content.Replace("<PathToBeReplaced>", testDir); 405 File.WriteAllText(testConfigFile, content); 406 407 var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, testConfigFile, timeOut: 300000); 408 Assert.AreEqual(0, result.ExitCode); 409 410 // Verify test file created. 411 string targetFilePath = Path.Combine(testDir, "TestFile.txt"); 412 FileAssert.Exists(targetFilePath); 413 string testContent = File.ReadAllText(targetFilePath); 414 Assert.True(testContent.Contains("TestContent")); 415 } 416 417 private void DeleteResourceArtifacts() 418 { 419 // Delete all .txt files in the test directory; they are placed there by the tests 420 foreach (string file in Directory.GetFiles(TestCommon.GetTestDataFile("Configuration"), "*.txt")) 421 { 422 File.Delete(file); 423 } 424 } 425 } 426 }