winget-cli

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

RestInterface_1_0.cpp (31398B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "TestCommon.h"
      5 #include "TestRestRequestHandler.h"
      6 #include <Rest/Schema/1_0/Interface.h>
      7 #include <Rest/Schema/IRestClient.h>
      8 #include <AppInstallerVersions.h>
      9 #include <AppInstallerErrors.h>
     10 #include <winget/ManifestValidation.h>
     11 #include <AppInstallerSHA256.h>
     12 
     13 using namespace TestCommon;
     14 using namespace AppInstaller::Http;
     15 using namespace AppInstaller::Utility;
     16 using namespace AppInstaller::Manifest;
     17 using namespace AppInstaller::Repository;
     18 using namespace AppInstaller::Repository::Rest;
     19 using namespace AppInstaller::Repository::Rest::Schema;
     20 using namespace AppInstaller::Repository::Rest::Schema::V1_0;
     21 
     22 namespace
     23 {
     24     const std::string TestRestUriString = "http://restsource.com/api";
     25 
     26     utility::string_t GetGoodManifest_RequiredFields()
     27     {
     28         return _XPLATSTR(
     29             R"delimiter({
     30         "Data": {
     31             "PackageIdentifier": "Foo.Bar",
     32             "Versions": [
     33                 {
     34                     "PackageVersion": "5.0.0",
     35                     "DefaultLocale": {
     36                         "PackageLocale": "en-us",
     37                         "Publisher": "Foo",
     38                         "PackageName": "Bar",
     39                         "License": "Foo bar license",
     40                         "ShortDescription": "Foo bar description"
     41                     },
     42                     "Installers": [
     43                         {
     44                             "Architecture": "x64",
     45                             "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6",
     46                             "InstallerType": "exe",
     47                             "InstallerUrl": "https://installer.example.com/foobar.exe"
     48                         }
     49                     ]
     50                 }
     51             ]
     52         }
     53     })delimiter");
     54     }
     55 
     56     utility::string_t GetManifestsResponse_MultipleVersions()
     57     {
     58         return _XPLATSTR(
     59             R"delimiter({
     60         "Data": {
     61             "PackageIdentifier": "Foo.Bar",
     62             "Versions": [
     63                 {
     64                     "PackageVersion": "5.0.0",
     65                     "DefaultLocale": {
     66                         "PackageLocale": "en-us",
     67                         "Publisher": "Foo",
     68                         "PackageName": "Bar",
     69                         "License": "Foo bar license",
     70                         "ShortDescription": "Foo bar description"
     71                     },
     72                     "Installers": [
     73                         {
     74                             "Architecture": "x64",
     75                             "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6",
     76                             "InstallerType": "exe",
     77                             "InstallerUrl": "https://installer.example.com/foobar.exe"
     78                         }
     79                     ]
     80                 },
     81                 {
     82                     "PackageVersion": "6.0.0",
     83                     "DefaultLocale": {
     84                         "PackageLocale": "en-us",
     85                         "Publisher": "Foo",
     86                         "PackageName": "Bar",
     87                         "License": "Foo bar license",
     88                         "ShortDescription": "Foo bar description"
     89                     },
     90                     "Installers": [
     91                         {
     92                             "Architecture": "x64",
     93                             "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6",
     94                             "InstallerType": "exe",
     95                             "InstallerUrl": "https://installer.example.com/foobar.exe"
     96                         }
     97                     ]
     98                 }
     99             ]
    100         }
    101     })delimiter");
    102     }
    103 
    104     struct GoodManifest_AllFields
    105     {
    106         utility::string_t GetSampleManifest_AllFields()
    107         {
    108             return _XPLATSTR(
    109                 R"delimiter(
    110         {
    111           "Data": {
    112             "PackageIdentifier": "Foo.Bar",
    113             "Versions": [
    114               {
    115                 "PackageVersion": "3.0.0abc",
    116                 "DefaultLocale": {
    117                   "PackageLocale": "en-US",
    118                   "Publisher": "Foo",
    119                   "PublisherUrl": "http://publisher.net",
    120                   "PublisherSupportUrl": "http://publisherSupport.net",
    121                   "PrivacyUrl": "http://packagePrivacyUrl.net",
    122                   "Author": "FooBar",
    123                   "PackageName": "Bar",
    124                   "PackageUrl": "http://packageUrl.net",
    125                   "License": "Foo Bar License",
    126                   "LicenseUrl": "http://licenseUrl.net",
    127                   "Copyright": "Foo Bar Copyright",
    128                   "CopyrightUrl": "http://copyrightUrl.net",
    129                   "ShortDescription": "Foo bar is a foo bar.",
    130                   "Description": "Foo bar is a placeholder.",
    131                   "Tags": [
    132                     "FooBar",
    133                     "Foo",
    134                     "Bar"
    135                   ],
    136                   "Moniker": "FooBarMoniker"
    137                 },
    138                 "Channel": "",
    139                 "Locales": [
    140                   {
    141                     "PackageLocale": "fr-Fr",
    142                     "Publisher": "Foo French",
    143                     "PublisherUrl": "http://publisher-fr.net",
    144                     "PublisherSupportUrl": "http://publisherSupport-fr.net",
    145                     "PrivacyUrl": "http://packagePrivacyUrl-fr.net",
    146                     "Author": "FooBar French",
    147                     "PackageName": "Bar",
    148                     "PackageUrl": "http://packageUrl-fr.net",
    149                     "License": "Foo Bar License",
    150                     "LicenseUrl": "http://licenseUrl-fr.net",
    151                     "Copyright": "Foo Bar Copyright",
    152                     "CopyrightUrl": "http://copyrightUrl-fr.net",
    153                     "ShortDescription": "Foo bar is a foo bar French.",
    154                     "Description": "Foo bar is a placeholder French.",
    155                     "Tags": [
    156                       "FooBarFr",
    157                       "FooFr",
    158                       "BarFr"
    159                     ]
    160                   }
    161                 ],
    162                 "Installers": [
    163                   {
    164                     "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6",
    165                     "InstallerUrl": "http://foobar.exe",
    166                     "Architecture": "x86",
    167                     "InstallerLocale": "en-US",
    168                     "Platform": [
    169                       "Windows.Desktop"
    170                     ],
    171                     "MinimumOSVersion": "1078",
    172                     "InstallerType": "msix",
    173                     "Scope": "user",
    174                     "SignatureSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6",
    175                     "InstallModes": [
    176                       "interactive"
    177                     ],
    178                     "InstallerSwitches": {
    179                       "Silent": "/s",
    180                       "SilentWithProgress": "/s",
    181                       "Interactive": "/i",
    182                       "InstallLocation": "C:\\Users\\User1",
    183                       "Log": "/l",
    184                       "Upgrade": "/u",
    185                       "Custom": "/custom"
    186                     },
    187                     "InstallerSuccessCodes": [
    188                       0
    189                     ],
    190                     "UpgradeBehavior": "install",
    191                     "Commands": [
    192                       "command1"
    193                     ],
    194                     "Protocols": [
    195                        "protocol1"
    196                     ],
    197                     "FileExtensions": [
    198                       ".file-extension"
    199                     ],
    200                     "Dependencies": {
    201                       "WindowsFeatures": [
    202                         "feature1"
    203                       ],
    204                       "WindowsLibraries": [
    205                         "library1"
    206                       ],
    207                       "PackageDependencies": [
    208                         {
    209                           "PackageIdentifier": "Foo.Baz",
    210                           "MinimumVersion": "2.0.0"
    211                         }
    212                       ],
    213                       "ExternalDependencies": [
    214                         "FooBarBaz"
    215                       ]
    216                     },
    217                     "PackageFamilyName": "FooBar.PackageFamilyName",
    218                     "ProductCode": "",
    219                     "Capabilities": [
    220                       "Bluetooth"
    221                     ],
    222                     "RestrictedCapabilities": [
    223                       "restrictedCapability"
    224                     ]
    225                   }
    226                 ]
    227               }
    228             ]
    229           },
    230           "ContinuationToken": "abcd"
    231         })delimiter");
    232         }
    233 
    234         void VerifyLocalizations_AllFields(const Manifest& manifest)
    235         {
    236             REQUIRE(manifest.DefaultLocalization.Locale == "en-US");
    237             REQUIRE(manifest.DefaultLocalization.Get<Localization::Publisher>() == "Foo");
    238             REQUIRE(manifest.DefaultLocalization.Get<Localization::PublisherUrl>() == "http://publisher.net");
    239             REQUIRE(manifest.DefaultLocalization.Get<Localization::PublisherSupportUrl>() == "http://publisherSupport.net");
    240             REQUIRE(manifest.DefaultLocalization.Get<Localization::PrivacyUrl>() == "http://packagePrivacyUrl.net");
    241             REQUIRE(manifest.DefaultLocalization.Get<Localization::Author>() == "FooBar");
    242             REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageName>() == "Bar");
    243             REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageUrl>() == "http://packageUrl.net");
    244             REQUIRE(manifest.DefaultLocalization.Get<Localization::License>() == "Foo Bar License");
    245             REQUIRE(manifest.DefaultLocalization.Get<Localization::LicenseUrl>() == "http://licenseUrl.net");
    246             REQUIRE(manifest.DefaultLocalization.Get<Localization::Copyright>() == "Foo Bar Copyright");
    247             REQUIRE(manifest.DefaultLocalization.Get<Localization::CopyrightUrl>() == "http://copyrightUrl.net");
    248             REQUIRE(manifest.DefaultLocalization.Get<Localization::ShortDescription>() == "Foo bar is a foo bar.");
    249             REQUIRE(manifest.DefaultLocalization.Get<Localization::Description>() == "Foo bar is a placeholder.");
    250             REQUIRE(manifest.DefaultLocalization.Get<Localization::Tags>().size() == 3);
    251             REQUIRE(manifest.DefaultLocalization.Get<Localization::Tags>().at(0) == "FooBar");
    252             REQUIRE(manifest.DefaultLocalization.Get<Localization::Tags>().at(1) == "Foo");
    253             REQUIRE(manifest.DefaultLocalization.Get<Localization::Tags>().at(2) == "Bar");
    254 
    255             REQUIRE(manifest.Localizations.size() == 1);
    256             ManifestLocalization frenchLocalization = manifest.Localizations.at(0);
    257             REQUIRE(frenchLocalization.Locale == "fr-Fr");
    258             REQUIRE(frenchLocalization.Get<Localization::Publisher>() == "Foo French");
    259             REQUIRE(frenchLocalization.Get<Localization::PublisherUrl>() == "http://publisher-fr.net");
    260             REQUIRE(frenchLocalization.Get<Localization::PublisherSupportUrl>() == "http://publisherSupport-fr.net");
    261             REQUIRE(frenchLocalization.Get<Localization::PrivacyUrl>() == "http://packagePrivacyUrl-fr.net");
    262             REQUIRE(frenchLocalization.Get<Localization::Author>() == "FooBar French");
    263             REQUIRE(frenchLocalization.Get<Localization::PackageName>() == "Bar");
    264             REQUIRE(frenchLocalization.Get<Localization::PackageUrl>() == "http://packageUrl-fr.net");
    265             REQUIRE(frenchLocalization.Get<Localization::License>() == "Foo Bar License");
    266             REQUIRE(frenchLocalization.Get<Localization::LicenseUrl>() == "http://licenseUrl-fr.net");
    267             REQUIRE(frenchLocalization.Get<Localization::Copyright>() == "Foo Bar Copyright");
    268             REQUIRE(frenchLocalization.Get<Localization::CopyrightUrl>() == "http://copyrightUrl-fr.net");
    269             REQUIRE(frenchLocalization.Get<Localization::ShortDescription>() == "Foo bar is a foo bar French.");
    270             REQUIRE(frenchLocalization.Get<Localization::Description>() == "Foo bar is a placeholder French.");
    271             REQUIRE(frenchLocalization.Get<Localization::Tags>().size() == 3);
    272             REQUIRE(frenchLocalization.Get<Localization::Tags>().at(0) == "FooBarFr");
    273             REQUIRE(frenchLocalization.Get<Localization::Tags>().at(1) == "FooFr");
    274             REQUIRE(frenchLocalization.Get<Localization::Tags>().at(2) == "BarFr");
    275         }
    276 
    277         void VerifyInstallers_AllFields(const Manifest& manifest)
    278         {
    279             REQUIRE(manifest.Installers.size() == 1);
    280 
    281             ManifestInstaller actualInstaller = manifest.Installers.at(0);
    282             REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6"));
    283             REQUIRE(actualInstaller.Url == "http://foobar.exe");
    284             REQUIRE(actualInstaller.Arch == Architecture::X86);
    285             REQUIRE(actualInstaller.Locale == "en-US");
    286             REQUIRE(actualInstaller.Platform.size() == 1);
    287             REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop);
    288             REQUIRE(actualInstaller.MinOSVersion == "1078");
    289             REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Msix);
    290             REQUIRE(actualInstaller.Scope == ScopeEnum::User);
    291             REQUIRE(actualInstaller.SignatureSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6"));
    292             REQUIRE(actualInstaller.InstallModes.size() == 1);
    293             REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive);
    294             REQUIRE(actualInstaller.Switches.size() == 7);
    295             REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s");
    296             REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s");
    297             REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i");
    298             REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1");
    299             REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l");
    300             REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u");
    301             REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom");
    302             REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1);
    303             REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0);
    304             REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Install);
    305             REQUIRE(actualInstaller.Commands.at(0) == "command1");
    306             REQUIRE(actualInstaller.Protocols.at(0) == "protocol1");
    307             REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension");
    308             REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1"));
    309             REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1"));
    310             REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0"));
    311             REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz"));
    312             REQUIRE(actualInstaller.PackageFamilyName == "FooBar.PackageFamilyName");
    313             REQUIRE(actualInstaller.ProductCode == "");
    314             REQUIRE(actualInstaller.Capabilities.at(0) == "Bluetooth");
    315             REQUIRE(actualInstaller.RestrictedCapabilities.at(0) == "restrictedCapability");
    316         }
    317     };
    318 }
    319 
    320 TEST_CASE("Search_GoodResponse", "[RestSource][Interface_1_0]")
    321 {
    322     utility::string_t sample = _XPLATSTR(
    323         R"delimiter({
    324             "Data" : [
    325                {
    326               "PackageIdentifier": "git.package",
    327               "PackageName": "package",
    328               "Publisher": "git",
    329               "Versions": [
    330                 {   "PackageVersion": "1.0.0" },
    331                 {   "PackageVersion": "2.0.0" }]
    332             }]
    333         })delimiter");
    334 
    335     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
    336     Interface v1{ TestRestUriString, std::move(helper) };
    337     Schema::IRestClient::SearchResult searchResponse = v1.Search({});
    338     REQUIRE(searchResponse.Matches.size() == 1);
    339     Schema::IRestClient::Package package = searchResponse.Matches.at(0);
    340     REQUIRE(package.PackageInformation.PackageIdentifier.compare("git.package") == 0);
    341     REQUIRE(package.PackageInformation.Publisher.compare("git") == 0);
    342     REQUIRE(package.PackageInformation.PackageName.compare("package") == 0);
    343     REQUIRE(package.Versions.size() == 2);
    344     REQUIRE(package.Versions.at(0).VersionAndChannel.GetVersion().ToString().compare("1.0.0") == 0);
    345     REQUIRE(package.Versions.at(1).VersionAndChannel.GetVersion().ToString().compare("2.0.0") == 0);
    346 }
    347 
    348 TEST_CASE("Search_GoodResponse_AllFields", "[RestSource][Interface_1_0]")
    349 {
    350     utility::string_t sample = _XPLATSTR(
    351         R"delimiter({
    352             "Data" : [
    353                {
    354               "PackageIdentifier": "git.package",
    355               "PackageName": "package",
    356               "Publisher": "git",
    357               "Versions": [
    358                 {
    359                     "PackageVersion": "1.0.0",
    360                     "PackageFamilyNames" : [
    361                         "pfn1",
    362                         "pfn2",
    363                         "pfn2"
    364                     ],
    365                     "ProductCodes" : [
    366                         "pc1",
    367                         "pc2"
    368                     ]
    369                 }]
    370             }]
    371         })delimiter");
    372 
    373     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
    374     Interface v1{ TestRestUriString, std::move(helper) };
    375     Schema::IRestClient::SearchResult searchResponse = v1.Search({});
    376     REQUIRE(searchResponse.Matches.size() == 1);
    377     Schema::IRestClient::Package package = searchResponse.Matches.at(0);
    378     REQUIRE(package.PackageInformation.PackageIdentifier.compare("git.package") == 0);
    379     REQUIRE(package.PackageInformation.Publisher.compare("git") == 0);
    380     REQUIRE(package.PackageInformation.PackageName.compare("package") == 0);
    381     REQUIRE(package.Versions.size() == 1);
    382     REQUIRE(package.Versions.at(0).VersionAndChannel.GetVersion().ToString().compare("1.0.0") == 0);
    383     REQUIRE(package.Versions.at(0).PackageFamilyNames.size() == 2);
    384     REQUIRE(package.Versions.at(0).PackageFamilyNames.at(0) == "pfn1");
    385     REQUIRE(package.Versions.at(0).PackageFamilyNames.at(1) == "pfn2");
    386     REQUIRE(package.Versions.at(0).ProductCodes.at(0) == "pc1");
    387     REQUIRE(package.Versions.at(0).ProductCodes.at(1) == "pc2");
    388 }
    389 
    390 TEST_CASE("Search_GoodResponse_404AsEmpty", "[RestSource][Interface_1_0]")
    391 {
    392     utility::string_t notFoundResponse = _XPLATSTR(
    393         R"delimiter({"code":"DataNotFound","data":[],"details":[],"innererror":{"code":"DataNotFound","data":[],"details":[],"message":"Product is not present","source":"StoreEdgeFD"},"message":"Product is not present","source":"StoreEdgeFD"})delimiter");
    394 
    395     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound, std::move(notFoundResponse)) };
    396     Interface v1{ TestRestUriString, std::move(helper) };
    397     Schema::IRestClient::SearchResult searchResponse = v1.Search({});
    398     REQUIRE(searchResponse.Matches.size() == 0);
    399 }
    400 
    401 TEST_CASE("Search_ContinuationToken", "[RestSource][Interface_1_0]")
    402 {
    403     utility::string_t sample = _XPLATSTR(
    404         R"delimiter({
    405             "Data" : [
    406                {
    407               "PackageIdentifier": "git.package",
    408               "PackageName": "package",
    409               "Publisher": "git",
    410               "Versions": [
    411                 {   "PackageVersion": "1.0.0" }]
    412             },
    413             {
    414               "PackageIdentifier": "foo.package",
    415               "PackageName": "package",
    416               "Publisher": "foo",
    417               "Versions": [
    418                 {   "PackageVersion": "1.0.0" }]
    419             }],
    420            "ContinuationToken" : "abcd-ct="
    421         })delimiter");
    422 
    423     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
    424     Interface v1{ TestRestUriString, std::move(helper) };
    425     SearchRequest request{};
    426     request.MaximumResults = 9;
    427     Schema::IRestClient::SearchResult results = v1.Search(request);
    428     REQUIRE(results.Matches.size() == request.MaximumResults);
    429 
    430     SearchRequest requestWithSize1{};
    431     requestWithSize1.MaximumResults = 1;
    432     Schema::IRestClient::SearchResult resultsWithSize1 = v1.Search(requestWithSize1);
    433     REQUIRE(resultsWithSize1.Matches.size() == requestWithSize1.MaximumResults);
    434 }
    435 
    436 TEST_CASE("Search_BadResponse_NoVersions", "[RestSource][Interface_1_0]")
    437 {
    438     utility::string_t sample = _XPLATSTR(
    439         R"delimiter({
    440             "Data" : [
    441                {
    442               "PackageIdentifier": "git.package",
    443               "PackageName": "package",
    444               "Publisher": "git",
    445               "Versions": null }]
    446         })delimiter");
    447 
    448     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
    449     Interface v1{ TestRestUriString, std::move(helper) };
    450     REQUIRE_THROWS_HR(v1.Search({}), APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA);
    451 }
    452 
    453 TEST_CASE("Search_BadResponse_NotFoundCode", "[RestSource][Interface_1_0]")
    454 {
    455     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) };
    456     Interface v1{ TestRestUriString, std::move(helper) };
    457     REQUIRE_THROWS_HR(v1.Search({}), APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND);
    458 }
    459 
    460 TEST_CASE("Search_Optimized_ManifestResponse", "[RestSource][Interface_1_0]")
    461 {
    462     utility::string_t sample = GetGoodManifest_RequiredFields();
    463     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
    464     AppInstaller::Repository::SearchRequest request;
    465     PackageMatchFilter filter{ PackageMatchField::Id, MatchType::Exact, "Foo" };
    466     request.Filters.emplace_back(std::move(filter));
    467     Interface v1{ TestRestUriString, std::move(helper) };
    468     Schema::IRestClient::SearchResult result = v1.Search(request);
    469     REQUIRE(result.Matches.size() == 1);
    470     REQUIRE(result.Matches[0].Versions.size() == 1);
    471     REQUIRE(result.Matches[0].Versions[0].VersionAndChannel.GetVersion().ToString() == "5.0.0");
    472     REQUIRE(result.Matches[0].Versions[0].VersionAndChannel.GetChannel().ToString() == "");
    473     REQUIRE(result.Matches[0].Versions[0].Manifest);
    474     
    475     // Verify manifest is populated
    476     Manifest manifest = result.Matches[0].Versions[0].Manifest.value();
    477     REQUIRE(manifest.Id == "Foo.Bar");
    478     REQUIRE(manifest.Version == "5.0.0");
    479     REQUIRE(manifest.DefaultLocalization.Locale == "en-us");
    480     REQUIRE(manifest.DefaultLocalization.Get<Localization::Publisher>() == "Foo");
    481     REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageName>() == "Bar");
    482     REQUIRE(manifest.DefaultLocalization.Get<Localization::License>() == "Foo bar license");
    483     REQUIRE(manifest.DefaultLocalization.Get<Localization::ShortDescription>() == "Foo bar description");
    484     REQUIRE(manifest.Installers.size() == 1);
    485     REQUIRE(manifest.Installers[0].Arch == Architecture::X64);
    486     REQUIRE(manifest.Installers[0].Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6"));
    487     REQUIRE(manifest.Installers[0].BaseInstallerType == InstallerTypeEnum::Exe);
    488     REQUIRE(manifest.Installers[0].Url == "https://installer.example.com/foobar.exe");
    489 }
    490 
    491 TEST_CASE("Search_Optimized_ManifestResponse_MultipleVersions", "[RestSource][Interface_1_0]")
    492 {
    493     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) };
    494     AppInstaller::Repository::SearchRequest request;
    495     PackageMatchFilter filter{ PackageMatchField::Id, MatchType::Exact, "Foo.Bar" };
    496     request.Filters.emplace_back(std::move(filter));
    497     Interface v1{ TestRestUriString, std::move(helper) };
    498     Schema::IRestClient::SearchResult result = v1.Search(request);
    499     REQUIRE(result.Matches.size() == 1);
    500     REQUIRE(result.Matches[0].Versions.size() == 2);
    501     REQUIRE(result.Matches[0].Versions[0].VersionAndChannel.GetVersion().ToString() == "5.0.0");
    502     REQUIRE(result.Matches[0].Versions[0].Manifest);
    503     REQUIRE(result.Matches[0].Versions[1].VersionAndChannel.GetVersion().ToString() == "6.0.0");
    504     REQUIRE(result.Matches[0].Versions[1].Manifest);
    505 }
    506 
    507 TEST_CASE("Search_Optimized_NoResponse_NotFoundCode", "[RestSource][Interface_1_0]")
    508 {
    509     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) };
    510     AppInstaller::Repository::SearchRequest request;
    511     PackageMatchFilter filter{ PackageMatchField::Id, MatchType::Exact, "Foo" };
    512     request.Filters.emplace_back(std::move(filter));
    513     Interface v1{ TestRestUriString, std::move(helper) };
    514     REQUIRE_THROWS_HR(v1.Search(request), APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND);
    515 }
    516 
    517 TEST_CASE("GetManifests_GoodResponse", "[RestSource][Interface_1_0]")
    518 {
    519     GoodManifest_AllFields sampleManifest;
    520     utility::string_t sample = sampleManifest.GetSampleManifest_AllFields();
    521     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
    522     Interface v1{ TestRestUriString, std::move(helper) };
    523     std::vector<Manifest> manifests = v1.GetManifests("Foo.Bar");
    524     REQUIRE(manifests.size() == 1);
    525 
    526     // Verify manifest is populated
    527     Manifest manifest = manifests[0];
    528     REQUIRE(manifest.Id == "Foo.Bar");
    529     REQUIRE(manifest.Version == "3.0.0abc");
    530     REQUIRE(manifest.Moniker == "FooBarMoniker");
    531     REQUIRE(manifest.Channel == "");
    532     REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.0.0" });
    533     sampleManifest.VerifyLocalizations_AllFields(manifest);
    534     sampleManifest.VerifyInstallers_AllFields(manifest);
    535 }
    536 
    537 TEST_CASE("GetManifests_GoodResponse_404AsEmpty", "[RestSource][Interface_1_0]")
    538 {
    539     utility::string_t notFoundResponse = _XPLATSTR(
    540         R"delimiter({"code":"DataNotFound","data":[],"details":[],"innererror":{"code":"DataNotFound","data":[],"details":[],"message":"Product is not present","source":"StoreEdgeFD"},"message":"Product is not present","source":"StoreEdgeFD"})delimiter");
    541 
    542     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound, std::move(notFoundResponse)) };
    543     Interface v1{ TestRestUriString, std::move(helper) };
    544     std::vector<Manifest> manifests = v1.GetManifests("Foo.Bar");
    545     REQUIRE(manifests.size() == 0);
    546 }
    547 
    548 TEST_CASE("GetManifests_GoodResponse_MultipleVersions", "[RestSource][Interface_1_0]")
    549 {
    550     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) };
    551     Interface v1{ TestRestUriString, std::move(helper) };
    552 
    553     // GetManifests
    554     std::vector<Manifest> manifests = v1.GetManifests("Foo.Bar");
    555     REQUIRE(manifests.size() == 2);
    556     REQUIRE(manifests[0].Version == "5.0.0");
    557     REQUIRE(manifests[1].Version == "6.0.0");
    558 }
    559 
    560 TEST_CASE("GetManifests_BadResponse_SuccessCode", "[RestSource][Interface_1_0]")
    561 {
    562     utility::string_t badManifest = _XPLATSTR(
    563         R"delimiter({
    564         "Data": {
    565             "PackageIdentifier": "Foo.Bar",
    566             "Versions": [
    567                 {
    568                     "PackageVersion": "5.0.0"
    569                 }
    570             ]
    571         }
    572     })delimiter");
    573 
    574     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(badManifest)) };
    575     Interface v1{ TestRestUriString, std::move(helper) };
    576     REQUIRE_THROWS_HR(v1.GetManifests("Foo.Bar"), APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA);
    577 }
    578 
    579 TEST_CASE("GetManifests_NotFoundCode", "[RestSource][Interface_1_0]")
    580 {
    581     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) };
    582     Interface v1{ TestRestUriString, std::move(helper) };
    583     REQUIRE_THROWS_HR(v1.GetManifests("Foo.Bar"), APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND);
    584 }
    585 
    586 TEST_CASE("GetManifests_GoodResponse_UnknownInstaller", "[RestSource][Interface_1_0]")
    587 {
    588     utility::string_t msstoreInstallerResponse = _XPLATSTR(
    589         R"delimiter({
    590         "Data": {
    591             "PackageIdentifier": "Foo.Bar",
    592             "Versions": [
    593                 {
    594                     "PackageVersion": "5.0.0",
    595                     "DefaultLocale": {
    596                         "PackageLocale": "en-us",
    597                         "Publisher": "Foo",
    598                         "PackageName": "Bar",
    599                         "License": "Foo bar license",
    600                         "ShortDescription": "Foo bar description"
    601                     },
    602                     "Installers": [
    603                         {
    604                             "Architecture": "x64",
    605                             "InstallerType": "msstore",
    606                             "MSStoreProductIdentifier": "9nblggh4nns1"
    607                         }
    608                     ]
    609                 }
    610             ]
    611         }
    612     })delimiter");
    613 
    614     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(msstoreInstallerResponse)) };
    615     Interface v1{ TestRestUriString, std::move(helper) };
    616     std::vector<Manifest> manifests = v1.GetManifests("Foo.Bar");
    617     REQUIRE(manifests.size() == 1);
    618 
    619     // Verify manifest is populated and manifest validation passed
    620     Manifest& manifest = manifests[0];
    621     REQUIRE(manifest.Installers.size() == 1);
    622     REQUIRE(manifest.Installers.at(0).BaseInstallerType == InstallerTypeEnum::Unknown);
    623     REQUIRE(manifest.Installers.at(0).ProductId.empty());
    624 }
    625 
    626 TEST_CASE("GetManifestByVersion_GoodResponse_MultipleVersions_VersionFound", "[RestSource][Interface_1_0]")
    627 {
    628     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) };
    629     Interface v1{ TestRestUriString, std::move(helper) };
    630 
    631     // GetManifests
    632     std::optional<Manifest> manifest = v1.GetManifestByVersion("Foo.Bar", "5.0.0", "");
    633     REQUIRE(manifest.has_value());
    634     REQUIRE(manifest->Version == "5.0.0");
    635 }
    636 
    637 TEST_CASE("GetManifestByVersion_GoodResponse_MultipleVersions_VersionNotFound", "[RestSource][Interface_1_0]")
    638 {
    639     HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) };
    640     Interface v1{ TestRestUriString, std::move(helper) };
    641 
    642     // GetManifests
    643     std::optional<Manifest> manifest = v1.GetManifestByVersion("Foo.Bar", "7.0.0", "");
    644     REQUIRE_FALSE(manifest.has_value());
    645 }