Skip to content

Commands shouldn't be invoked with default(T) values when argument is required and no default value is provided #646

@johncrim

Description

@johncrim

It looks like there is a bunch of validation for missing arguments, but it isn't being called when the option isn't specified. The following test fails, I've highlighted what I think are problems with PROBLEM :

        [Fact]
        public void Invoke_command_with_missing_arguments_should_return_error()
        {
            var command = new RootCommand()
                          {
                              new Option("--int-value")
                              {
                                  Argument = new Argument("intValue")
                                             {
                                                 ArgumentType = typeof(int)
                                             }
                              }
                          };

            bool commandInvoked = false;
            command.Handler = CommandHandler.Create<int>((int intValue) =>
                                                         {  // PROBLEM: This handler is called with intValue: 0
                                                             commandInvoked = true;
                                                         });

            // Argument doesn't have a default value, and is required once:
            Assert.False(command.Children.OfType<Option>().First().Argument.HasDefaultValue);
            Assert.Equal(1, command.Children.OfType<Option>().First().Argument.Arity.MinimumNumberOfValues);

            var console = new TestConsole();
            command.Invoke("", console);

            // PROBLEM: Expected: Command not invoked
            Assert.False(commandInvoked);
            // PROBLEM: Expected: Should see errors on the console
        }

I looked at #484 , and while that is the root of the problem, it doesn't make sense from a usability standpoint. In short, if I have a command like the above, I shouldn't have to worry about a required argument being passed in as default(T) when arity is 1, and no default value is specified. If I want a default value for a parameter, I'll specify it, eg (int intValue=0) and/or Argument.SetDefaultValue(0).

Please reconsider this design.

It seems at odds with other behavior, too. For example, here's a test in the codebase that passes:

        [Fact]
        public void When_command_arguments_are_fewer_than_minimum_arity_then_an_error_is_returned()
        {
            var command = new Command("the-command")
            {
                Argument = new Argument
                {
                    Arity = new ArgumentArity(2, 3)
                }
            };

            var result = command.Parse("1");

            result.Errors
                  .Select(e => e.Message)
                  .Should()
                  .Contain(ValidationMessages.Instance.RequiredArgumentMissing(result.CommandResult));
        }

Am I missing something that positional arguments are fundamentally different from named arguments?

Or, if this design is absolutely not going to change, is there a good way to add middleware that changes this behavior, such that all required arguments with arity.min = 1, raise the existing "missing parameter" error messages?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions