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"); }
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"); }
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"); }
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)"); }
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)"); }
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)"); }
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"); }
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)"); }
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"); }
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(); }
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"); }
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"); }
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"); }
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"); }
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(); }
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." ); }