diff --git a/e2e/spring-shell-e2e-tests/test/sample-e2e-default-value.test.ts b/e2e/spring-shell-e2e-tests/test/sample-e2e-default-value.test.ts index bcdd5bdb..a76c584d 100644 --- a/e2e/spring-shell-e2e-tests/test/sample-e2e-default-value.test.ts +++ b/e2e/spring-shell-e2e-tests/test/sample-e2e-default-value.test.ts @@ -74,6 +74,146 @@ describe('e2e commands default-value', () => { await expect(cli.exitCode()).resolves.toBe(0); }; + /** + * testDefaultValueBoolean1 - 1 + */ + const annoDefaultValueBoolean1WithoutArgReturnsFalseDesc = 'default boolean1 without arg returns false (anno)'; + const annoDefaultValueBoolean1WithoutArgCommand = ['e2e anno default-value-boolean1']; + const annoDefaultValueBoolean1WithoutArgReturnsFalse = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello false')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean1Registration - 1 + */ + const regDefaultValueBoolean1WithoutArgReturnsFalseDesc = 'default boolean1 without arg returns false (reg)'; + const regDefaultValueBoolean1WithoutArgCommand = ['e2e reg default-value-boolean1']; + const regDefaultValueBoolean1WithoutArgReturnsFalse = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello false')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean1 - 2 + */ + const annoDefaultValueBoolean2WithArgReturnsTrueDesc = 'default boolean1 with arg returns true (anno)'; + const annoDefaultValueBoolean2WithArgCommand = ['e2e anno default-value-boolean1 --arg1']; + const annoDefaultValueBoolean2WithArgReturnsTrue = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello true')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean1Registration - 2 + */ + const regDefaultValueBoolean2WithArgReturnsTrueDesc = 'default boolean1 with arg returns true (reg)'; + const regDefaultValueBoolean2WithArgCommand = ['e2e reg default-value-boolean1 --arg1']; + const regDefaultValueBoolean2WithArgReturnsTrue = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello true')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean1 - 3 + */ + const annoDefaultValueBoolean3WithArgReturnsFalseDesc = 'default boolean1 with arg returns false (anno)'; + const annoDefaultValueBoolean3WithArgCommand = ['e2e anno default-value-boolean1 --arg1 false']; + const annoDefaultValueBoolean3WithArgReturnsFalse = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello false')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean1Registration - 3 + */ + const regDefaultValueBoolean3WithArgReturnsFalseDesc = 'default boolean1 with arg returns false (reg)'; + const regDefaultValueBoolean3WithArgCommand = ['e2e reg default-value-boolean1 --arg1 false']; + const regDefaultValueBoolean3WithArgReturnsFalse = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello false')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean2 - 1 + */ + const annoDefaultValueBoolean2WithoutArgReturnsTrueDesc = 'default boolean2 without arg returns true (anno)'; + const annoDefaultValueBoolean2WithoutArgCommand = ['e2e anno default-value-boolean2']; + const annoDefaultValueBoolean2WithoutArgReturnsTrue = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello true')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean2Registration - 1 + */ + const regDefaultValueBoolean2WithoutArgReturnsTrueDesc = 'default boolean2 without arg returns true (reg)'; + const regDefaultValueBoolean2WithoutArgCommand = ['e2e reg default-value-boolean2']; + const regDefaultValueBoolean2WithoutArgReturnsTrue = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello true')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean3 - 1 + */ + const annoDefaultValueBoolean3WithoutArgReturnsFalseDesc = 'default boolean3 without arg returns false (anno)'; + const annoDefaultValueBoolean3WithoutArgCommand = ['e2e anno default-value-boolean3']; + const annoDefaultValueBoolean3WithoutArgReturnsFalse = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello false')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + + /** + * testDefaultValueBoolean3Registration - 1 + */ + const regDefaultValueBoolean3WithoutArgReturnsFalseDesc = 'default boolean3 without arg returns false (reg)'; + const regDefaultValueBoolean3WithoutArgCommand = ['e2e reg default-value-boolean3']; + const regDefaultValueBoolean3WithoutArgReturnsFalse = async (cli: Cli) => { + cli.run(); + await waitForExpect(async () => { + const screen = cli.screen(); + expect(screen).toEqual(expect.arrayContaining([expect.stringContaining('Hello false')])); + }); + await expect(cli.exitCode()).resolves.toBe(0); + }; + beforeEach(async () => { waitForExpect.defaults.timeout = waitForExpectDefaultTimeout; waitForExpect.defaults.interval = waitForExpectDefaultInterval; @@ -139,6 +279,126 @@ describe('e2e commands default-value', () => { }, testTimeout ); + + it( + annoDefaultValueBoolean1WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean1WithoutArgCommand] + }); + await annoDefaultValueBoolean1WithoutArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean1WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean1WithoutArgCommand] + }); + await regDefaultValueBoolean1WithoutArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean2WithArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean2WithArgCommand] + }); + await annoDefaultValueBoolean2WithArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean2WithArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean2WithArgCommand] + }); + await regDefaultValueBoolean2WithArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean3WithArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean3WithArgCommand] + }); + await annoDefaultValueBoolean3WithArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean3WithArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean3WithArgCommand] + }); + await regDefaultValueBoolean3WithArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean2WithoutArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean2WithoutArgCommand] + }); + await annoDefaultValueBoolean2WithoutArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean2WithoutArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean2WithoutArgCommand] + }); + await regDefaultValueBoolean2WithoutArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean3WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean3WithoutArgCommand] + }); + await annoDefaultValueBoolean3WithoutArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean3WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean3WithoutArgCommand] + }); + await regDefaultValueBoolean3WithoutArgReturnsFalse(cli); + }, + testTimeout + ); }); /** @@ -197,5 +457,125 @@ describe('e2e commands default-value', () => { }, testTimeout ); + + it( + annoDefaultValueBoolean1WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean1WithoutArgCommand] + }); + await annoDefaultValueBoolean1WithoutArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean1WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean1WithoutArgCommand] + }); + await regDefaultValueBoolean1WithoutArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean2WithArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean2WithArgCommand] + }); + await annoDefaultValueBoolean2WithArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean2WithArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean2WithArgCommand] + }); + await regDefaultValueBoolean2WithArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean3WithArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean3WithArgCommand] + }); + await annoDefaultValueBoolean3WithArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean3WithArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean3WithArgCommand] + }); + await regDefaultValueBoolean3WithArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean2WithoutArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean2WithoutArgCommand] + }); + await annoDefaultValueBoolean2WithoutArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean2WithoutArgReturnsTrueDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean2WithoutArgCommand] + }); + await regDefaultValueBoolean2WithoutArgReturnsTrue(cli); + }, + testTimeout + ); + + it( + annoDefaultValueBoolean3WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...annoDefaultValueBoolean3WithoutArgCommand] + }); + await annoDefaultValueBoolean3WithoutArgReturnsFalse(cli); + }, + testTimeout + ); + + it( + regDefaultValueBoolean3WithoutArgReturnsFalseDesc, + async () => { + cli = new Cli({ + command: command, + options: [...options, ...regDefaultValueBoolean3WithoutArgCommand] + }); + await regDefaultValueBoolean3WithoutArgReturnsFalse(cli); + }, + testTimeout + ); }); }); diff --git a/spring-shell-samples/src/main/java/org/springframework/shell/samples/e2e/DefaultValueCommands.java b/spring-shell-samples/src/main/java/org/springframework/shell/samples/e2e/DefaultValueCommands.java index 242e692b..5e88bb87 100644 --- a/spring-shell-samples/src/main/java/org/springframework/shell/samples/e2e/DefaultValueCommands.java +++ b/spring-shell-samples/src/main/java/org/springframework/shell/samples/e2e/DefaultValueCommands.java @@ -53,4 +53,83 @@ public class DefaultValueCommands extends BaseE2ECommands { .and() .build(); } + + @ShellMethod(key = LEGACY_ANNO + "default-value-boolean1", group = GROUP) + public String testDefaultValueBoolean1( + @ShellOption(defaultValue = "false") boolean arg1 + ) { + return "Hello " + arg1; + } + + @Bean + public CommandRegistration testDefaultValueBoolean1Registration() { + return CommandRegistration.builder() + .command(REG, "default-value-boolean1") + .group(GROUP) + .withOption() + .longNames("arg1") + .defaultValue("false") + .type(boolean.class) + .and() + .withTarget() + .function(ctx -> { + boolean arg1 = ctx.getOptionValue("arg1"); + return "Hello " + arg1; + }) + .and() + .build(); + } + + @ShellMethod(key = LEGACY_ANNO + "default-value-boolean2", group = GROUP) + public String testDefaultValueBoolean2( + @ShellOption(defaultValue = "true") boolean arg1 + ) { + return "Hello " + arg1; + } + + @Bean + public CommandRegistration testDefaultValueBoolean2Registration() { + return CommandRegistration.builder() + .command(REG, "default-value-boolean2") + .group(GROUP) + .withOption() + .longNames("arg1") + .defaultValue("true") + .type(boolean.class) + .and() + .withTarget() + .function(ctx -> { + boolean arg1 = ctx.getOptionValue("arg1"); + return "Hello " + arg1; + }) + .and() + .build(); + } + + @ShellMethod(key = LEGACY_ANNO + "default-value-boolean3", group = GROUP) + public String testDefaultValueBoolean3( + @ShellOption boolean arg1 + ) { + return "Hello " + arg1; + } + + @Bean + public CommandRegistration testDefaultValueBoolean3Registration() { + return CommandRegistration.builder() + .command(REG, "default-value-boolean3") + .group(GROUP) + .withOption() + .longNames("arg1") + .required(false) + .type(boolean.class) + .defaultValue("false") + .and() + .withTarget() + .function(ctx -> { + boolean arg1 = ctx.getOptionValue("arg1"); + return "Hello " + arg1; + }) + .and() + .build(); + } } diff --git a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java b/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java index b36a0293..9db814c2 100644 --- a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java +++ b/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java @@ -158,7 +158,13 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, App optionSpec.defaultValue(so.defaultValue()); } if (ObjectUtils.nullSafeEquals(so.defaultValue(), ShellOption.NONE)) { - optionSpec.required(); + if (ClassUtils.isAssignable(boolean.class, parameterType)) { + optionSpec.required(false); + optionSpec.defaultValue("false"); + } + else { + optionSpec.required(); + } } if (!ClassUtils.isAssignable(NoValueProvider.class, so.valueProvider())) { CompletionResolver completionResolver = ctx -> { diff --git a/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTests.java b/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTests.java index 2ec28711..1c563336 100644 --- a/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTests.java +++ b/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTests.java @@ -405,4 +405,42 @@ public class StandardMethodTargetRegistrarTests { public void foo3(@ShellOption(defaultValue = ShellOption.NULL) String arg1) { } } + + @Test + public void testOptionValuesWithBoolean() { + applicationContext = new AnnotationConfigApplicationContext(ValuesWithBoolean.class); + registrar.setApplicationContext(applicationContext); + registrar.register(catalog); + + assertThat(catalog.getRegistrations().get("foo1")).isNotNull(); + assertThat(catalog.getRegistrations().get("foo1").getOptions()).hasSize(1); + assertThat(catalog.getRegistrations().get("foo1").getOptions().get(0).getDefaultValue()).isEqualTo("false"); + assertThat(catalog.getRegistrations().get("foo1").getOptions().get(0).isRequired()).isFalse(); + + assertThat(catalog.getRegistrations().get("foo2")).isNotNull(); + assertThat(catalog.getRegistrations().get("foo2").getOptions()).hasSize(1); + assertThat(catalog.getRegistrations().get("foo2").getOptions().get(0).getDefaultValue()).isEqualTo("true"); + assertThat(catalog.getRegistrations().get("foo2").getOptions().get(0).isRequired()).isFalse(); + + assertThat(catalog.getRegistrations().get("foo3")).isNotNull(); + assertThat(catalog.getRegistrations().get("foo3").getOptions()).hasSize(1); + assertThat(catalog.getRegistrations().get("foo3").getOptions().get(0).isRequired()).isFalse(); + assertThat(catalog.getRegistrations().get("foo3").getOptions().get(0).getDefaultValue()).isEqualTo("false"); + } + + @ShellComponent + public static class ValuesWithBoolean { + + @ShellMethod(value = "foo1") + public void foo1(@ShellOption(defaultValue = "false") boolean arg1) { + } + + @ShellMethod(value = "foo2") + public void foo2(@ShellOption(defaultValue = "true") boolean arg1) { + } + + @ShellMethod(value = "foo3") + public void foo3(@ShellOption boolean arg1) { + } + } }