示例#1
0
    public async Task Command_can_throw_a_special_exception_which_exits_with_specified_code_and_message()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{
    public ValueTask ExecuteAsync(IConsole console) =>
        throw new CommandException(""Something went wrong"", 69);
}
");

        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            Array.Empty <string>(),
            new Dictionary <string, string>()
            );

        var stdOut = FakeConsole.ReadOutputString();
        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().Be(69);
        stdOut.Should().BeEmpty();
        stdErr.Trim().Should().Be("Something went wrong");
    }
示例#2
0
    public async Task Delegate_type_activator_fails_if_the_underlying_function_returns_null()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{
    public ValueTask ExecuteAsync(IConsole console)
    {
        console.Output.WriteLine(""foo"");
        return default;
    }
}");

        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .UseTypeActivator(_ => null !)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            Array.Empty <string>(),
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().Contain("Failed to create an instance of type");
    }
示例#3
0
    public async Task Default_type_activator_fails_if_the_type_does_not_have_a_parameterless_constructor()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{
    public Command(string foo) {}

    public ValueTask ExecuteAsync(IConsole console) => default;
}");

        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .UseTypeActivator(new DefaultTypeActivator())
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            Array.Empty <string>(),
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().Contain("Failed to create an instance of type");
    }
示例#4
0
    public async Task Parameter_binding_fails_if_a_parameter_of_non_scalar_type_has_not_been_provided_with_at_least_one_value()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{
    [CommandParameter(0)]
    public string Foo { get; set; }

    [CommandParameter(1)]
    public IReadOnlyList<string> Bar { get; set; }

    public ValueTask ExecuteAsync(IConsole console) => default;
}");

        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            new[] { "one" },
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().Contain("Missing required parameter(s)");
    }
示例#5
0
    public async Task Parameter_binding_fails_if_one_of_the_provided_parameters_is_unexpected()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{
    [CommandParameter(0)]
    public string Foo { get; set; }

    [CommandParameter(1)]
    public string Bar { get; set; }

    public ValueTask ExecuteAsync(IConsole console) => default;
}");

        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            new[] { "one", "two", "three" },
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().Contain("Unexpected parameter(s)");
    }
示例#6
0
        public async Task Option_binding_fails_if_one_of_the_provided_option_names_is_not_recognized()
        {
            // Arrange
            var commandType = DynamicCommandBuilder.Compile(
                // language=cs
                @"
[Command]
public class Command : ICommand
{
    [CommandOption(""foo"")]
    public string Foo { get; set; }
    
    public ValueTask ExecuteAsync(IConsole console) => default;
}");

            var application = new CliApplicationBuilder()
                              .AddCommand(commandType)
                              .UseConsole(FakeConsole)
                              .Build();

            // Act
            var exitCode = await application.RunAsync(
                new[] { "--foo", "one", "--bar", "two" },
                new Dictionary <string, string>()
                );

            var stdErr = FakeConsole.ReadErrorString();

            // Assert
            exitCode.Should().NotBe(0);
            stdErr.Should().Contain("Unrecognized option(s)");
        }
示例#7
0
        public async Task Option_binding_fails_if_an_option_of_scalar_type_has_been_provided_with_multiple_values()
        {
            // Arrange
            var commandType = DynamicCommandBuilder.Compile(
                // language=cs
                @"
[Command]
public class Command : ICommand
{
    [CommandOption(""foo"")]
    public string Foo { get; set; }
    
    public ValueTask ExecuteAsync(IConsole console) => default;
}");

            var application = new CliApplicationBuilder()
                              .AddCommand(commandType)
                              .UseConsole(FakeConsole)
                              .Build();

            // Act
            var exitCode = await application.RunAsync(
                new[] { "--foo", "one", "two", "three" },
                new Dictionary <string, string>()
                );

            var stdErr = FakeConsole.ReadErrorString();

            // Assert
            exitCode.Should().NotBe(0);
            stdErr.Should().Contain("expects a single argument, but provided with multiple");
        }
示例#8
0
        public async Task Option_binding_fails_if_a_required_option_has_been_provided_with_an_empty_value()
        {
            // Arrange
            var commandType = DynamicCommandBuilder.Compile(
                // language=cs
                @"
[Command]
public class Command : ICommand
{
    [CommandOption(""foo"", IsRequired = true)]
    public string Foo { get; set; }
    
    public ValueTask ExecuteAsync(IConsole console) => default;
}");

            var application = new CliApplicationBuilder()
                              .AddCommand(commandType)
                              .UseConsole(FakeConsole)
                              .Build();

            // Act
            var exitCode = await application.RunAsync(
                new[] { "--foo" },
                new Dictionary <string, string>()
                );

            var stdErr = FakeConsole.ReadErrorString();

            // Assert
            exitCode.Should().NotBe(0);
            stdErr.Should().Contain("Missing required option(s)");
        }
示例#9
0
    public async Task Parameter_or_option_value_conversion_fails_if_the_target_type_is_not_supported()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
public class CustomType {}

[Command]
public class Command : ICommand
{
    [CommandOption('f')]
    public CustomType Foo { get; set; }

    public ValueTask ExecuteAsync(IConsole console) => default;
}
");
        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            new[] { "-f", "xyz" },
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().Contain("has an unsupported underlying property type");
    }
示例#10
0
    public async Task Parameter_or_option_value_conversion_fails_if_the_value_cannot_be_converted_to_the_target_type()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{
    [CommandOption('f')]
    public int Foo { get; set; }

    public ValueTask ExecuteAsync(IConsole console) => default;
}
");
        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            new[] { "-f", "12.34" },
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().NotBeNullOrWhiteSpace();
    }
示例#11
0
        public async Task Application_configuration_fails_if_an_invalid_command_is_registered()
        {
            // Act
            var app = new CliApplicationBuilder()
                      .AddCommand(typeof(ApplicationSpecs))
                      .UseConsole(FakeConsole)
                      .Build();

            var exitCode = await app.RunAsync(
                Array.Empty <string>(),
                new Dictionary <string, string>()
                );

            var stdErr = FakeConsole.ReadErrorString();

            // Assert
            exitCode.Should().NotBe(0);
            stdErr.Should().Contain("not a valid command");
        }
示例#12
0
    public async Task Parameter_or_option_value_conversion_fails_if_one_of_the_validators_fail()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
public class ValidatorA : BindingValidator<int>
{
    public override BindingValidationError Validate(int value) => Ok();
}

public class ValidatorB : BindingValidator<int>
{
    public override BindingValidationError Validate(int value) => Error(""Hello world"");
}

[Command]
public class Command : ICommand
{
    [CommandOption('f', Validators = new[] {typeof(ValidatorA), typeof(ValidatorB)})]
    public int Foo { get; set; }

    public ValueTask ExecuteAsync(IConsole console) => default;
}
");
        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            new[] { "-f", "12" },
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().Contain("Hello world");
    }
