diff --git a/doc/release-notes/1.31/2558-productcode-case-insensitive.md b/doc/release-notes/1.31/2558-productcode-case-insensitive.md new file mode 100644 index 0000000000..5b689c28c3 --- /dev/null +++ b/doc/release-notes/1.31/2558-productcode-case-insensitive.md @@ -0,0 +1,5 @@ +## Bug Fixes + +### ProductCode matching is case-insensitive for single-package searches + +Single-package searches now send ProductCode matches as case-insensitive requests, allowing REST sources to match ProductCode values whose casing differs from the installed ARP value or manifest value. diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 50f8b0c02b..6b3dcd5eb2 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -951,9 +951,10 @@ namespace AppInstaller::CLI::Workflow { std::string_view query = args.GetArg(Execution::Args::Type::Query); - // Regardless of match type, always use an exact match for the system reference strings. + // Package family names use folded exact matching in the local index. Product codes + // must be case-insensitive for REST sources, which may preserve manifest casing. searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, query)); - searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, query)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::CaseInsensitive, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Name, matchType, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, matchType, query)); diff --git a/src/AppInstallerCLITests/SourceFlow.cpp b/src/AppInstallerCLITests/SourceFlow.cpp index d6f4084c26..7053c3103f 100644 --- a/src/AppInstallerCLITests/SourceFlow.cpp +++ b/src/AppInstallerCLITests/SourceFlow.cpp @@ -200,6 +200,25 @@ TEST_CASE("Search_ManifestResolution_BeforeSourcePriority", "[RestSource][Source REQUIRE(lookups == 1); } +TEST_CASE("SearchRequestForSingle_ProductCodeIsCaseInsensitive", "[workflow]") +{ + std::ostringstream output; + TestContext context{ output, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.Args.AddArg(Execution::Args::Type::Query, "Fiddler2"sv); + + context << GetSearchRequestForSingle; + + const auto& searchRequest = context.Get(); + auto productCode = std::find_if(searchRequest.Inclusions.begin(), searchRequest.Inclusions.end(), [](const auto& filter) + { + return filter.Field == PackageMatchField::ProductCode; + }); + REQUIRE(productCode != searchRequest.Inclusions.end()); + REQUIRE(productCode->Value == "Fiddler2"); + REQUIRE(productCode->Type == MatchType::CaseInsensitive); +} + TEST_CASE("SourcePriority_SourceOutput", "[SourcePriority][workflow]") { GroupPolicyTestOverride policies;