Skip to content

Commit 990e2d7

Browse files
authored
Merge pull request #1619 from bartvanandel/feat/order-by-parameter
(#3709) Add support for ordering strategies using a new `--order-by` argument on the search command
2 parents 6722dce + 3a36bc8 commit 990e2d7

File tree

10 files changed

+1157
-22
lines changed

10 files changed

+1157
-22
lines changed

src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ $commandOptions = @{
5656
pin = "--name='' --version=''"
5757
push = "--api-key='' --source=''"
5858
rule = "--name=''"
59-
search = "--all-versions --approved-only --by-id-only --by-tag-only --cert='' --certpassword='' --detail --disable-repository-optimizations --download-cache-only --exact --id-only --id-starts-with --include-configured-sources --include-programs --not-broken --order-by-popularity --page='' --page-size='' --password='' --prerelease --source='' --user=''"
59+
search = "--all-versions --approved-only --by-id-only --by-tag-only --cert='' --certpassword='' --detail --disable-repository-optimizations --download-cache-only --exact --id-only --id-starts-with --include-configured-sources --include-programs --not-broken --order-by='' --order-by-popularity --page='' --page-size='' --password='' --prerelease --source='' --user=''"
6060
source = "--admin-only --allow-self-service --bypass-proxy --cert='' --certpassword='' --name='' --password='' --priority='' --source='' --user=''"
6161
support = ""
6262
template = "--name=''"
@@ -155,7 +155,7 @@ function script:chocoRemotePackages($filter) {
155155
if ($filter -and $filter.StartsWith(".")) {
156156
return;
157157
} #file search
158-
@('packages.config|') + @(& $script:choco search $filter --page='0' --page-size='30' -r --id-starts-with --order-by-popularity) |
158+
@('packages.config|') + @(& $script:choco search $filter --page='0' --page-size='30' -r --id-starts-with --order-by='popularity') |
159159
Where-Object { $_ -like "$filter*" } |
160160
ForEach-Object { $_.Split('|')[0] }
161161
}
@@ -166,6 +166,22 @@ function Get-AliasPattern($exe) {
166166
"($($aliases -join '|'))"
167167
}
168168

169+
function Get-ChocoOrderByOptions {
170+
<#
171+
.SYNOPSIS
172+
Returns the list of canonical --order-by values for Chocolatey.
173+
174+
.DESCRIPTION
175+
These values correspond to the distinct, non-aliased entries in the
176+
PackageOrder enum. They are sorted alphabetically and must be updated
177+
manually when the enum changes.
178+
179+
.OUTPUTS
180+
A string in the format "Id|LastPublished|Popularity|Title|Unsorted"
181+
#>
182+
return @("Id", "LastPublished", "Popularity", "Title", "Unsorted")
183+
}
184+
169185
function ChocolateyTabExpansion($lastBlock) {
170186
switch -regex ($lastBlock -replace "^$(Get-AliasPattern choco) ", "") {
171187

@@ -254,6 +270,13 @@ function ChocolateyTabExpansion($lastBlock) {
254270
chocoLocalPackagesUpgrade $matches['package']
255271
}
256272

273+
# Custom completion for --order-by values
274+
"^search.*--order-by='?(?<prefix>.*)'?$" {
275+
$prefix = $matches['prefix']
276+
return Get-ChocoOrderByOptions | Where-Object { $_ -like "$prefix*" } | ForEach-Object { "--order-by='$_'"}
277+
}
278+
279+
257280
# Handles more options after others
258281
"^(?<cmd>$($commandOptions.Keys -join '|'))(?<currentArguments>.*)\s+(?<op>\S*)$" {
259282
chocoCmdOperations $commandOptions $matches['cmd'] $matches['op'] $matches['currentArguments']
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright © 2017 - 2025 Chocolatey Software, Inc
2+
// Copyright © 2011 - 2017 RealDimensions Software, LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
//
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
using FluentAssertions;
18+
19+
namespace chocolatey.tests
20+
{
21+
public static class MockLoggerExtensions
22+
{
23+
public static void ShouldHaveWarningContaining(this MockLogger logger, string expectedSubstring)
24+
{
25+
logger.Messages.Should().ContainKey(LogLevel.Warn.ToString())
26+
.WhoseValue.Should().Contain(w => w.Contains(expectedSubstring));
27+
}
28+
}
29+
}

src/chocolatey.tests/chocolatey.tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@
184184
<Compile Include="infrastructure.app\services\ChocolateyConfigSettingsServiceSpecs.cs" />
185185
<Compile Include="infrastructure.app\services\ChocolateyPackageServiceSpecs.cs" />
186186
<Compile Include="infrastructure.app\services\FilesServiceSpecs.cs" />
187+
<Compile Include="infrastructure.app\services\NugetListSpecs.cs" />
187188
<Compile Include="infrastructure.app\services\NugetServiceSpecs.cs" />
188189
<Compile Include="infrastructure.app\services\RegistryServiceSpecs.cs" />
189190
<Compile Include="infrastructure.app\services\RulesServiceSpecs.cs" />
@@ -205,6 +206,7 @@
205206
<Compile Include="infrastructure\tolerance\FaultToleranceSpecs.cs" />
206207
<Compile Include="infrastructure.app\utility\PackageUtilitySpecs.cs" />
207208
<Compile Include="MockLogger.cs" />
209+
<Compile Include="MockLoggerExtensions.cs" />
208210
<Compile Include="Properties\AssemblyInfo.cs" />
209211
<Compile Include="TinySpec.cs" />
210212
<Compile Include="UNCHelper.cs" />

src/chocolatey.tests/infrastructure.app/commands/ChocolateySearchCommandSpecs.cs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
using chocolatey.infrastructure.commandline;
2525
using Moq;
2626
using FluentAssertions;
27+
using chocolatey.infrastructure.app.domain;
2728

2829
namespace chocolatey.tests.infrastructure.app.commands
2930
{
@@ -38,6 +39,8 @@ public abstract class ChocolateySearchCommandSpecsBase : TinySpec
3839

3940
public override void Context()
4041
{
42+
MockLogger.Reset();
43+
4144
Configuration.Sources = "bob";
4245
Command = new ChocolateySearchCommand(PackageService.Object);
4346
}
@@ -259,6 +262,24 @@ public void Should_add_short_version_of_password_to_the_option_set()
259262
{
260263
_optionSet.Contains("p").Should().BeTrue();
261264
}
265+
266+
[Fact]
267+
public void Should_add_order_by_to_the_option_set()
268+
{
269+
_optionSet.Contains("order-by").Should().BeTrue();
270+
}
271+
272+
[Fact]
273+
public void Should_add_order_by_popularity_to_the_option_set()
274+
{
275+
_optionSet.Contains("order-by-popularity").Should().BeTrue();
276+
}
277+
278+
[Fact]
279+
public void Should_have_marked_order_by_popularity_as_deprecated()
280+
{
281+
_optionSet["order-by-popularity"].Description.Should().Contain("Deprecated");
282+
}
262283
}
263284

264285
public class When_handling_additional_argument_parsing : ChocolateySearchCommandSpecsBase
@@ -316,6 +337,192 @@ public void Should_call_service_list_noop()
316337
}
317338
}
318339

340+
[NUnit.Framework.TestFixtureSource(nameof(TestOrders))]
341+
public class When_handling_order_by_parameter_correctly_parses_argument : ChocolateySearchCommandSpecsBase
342+
{
343+
private OptionSet _optionSet;
344+
private OptionContext _optionContext;
345+
private PackageOrder _order;
346+
private string _orderString;
347+
348+
public When_handling_order_by_parameter_correctly_parses_argument(string orderString)
349+
{
350+
_orderString = orderString;
351+
_order = (PackageOrder)Enum.Parse(typeof(PackageOrder), _orderString);
352+
}
353+
354+
private static object[] TestOrders
355+
{
356+
get
357+
{
358+
var values = Enum.GetNames(typeof(PackageOrder));
359+
var result = new List<object>();
360+
361+
foreach (var value in values)
362+
{
363+
result.Add(new object[] { value });
364+
}
365+
366+
return result.ToArray();
367+
}
368+
}
369+
370+
public override void Context()
371+
{
372+
base.Context();
373+
374+
_optionSet = new OptionSet();
375+
Command.ConfigureArgumentParser(_optionSet, Configuration);
376+
_optionContext = new OptionContext(_optionSet)
377+
{
378+
Option = _optionSet["order-by"]
379+
};
380+
_optionContext.OptionName = _optionContext.Option.Names.First();
381+
_optionContext.OptionValues.Add(_orderString);
382+
}
383+
384+
public override void Because()
385+
{
386+
_optionContext.Option.Invoke(_optionContext);
387+
}
388+
389+
[Fact]
390+
public void Should_have_set_expected_package_sort_on_config()
391+
{
392+
Configuration.ListCommand.OrderBy.Should().Be(_order);
393+
}
394+
}
395+
396+
[NUnit.Framework.TestFixture(null)]
397+
[NUnit.Framework.TestFixture("")]
398+
public class When_handling_order_by_with_empty_values : ChocolateySearchCommandSpecsBase
399+
{
400+
private OptionSet _optionSet;
401+
private OptionContext _optionContext;
402+
private Exception _ex = null;
403+
private string _testValue;
404+
405+
public When_handling_order_by_with_empty_values(string testValue)
406+
{
407+
_testValue = testValue;
408+
}
409+
410+
public override void Context()
411+
{
412+
base.Context();
413+
414+
_optionSet = new OptionSet();
415+
Command.ConfigureArgumentParser(_optionSet, Configuration);
416+
_optionContext = new OptionContext(_optionSet)
417+
{
418+
Option = _optionSet["order-by"]
419+
};
420+
_optionContext.OptionName = _optionContext.Option.Names.First();
421+
_optionContext.OptionValues.Add(_testValue);
422+
}
423+
424+
public override void Because()
425+
{
426+
try
427+
{
428+
_optionContext.Option.Invoke(_optionContext);
429+
}
430+
catch (Exception ex)
431+
{
432+
_ex = ex;
433+
}
434+
}
435+
436+
[Fact]
437+
public void Should_have_thrown_expected_exception()
438+
{
439+
_ex.Should().NotBeNull()
440+
.And.BeOfType<ApplicationException>()
441+
.Which.Message.Should().StartWith("No '--order-by' clause was provided. Specify one of the supported clauses:");
442+
}
443+
}
444+
445+
public class When_handling_order_by_with_unsupported_value : ChocolateySearchCommandSpecsBase
446+
{
447+
private OptionSet _optionSet;
448+
private OptionContext _optionContext;
449+
private Exception _ex = null;
450+
451+
public override void Context()
452+
{
453+
base.Context();
454+
455+
_optionSet = new OptionSet();
456+
Command.ConfigureArgumentParser(_optionSet, Configuration);
457+
_optionContext = new OptionContext(_optionSet)
458+
{
459+
Option = _optionSet["order-by"]
460+
};
461+
_optionContext.OptionName = _optionContext.Option.Names.First();
462+
_optionContext.OptionValues.Add("NotExisting");
463+
}
464+
465+
public override void Because()
466+
{
467+
try
468+
{
469+
_optionContext.Option.Invoke(_optionContext);
470+
}
471+
catch (Exception ex)
472+
{
473+
_ex = ex;
474+
}
475+
}
476+
477+
[Fact]
478+
public void Should_have_thrown_expected_exception()
479+
{
480+
_ex.Should().NotBeNull()
481+
.And.BeOfType<ApplicationException>()
482+
.Which.Message.Should().StartWith("The '--order-by' clause 'NotExisting' is not recognized. Use one of the supported clauses:");
483+
}
484+
}
485+
486+
public class When_handling_order_by_popularity : ChocolateySearchCommandSpecsBase
487+
{
488+
private OptionSet _optionSet;
489+
private OptionContext _optionContext;
490+
491+
public override void Context()
492+
{
493+
base.Context();
494+
495+
_optionSet = new OptionSet();
496+
Command.ConfigureArgumentParser(_optionSet, Configuration);
497+
_optionContext = new OptionContext(_optionSet)
498+
{
499+
Option = _optionSet["order-by-popularity"]
500+
};
501+
_optionContext.OptionName = _optionContext.Option.Names.First();
502+
_optionContext.OptionValues.Add(bool.TrueString);
503+
}
504+
505+
public override void Because()
506+
{
507+
_optionContext.Option.Invoke(_optionContext);
508+
}
509+
510+
[Fact]
511+
public void Should_have_set_expected_order_by_property_value()
512+
{
513+
Configuration.ListCommand.OrderBy.Should().Be(PackageOrder.Popularity);
514+
}
515+
516+
[Fact]
517+
public void Should_have_outputted_warning_message()
518+
{
519+
MockLogger.Messages.Should()
520+
.ContainKey(LogLevel.Warn.ToString())
521+
.WhoseValue.Should().Contain(@"'--order-by-popularity' is deprecated and will be removed in a future release.
522+
Use '--order-by='Popularity'' instead.");
523+
}
524+
}
525+
319526
public class When_noop_is_called : ChocolateySearchCommandSpecsBase
320527
{
321528
public override void Because()

0 commit comments

Comments
 (0)