示例#13
0
    public async Task Fake_console_can_be_used_with_an_in_memory_backing_store()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{   
    public ValueTask ExecuteAsync(IConsole console)
    {
        var input = console.Input.ReadToEnd();
        console.Output.WriteLine(input);
        console.Error.WriteLine(input);

        return default;
    }
}
");

        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        FakeConsole.WriteInput("Hello world");

        var exitCode = await application.RunAsync(
            Array.Empty <string>(),
            new Dictionary <string, string>()
            );

        var stdOut = FakeConsole.ReadOutputString();
        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().Be(0);
        stdOut.Trim().Should().Be("Hello world");
        stdErr.Trim().Should().Be("Hello world");
    }
示例#14
0
    public async Task Parameter_or_option_value_conversion_fails_if_the_static_parse_method_throws()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
public class CustomType
{
    public string Value { get; }

    private CustomType(string value) => Value = value;

    public static CustomType Parse(string value) => throw new Exception(""Hello world"");
}

[Command]
public class Command : ICommand
{
    [CommandOption('f')]
    public CustomType Foo { get; set; }

    public ValueTask ExecuteAsync(IConsole console) => default;
}
");
        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            new[] { "-f", "bar" },
            new Dictionary <string, string>()
            );

        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdErr.Should().Contain("Hello world");
    }
示例#15
0
        public async Task Help_text_is_printed_on_invalid_user_input()
        {
            // Arrange
            var application = new CliApplicationBuilder()
                              .AddCommand <NoOpCommand>()
                              .UseConsole(FakeConsole)
                              .SetDescription("This will be in help text")
                              .Build();

            // Act
            var exitCode = await application.RunAsync(
                new[] { "invalid-command", "--invalid-option" },
                new Dictionary <string, string>()
                );

            var stdOut = FakeConsole.ReadOutputString();
            var stdErr = FakeConsole.ReadErrorString();

            // Assert
            exitCode.Should().NotBe(0);
            stdOut.Should().Contain("This will be in help text");
            stdErr.Should().NotBeNullOrWhiteSpace();
        }
示例#16
0
    public async Task Command_can_throw_an_exception_with_an_inner_exception_which_exits_with_a_stacktrace()
    {
        // Arrange
        var commandType = DynamicCommandBuilder.Compile(
            // language=cs
            @"
[Command]
public class Command : ICommand
{
    public ValueTask ExecuteAsync(IConsole console) =>
        throw new Exception(""Something went wrong"", new Exception(""Another exception""));
}
");

        var application = new CliApplicationBuilder()
                          .AddCommand(commandType)
                          .UseConsole(FakeConsole)
                          .Build();

        // Act
        var exitCode = await application.RunAsync(
            Array.Empty <string>(),
            new Dictionary <string, string>()
            );

        var stdOut = FakeConsole.ReadOutputString();
        var stdErr = FakeConsole.ReadErrorString();

        // Assert
        exitCode.Should().NotBe(0);
        stdOut.Should().BeEmpty();
        stdErr.Should().ContainAllInOrder(
            "System.Exception", "Something went wrong",
            "System.Exception", "Another exception",
            "at", "CliFx."
            );
    }