MSStoreDownloadFlow.cpp (35892B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "TestHooks.h" 5 #include "TestRestRequestHandler.h" 6 #include "WorkflowCommon.h" 7 #include <AppInstallerStrings.h> 8 #include <AppInstallerSHA256.h> 9 #include <winget/JsonUtil.h> 10 #include <Commands/DownloadCommand.h> 11 12 using namespace TestCommon; 13 using namespace AppInstaller::CLI; 14 using namespace AppInstaller::CLI::Execution; 15 using namespace AppInstaller::Settings; 16 using namespace AppInstaller::Utility::literals; 17 18 utility::string_t TestDisplayCatalogResponse = _XPLATSTR( 19 R"delimiter( 20 { 21 "Product": { 22 "DisplaySkuAvailabilities": [ 23 { 24 "Sku": { 25 "SkuId": "0015", 26 "Properties": { 27 "Packages": [ 28 { 29 "PackageId": "PackageEnglish", 30 "Architectures": [ "x64", "arm" ], 31 "Languages": [ "en-US", "en-GB" ], 32 "PackageFormat": "Appx", 33 "ContentId": "LicenseContentId", 34 "FulfillmentData": { 35 "WuCategoryId": "TestCategoryIdEnglish" 36 } 37 }, 38 { 39 "PackageId": "PackageFrench", 40 "Architectures": [ "x64", "arm" ], 41 "Languages": [ "fr-FR" ], 42 "PackageFormat": "Appx", 43 "ContentId": "LicenseContentId", 44 "FulfillmentData": { 45 "WuCategoryId": "TestCategoryIdFrench" 46 } 47 } 48 ] 49 } 50 } 51 } 52 ] 53 } 54 })delimiter"); 55 56 utility::string_t TestLicensingResponseRaw = _XPLATSTR( 57 R"delimiter( 58 { 59 "license": { 60 "keys": [ 61 { 62 "value": "<LicenseContent>" 63 } 64 ] 65 } 66 })delimiter"); 67 68 std::string LicenseContent = "TestLicense"; 69 70 utility::string_t TestLicensingResponse = AppInstaller::Utility::ReplaceWhileCopying( 71 TestLicensingResponseRaw, L"<LicenseContent>", 72 AppInstaller::Utility::ConvertToUTF16(AppInstaller::JSON::Base64Encode(std::vector<BYTE>{ LicenseContent.begin(), LicenseContent.end() }))); 73 74 utility::string_t TestDisplayCatalogResponse_TargetSkuNotFound = _XPLATSTR( 75 R"delimiter( 76 { 77 "Product": { 78 "DisplaySkuAvailabilities": [ 79 { 80 "Sku": { 81 "SkuId": "0011", 82 "Properties": { 83 "Packages": [ 84 { 85 "PackageId": "PackageEnglish", 86 "Architectures": [ "x64", "arm" ], 87 "Languages": [ "en-US", "en-GB" ], 88 "PackageFormat": "Appx", 89 "ContentId": "LicenseContentId", 90 "FulfillmentData": { 91 "WuCategoryId": "TestCategoryIdEnglish" 92 } 93 } 94 ] 95 } 96 } 97 } 98 ] 99 } 100 })delimiter"); 101 102 std::vector<SFS::AppContent> GetSfsAppContentsOverrideFunction(std::string_view wuCategoryId) 103 { 104 std::string wuCategoryIdStr{ wuCategoryId }; 105 106 std::vector<SFS::AppContent> result; 107 108 std::unique_ptr<SFS::ContentId> contentId; 109 std::vector<SFS::AppPrerequisiteContent> dependencies; 110 std::vector<SFS::AppFile> packages; 111 std::unique_ptr<SFS::AppContent> appContent; 112 113 std::vector<BYTE> sha256Bytes = AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798223CD4FEBAB1D734A07C2E51E56A28E0DF8123"); 114 std::string base64EncodedSha256 = AppInstaller::JSON::Base64Encode(sha256Bytes); 115 116 { 117 // Create dependencies content 118 std::unique_ptr<SFS::ContentId> dependencyContentId; 119 std::vector<SFS::AppFile> dependencyPackages; 120 std::unique_ptr<SFS::AppPrerequisiteContent> dependencyContent; 121 122 std::ignore = SFS::ContentId::Make("testDependency", "testDependency", "1.0.0.0", dependencyContentId); 123 124 std::unique_ptr<SFS::AppFile> dependencyX64; 125 std::ignore = SFS::AppFile::Make( 126 wuCategoryIdStr + ".appx", 127 "https://NotUsed/" + wuCategoryIdStr + "/dependency/x64", 128 100, 129 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 130 { SFS::Architecture::Amd64 }, 131 { "Universal=10.0.0.0" }, 132 wuCategoryIdStr + ".Dependency_1.2.3.4_x64__8wekyb3d8bbwe", 133 dependencyX64); 134 dependencyPackages.emplace_back(std::move(*dependencyX64)); 135 136 // Lower target OS dependency 137 std::unique_ptr<SFS::AppFile> dependencyX64_lower; 138 std::ignore = SFS::AppFile::Make( 139 wuCategoryIdStr + ".appx", 140 "https://NotUsed/" + wuCategoryIdStr + "/dependency/x64", 141 100, 142 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 143 { SFS::Architecture::Amd64 }, 144 { "Universal=9.0.0.0" }, 145 wuCategoryIdStr + ".Dependency_0.9.3.4_x64__8wekyb3d8bbwe", 146 dependencyX64_lower); 147 dependencyPackages.emplace_back(std::move(*dependencyX64_lower)); 148 149 std::unique_ptr<SFS::AppFile> dependencyArm; 150 std::ignore = SFS::AppFile::Make( 151 wuCategoryIdStr + ".appx", 152 "https://NotUsed/" + wuCategoryIdStr + "/dependency/arm", 153 100, 154 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 155 { SFS::Architecture::Arm }, 156 { "Universal=10.0.0.0" }, 157 wuCategoryIdStr + ".Dependency_1.2.3.4_arm__8wekyb3d8bbwe", 158 dependencyArm); 159 dependencyPackages.emplace_back(std::move(*dependencyArm)); 160 161 std::ignore = SFS::AppPrerequisiteContent::Make(std::move(dependencyContentId), std::move(dependencyPackages), dependencyContent); 162 163 dependencies.emplace_back(std::move(*dependencyContent)); 164 } 165 166 { 167 // Create main packages 168 169 // Good candidate x64 170 std::unique_ptr<SFS::AppFile> packageX64; 171 std::ignore = SFS::AppFile::Make( 172 wuCategoryIdStr + ".appx", 173 "https://NotUsed/" + wuCategoryIdStr + "/x64", 174 100, 175 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 176 { SFS::Architecture::Amd64 }, 177 { "Desktop=10.0.0.0" }, 178 wuCategoryIdStr + "_1.0.0.0_x64__8wekyb3d8bbwe", 179 packageX64); 180 packages.emplace_back(std::move(*packageX64)); 181 182 // Good candidate x64, lower minimum OS version, lower package version 183 std::unique_ptr<SFS::AppFile> packageX64_lower; 184 std::ignore = SFS::AppFile::Make( 185 wuCategoryIdStr + ".appx", 186 "https://NotUsed/" + wuCategoryIdStr + "/x64", 187 100, 188 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 189 { SFS::Architecture::Amd64 }, 190 { "Desktop=9.0.0.0" }, 191 wuCategoryIdStr + "_0.9.0.0_x64__8wekyb3d8bbwe", 192 packageX64_lower); 193 packages.emplace_back(std::move(*packageX64_lower)); 194 195 // Good candidate arm 196 std::unique_ptr<SFS::AppFile> packageArm; 197 std::ignore = SFS::AppFile::Make( 198 wuCategoryIdStr + ".appx", 199 "https://NotUsed/" + wuCategoryIdStr + "/arm", 200 100, 201 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 202 { SFS::Architecture::Arm }, 203 { "Desktop=10.0.0.0" }, 204 wuCategoryIdStr + "_1.0.0.0_arm__8wekyb3d8bbwe", 205 packageArm); 206 packages.emplace_back(std::move(*packageArm)); 207 208 // Good candidate IoT 209 std::unique_ptr<SFS::AppFile> packageIoT; 210 std::ignore = SFS::AppFile::Make( 211 wuCategoryIdStr + ".appx", 212 "https://NotUsed/" + wuCategoryIdStr + "/IoT/arm", 213 100, 214 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 215 { SFS::Architecture::Arm }, 216 { "IoT=10.0.0.0" }, 217 wuCategoryIdStr + ".IoT_1.0.0.0_arm__8wekyb3d8bbwe", 218 packageIoT); 219 packages.emplace_back(std::move(*packageIoT)); 220 221 // Good candidate IoT has newer version 222 std::unique_ptr<SFS::AppFile> packageIoT2; 223 std::ignore = SFS::AppFile::Make( 224 wuCategoryIdStr + ".appx", 225 "https://NotUsed/" + wuCategoryIdStr + "/IoT/arm/2.0", 226 100, 227 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 228 { SFS::Architecture::Arm }, 229 { "IoT=10.0.0.0" }, 230 wuCategoryIdStr + ".IoT_2.0.0.0_arm__8wekyb3d8bbwe", 231 packageIoT2); 232 packages.emplace_back(std::move(*packageIoT2)); 233 234 // Candidate unsupported platform 235 std::unique_ptr<SFS::AppFile> packageXbox; 236 std::ignore = SFS::AppFile::Make( 237 wuCategoryIdStr + ".appx", 238 "https://NotUsed/" + wuCategoryIdStr + "/Xbox/arm", 239 100, 240 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 241 { SFS::Architecture::Arm }, 242 { "Xbox=10.0.0.0" }, 243 wuCategoryIdStr + ".Xbox_1.0.0.0_arm__8wekyb3d8bbwe", 244 packageXbox); 245 packages.emplace_back(std::move(*packageXbox)); 246 247 // Candidate unsupported filetype 248 std::unique_ptr<SFS::AppFile> packageData; 249 std::ignore = SFS::AppFile::Make( 250 wuCategoryIdStr + ".cab", 251 "https://NotUsed/" + wuCategoryIdStr + "/cab", 252 100, 253 { { SFS::HashType::Sha256, base64EncodedSha256 } }, 254 { SFS::Architecture::Arm }, 255 { "Desktop=10.0.0.0" }, 256 wuCategoryIdStr + ".Data_1.0.0.0_arm__8wekyb3d8bbwe", 257 packageData); 258 packages.emplace_back(std::move(*packageData)); 259 } 260 261 std::ignore = SFS::ContentId::Make("test", "test", "1.0.0.0", contentId); 262 std::ignore = SFS::AppContent::Make(std::move(contentId), "updateId", std::move(dependencies), std::move(packages), appContent); 263 264 result.emplace_back(std::move(*appContent)); 265 266 return result; 267 } 268 269 TEST_CASE("MSStoreDownloadFlow_Success", "[MSStoreDownloadFlow][workflow]") 270 { 271 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 272 273 std::ostringstream downloadOutput; 274 TestContext context{ downloadOutput, std::cin }; 275 auto previousThreadGlobals = context.SetForCurrentThread(); 276 OverrideDownloadInstallerFileForMSStoreDownload(context); 277 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 278 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 279 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 280 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 281 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 282 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 283 284 DownloadCommand download({}); 285 download.Execute(context); 286 REQUIRE(context.GetTerminationHR() == S_OK); 287 INFO(downloadOutput.str()); 288 289 // Verify downloaded files 290 REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); 291 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); 292 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); 293 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); 294 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); 295 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); 296 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); 297 298 // Verify license 299 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); 300 std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); 301 REQUIRE(licenseFile.is_open()); 302 std::string licenseFileStr; 303 std::getline(licenseFile, licenseFileStr); 304 REQUIRE(licenseFileStr == LicenseContent); 305 306 // Verify unsupported packages filtered out 307 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_1.0.0.0_IoT_Arm.appx")); 308 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Xbox_1.0.0.0_Xbox_Arm.appx")); 309 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Data_1.0.0.0_Desktop_Arm.cab")); 310 } 311 312 TEST_CASE("MSStoreDownloadFlow_Success_SkipDependencies", "[MSStoreDownloadFlow][workflow]") 313 { 314 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 315 316 std::ostringstream downloadOutput; 317 TestContext context{ downloadOutput, std::cin }; 318 auto previousThreadGlobals = context.SetForCurrentThread(); 319 OverrideDownloadInstallerFileForMSStoreDownload(context); 320 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 321 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 322 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 323 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 324 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 325 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 326 context.Args.AddArg(Execution::Args::Type::SkipDependencies); 327 328 DownloadCommand download({}); 329 download.Execute(context); 330 REQUIRE(context.GetTerminationHR() == S_OK); 331 INFO(downloadOutput.str()); 332 333 // Verify downloaded files 334 REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); 335 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); 336 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); 337 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); 338 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); 339 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); 340 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); 341 342 // Verify license 343 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); 344 std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); 345 REQUIRE(licenseFile.is_open()); 346 std::string licenseFileStr; 347 std::getline(licenseFile, licenseFileStr); 348 REQUIRE(licenseFileStr == LicenseContent); 349 } 350 351 TEST_CASE("MSStoreDownloadFlow_Success_SkipLicense", "[MSStoreDownloadFlow][workflow]") 352 { 353 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 354 355 std::ostringstream downloadOutput; 356 TestContext context{ downloadOutput, std::cin }; 357 auto previousThreadGlobals = context.SetForCurrentThread(); 358 OverrideDownloadInstallerFileForMSStoreDownload(context); 359 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 360 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 361 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 362 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 363 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 364 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 365 context.Args.AddArg(Execution::Args::Type::SkipMicrosoftStorePackageLicense); 366 367 DownloadCommand download({}); 368 download.Execute(context); 369 REQUIRE(context.GetTerminationHR() == S_OK); 370 INFO(downloadOutput.str()); 371 372 // Verify downloaded files 373 REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); 374 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); 375 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); 376 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); 377 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); 378 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); 379 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); 380 381 // Verify license 382 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); 383 } 384 385 TEST_CASE("MSStoreDownloadFlow_Success_SpecificLocale", "[MSStoreDownloadFlow][workflow]") 386 { 387 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 388 389 std::ostringstream downloadOutput; 390 TestContext context{ downloadOutput, std::cin }; 391 auto previousThreadGlobals = context.SetForCurrentThread(); 392 OverrideDownloadInstallerFileForMSStoreDownload(context); 393 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 394 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 395 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 396 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 397 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 398 context.Args.AddArg(Execution::Args::Type::Locale, "fr-FR"sv); 399 400 DownloadCommand download({}); 401 download.Execute(context); 402 REQUIRE(context.GetTerminationHR() == S_OK); 403 INFO(downloadOutput.str()); 404 405 // Verify downloaded files 406 REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); 407 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); 408 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_Universal_X64.appx")); 409 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_Universal_Arm.appx")); 410 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_Desktop_X64.appx")); 411 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_Desktop_Arm.appx")); 412 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench.IoT_2.0.0.0_IoT_Arm.appx")); 413 414 // Verify license 415 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); 416 std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); 417 REQUIRE(licenseFile.is_open()); 418 std::string licenseFileStr; 419 std::getline(licenseFile, licenseFileStr); 420 REQUIRE(licenseFileStr == LicenseContent); 421 } 422 423 TEST_CASE("MSStoreDownloadFlow_Success_SpecificArchitecture", "[MSStoreDownloadFlow][workflow]") 424 { 425 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 426 427 std::ostringstream downloadOutput; 428 TestContext context{ downloadOutput, std::cin }; 429 auto previousThreadGlobals = context.SetForCurrentThread(); 430 OverrideDownloadInstallerFileForMSStoreDownload(context); 431 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 432 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 433 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 434 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 435 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 436 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 437 context.Args.AddArg(Execution::Args::Type::InstallerArchitecture, "x64"sv); 438 439 DownloadCommand download({}); 440 download.Execute(context); 441 REQUIRE(context.GetTerminationHR() == S_OK); 442 INFO(downloadOutput.str()); 443 444 // Verify downloaded files 445 REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); 446 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); 447 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_x64.appx")); 448 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); 449 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); 450 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); 451 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); 452 453 // Verify license 454 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); 455 std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); 456 REQUIRE(licenseFile.is_open()); 457 std::string licenseFileStr; 458 std::getline(licenseFile, licenseFileStr); 459 REQUIRE(licenseFileStr == LicenseContent); 460 } 461 462 TEST_CASE("MSStoreDownloadFlow_Success_SpecificPlatform", "[MSStoreDownloadFlow][workflow]") 463 { 464 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 465 466 std::ostringstream downloadOutput; 467 TestContext context{ downloadOutput, std::cin }; 468 auto previousThreadGlobals = context.SetForCurrentThread(); 469 OverrideDownloadInstallerFileForMSStoreDownload(context); 470 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 471 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 472 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 473 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 474 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 475 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 476 context.Args.AddArg(Execution::Args::Type::Platform, "Windows.IoT"sv); 477 478 DownloadCommand download({}); 479 download.Execute(context); 480 REQUIRE(context.GetTerminationHR() == S_OK); 481 INFO(downloadOutput.str()); 482 483 // Verify downloaded files 484 REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); 485 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); 486 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); 487 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); 488 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); 489 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); 490 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); 491 492 // Verify license 493 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); 494 std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); 495 REQUIRE(licenseFile.is_open()); 496 std::string licenseFileStr; 497 std::getline(licenseFile, licenseFileStr); 498 REQUIRE(licenseFileStr == LicenseContent); 499 } 500 501 TEST_CASE("MSStoreDownloadFlow_Fail_TargetSkuNotFound", "[MSStoreDownloadFlow][workflow]") 502 { 503 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 504 505 std::ostringstream downloadOutput; 506 TestContext context{ downloadOutput, std::cin }; 507 auto previousThreadGlobals = context.SetForCurrentThread(); 508 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse_TargetSkuNotFound)); 509 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 510 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 511 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 512 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 513 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 514 515 DownloadCommand download({}); 516 download.Execute(context); 517 REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); 518 INFO(downloadOutput.str()); 519 } 520 521 TEST_CASE("MSStoreDownloadFlow_Fail_LocaleNotApplicable", "[MSStoreDownloadFlow][workflow]") 522 { 523 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 524 525 std::ostringstream downloadOutput; 526 TestContext context{ downloadOutput, std::cin }; 527 auto previousThreadGlobals = context.SetForCurrentThread(); 528 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 529 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 530 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 531 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 532 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 533 context.Args.AddArg(Execution::Args::Type::Locale, "ja-JP"sv); 534 535 DownloadCommand download({}); 536 download.Execute(context); 537 REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); 538 INFO(downloadOutput.str()); 539 } 540 541 TEST_CASE("MSStoreDownloadFlow_Fail_ArchitectureNotApplicable", "[MSStoreDownloadFlow][workflow]") 542 { 543 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 544 545 std::ostringstream downloadOutput; 546 TestContext context{ downloadOutput, std::cin }; 547 auto previousThreadGlobals = context.SetForCurrentThread(); 548 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 549 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 550 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 551 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 552 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 553 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 554 context.Args.AddArg(Execution::Args::Type::InstallerArchitecture, "arm64"sv); 555 556 DownloadCommand download({}); 557 download.Execute(context); 558 REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); 559 INFO(downloadOutput.str()); 560 } 561 562 TEST_CASE("MSStoreDownloadFlow_Fail_PlatformNotApplicable", "[MSStoreDownloadFlow][workflow]") 563 { 564 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 565 566 std::ostringstream downloadOutput; 567 TestContext context{ downloadOutput, std::cin }; 568 auto previousThreadGlobals = context.SetForCurrentThread(); 569 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 570 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 571 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 572 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 573 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 574 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 575 context.Args.AddArg(Execution::Args::Type::Platform, "Windows.Holographic"sv); 576 577 DownloadCommand download({}); 578 download.Execute(context); 579 REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE); 580 INFO(downloadOutput.str()); 581 } 582 583 TEST_CASE("MSStoreDownloadFlow_Fail_Licensing", "[MSStoreDownloadFlow][workflow]") 584 { 585 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 586 587 std::ostringstream downloadOutput; 588 TestContext context{ downloadOutput, std::cin }; 589 auto previousThreadGlobals = context.SetForCurrentThread(); 590 OverrideDownloadInstallerFileForMSStoreDownload(context); 591 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 592 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 593 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::InternalError)); 594 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 595 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 596 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 597 598 DownloadCommand download({}); 599 download.Execute(context); 600 REQUIRE_TERMINATED_WITH(context, MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, web::http::status_codes::InternalError)); 601 INFO(downloadOutput.str()); 602 } 603 604 TEST_CASE("MSStoreDownloadFlow_Fail_Licensing_Forbidden", "[MSStoreDownloadFlow][workflow]") 605 { 606 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 607 608 std::ostringstream downloadOutput; 609 TestContext context{ downloadOutput, std::cin }; 610 auto previousThreadGlobals = context.SetForCurrentThread(); 611 OverrideDownloadInstallerFileForMSStoreDownload(context); 612 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 613 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 614 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::Forbidden)); 615 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 616 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 617 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 618 619 DownloadCommand download({}); 620 download.Execute(context); 621 REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN); 622 INFO(downloadOutput.str()); 623 } 624 625 TEST_CASE("MSStoreDownloadFlow_Success_TargetOSVersion", "[MSStoreDownloadFlow][workflow]") 626 { 627 TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); 628 629 std::ostringstream downloadOutput; 630 TestContext context{ downloadOutput, std::cin }; 631 auto previousThreadGlobals = context.SetForCurrentThread(); 632 OverrideDownloadInstallerFileForMSStoreDownload(context); 633 TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); 634 TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); 635 TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); 636 context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); 637 context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); 638 context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); 639 context.Args.AddArg(Execution::Args::Type::Platform, "Windows.Desktop"sv); 640 context.Args.AddArg(Execution::Args::Type::OSVersion, "9.0.0.0"sv); 641 642 DownloadCommand download({}); 643 download.Execute(context); 644 REQUIRE(context.GetTerminationHR() == S_OK); 645 INFO(downloadOutput.str()); 646 647 // Verify downloaded files 648 REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); 649 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); 650 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_0.9.3.4_Universal_X64.appx")); 651 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); 652 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_0.9.0.0_Desktop_X64.appx")); 653 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); 654 REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); 655 656 // Verify license 657 REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); 658 std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); 659 REQUIRE(licenseFile.is_open()); 660 std::string licenseFileStr; 661 std::getline(licenseFile, licenseFileStr); 662 REQUIRE(licenseFileStr == LicenseContent); 663 }