winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }