fix: allow the cli to accept negative values (#33084)
also adding a unit testable picocli closes: #33068 Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
b717810061
commit
d981f7f55d
11 changed files with 299 additions and 217 deletions
|
@ -616,6 +616,12 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- twitter api -->
|
<!-- twitter api -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.twitter4j</groupId>
|
<groupId>org.twitter4j</groupId>
|
||||||
|
|
|
@ -22,7 +22,6 @@ import static org.keycloak.quarkus.runtime.Environment.isDevProfile;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isNonServerMode;
|
import static org.keycloak.quarkus.runtime.Environment.isNonServerMode;
|
||||||
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
|
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
|
||||||
import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun;
|
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.wasBuildEverRun;
|
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.wasBuildEverRun;
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.Start.isDevProfileNotAllowed;
|
import static org.keycloak.quarkus.runtime.cli.command.Start.isDevProfileNotAllowed;
|
||||||
|
@ -104,7 +103,7 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse arguments and execute any of the configured commands
|
// parse arguments and execute any of the configured commands
|
||||||
parseAndRun(cliArgs);
|
new Picocli().parseAndRun(cliArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -94,7 +94,7 @@ import picocli.CommandLine.Model.ISetter;
|
||||||
import picocli.CommandLine.Model.OptionSpec;
|
import picocli.CommandLine.Model.OptionSpec;
|
||||||
import picocli.CommandLine.Model.ArgGroupSpec;
|
import picocli.CommandLine.Model.ArgGroupSpec;
|
||||||
|
|
||||||
public final class Picocli {
|
public class Picocli {
|
||||||
|
|
||||||
public static final String ARG_PREFIX = "--";
|
public static final String ARG_PREFIX = "--";
|
||||||
public static final String ARG_SHORT_PREFIX = "-";
|
public static final String ARG_SHORT_PREFIX = "-";
|
||||||
|
@ -105,10 +105,7 @@ public final class Picocli {
|
||||||
boolean includeBuildTime;
|
boolean includeBuildTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Picocli() {
|
public void parseAndRun(List<String> cliArgs) {
|
||||||
}
|
|
||||||
|
|
||||||
public static void parseAndRun(List<String> cliArgs) {
|
|
||||||
// perform two passes over the cli args. First without option validation to determine the current command, then with option validation enabled
|
// perform two passes over the cli args. First without option validation to determine the current command, then with option validation enabled
|
||||||
CommandLine cmd = createCommandLine(spec -> spec
|
CommandLine cmd = createCommandLine(spec -> spec
|
||||||
.addUnmatchedArgsBinding(CommandLine.Model.UnmatchedArgsBinding.forStringArrayConsumer(new ISetter() {
|
.addUnmatchedArgsBinding(CommandLine.Model.UnmatchedArgsBinding.forStringArrayConsumer(new ISetter() {
|
||||||
|
@ -135,7 +132,7 @@ public final class Picocli {
|
||||||
exitCode = runReAugmentationIfNeeded(cliArgs, cmd, currentCommand);
|
exitCode = runReAugmentationIfNeeded(cliArgs, cmd, currentCommand);
|
||||||
} else {
|
} else {
|
||||||
PropertyMappers.sanitizeDisabledMappers();
|
PropertyMappers.sanitizeDisabledMappers();
|
||||||
exitCode = cmd.execute(argArray);
|
exitCode = run(cmd, argArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
exitOnFailure(exitCode, cmd);
|
exitOnFailure(exitCode, cmd);
|
||||||
|
@ -146,7 +143,11 @@ public final class Picocli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CommandLine createCommandLineForCommand(List<String> cliArgs, List<CommandLine> commandLineList) {
|
protected int run(CommandLine cmd, String[] argArray) {
|
||||||
|
return cmd.execute(argArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandLine createCommandLineForCommand(List<String> cliArgs, List<CommandLine> commandLineList) {
|
||||||
return createCommandLine(spec -> {
|
return createCommandLine(spec -> {
|
||||||
// use the incoming commandLineList from the initial parsing to determine the current command
|
// use the incoming commandLineList from the initial parsing to determine the current command
|
||||||
CommandSpec currentSpec = spec;
|
CommandSpec currentSpec = spec;
|
||||||
|
@ -177,7 +178,7 @@ public final class Picocli {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void catchParameterException(ParameterException parEx, CommandLine cmd, String[] args) {
|
private void catchParameterException(ParameterException parEx, CommandLine cmd, String[] args) {
|
||||||
int exitCode;
|
int exitCode;
|
||||||
try {
|
try {
|
||||||
exitCode = cmd.getParameterExceptionHandler().handleParseException(parEx, args);
|
exitCode = cmd.getParameterExceptionHandler().handleParseException(parEx, args);
|
||||||
|
@ -189,20 +190,20 @@ public final class Picocli {
|
||||||
exitOnFailure(exitCode, cmd);
|
exitOnFailure(exitCode, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void catchProfileException(String message, Throwable cause, CommandLine cmd) {
|
private void catchProfileException(String message, Throwable cause, CommandLine cmd) {
|
||||||
ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
|
ExecutionExceptionHandler errorHandler = new ExecutionExceptionHandler();
|
||||||
errorHandler.error(cmd.getErr(), message, cause);
|
errorHandler.error(cmd.getErr(), message, cause);
|
||||||
exitOnFailure(CommandLine.ExitCode.USAGE, cmd);
|
exitOnFailure(CommandLine.ExitCode.USAGE, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void exitOnFailure(int exitCode, CommandLine cmd) {
|
protected void exitOnFailure(int exitCode, CommandLine cmd) {
|
||||||
if (exitCode != cmd.getCommandSpec().exitCodeOnSuccess() && !Environment.isTestLaunchMode() || isRebuildCheck()) {
|
if (exitCode != cmd.getCommandSpec().exitCodeOnSuccess() && !Environment.isTestLaunchMode() || isRebuildCheck()) {
|
||||||
// hard exit wanted, as build failed and no subsequent command should be executed. no quarkus involved.
|
// hard exit wanted, as build failed and no subsequent command should be executed. no quarkus involved.
|
||||||
System.exit(exitCode);
|
System.exit(exitCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd, CommandLine currentCommand) {
|
protected int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd, CommandLine currentCommand) {
|
||||||
int exitCode = 0;
|
int exitCode = 0;
|
||||||
|
|
||||||
if (currentCommand == null) {
|
if (currentCommand == null) {
|
||||||
|
@ -669,7 +670,7 @@ public final class Picocli {
|
||||||
return key.startsWith("kc.provider.file");
|
return key.startsWith("kc.provider.file");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CommandLine createCommandLine(Consumer<CommandSpec> consumer) {
|
public CommandLine createCommandLine(Consumer<CommandSpec> consumer) {
|
||||||
CommandSpec spec = CommandSpec.forAnnotatedObject(new Main()).name(Environment.getCommand());
|
CommandSpec spec = CommandSpec.forAnnotatedObject(new Main()).name(Environment.getCommand());
|
||||||
consumer.accept(spec);
|
consumer.accept(spec);
|
||||||
|
|
||||||
|
@ -679,11 +680,15 @@ public final class Picocli {
|
||||||
cmd.setParameterExceptionHandler(new ShortErrorMessageHandler());
|
cmd.setParameterExceptionHandler(new ShortErrorMessageHandler());
|
||||||
cmd.setHelpFactory(new HelpFactory());
|
cmd.setHelpFactory(new HelpFactory());
|
||||||
cmd.getHelpSectionMap().put(SECTION_KEY_COMMAND_LIST, new SubCommandListRenderer());
|
cmd.getHelpSectionMap().put(SECTION_KEY_COMMAND_LIST, new SubCommandListRenderer());
|
||||||
cmd.setErr(new PrintWriter(System.err, true));
|
cmd.setErr(getErrWriter());
|
||||||
|
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected PrintWriter getErrWriter() {
|
||||||
|
return new PrintWriter(System.err, true);
|
||||||
|
}
|
||||||
|
|
||||||
private static void addHelp(CommandSpec currentSpec) {
|
private static void addHelp(CommandSpec currentSpec) {
|
||||||
try {
|
try {
|
||||||
currentSpec.addOption(OptionSpec.builder(Help.OPTION_NAMES)
|
currentSpec.addOption(OptionSpec.builder(Help.OPTION_NAMES)
|
||||||
|
@ -795,7 +800,6 @@ public final class Picocli {
|
||||||
return mapper.getExpectedValues().iterator();
|
return mapper.getExpectedValues().iterator();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.parameterConsumer(PropertyMapperParameterConsumer.INSTANCE)
|
|
||||||
.hidden(mapper.isHidden());
|
.hidden(mapper.isHidden());
|
||||||
|
|
||||||
if (mapper.getDefaultValue().isPresent()) {
|
if (mapper.getDefaultValue().isPresent()) {
|
||||||
|
@ -804,6 +808,14 @@ public final class Picocli {
|
||||||
|
|
||||||
if (mapper.getType() != null) {
|
if (mapper.getType() != null) {
|
||||||
optBuilder.type(mapper.getType());
|
optBuilder.type(mapper.getType());
|
||||||
|
if (mapper.isList()) {
|
||||||
|
// make picocli aware of the only list convention we allow
|
||||||
|
optBuilder.splitRegex(",");
|
||||||
|
} else if (mapper.getType().isEnum()) {
|
||||||
|
// prevent the auto-conversion that picocli does
|
||||||
|
// we validate the expected values later
|
||||||
|
optBuilder.type(String.class);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
optBuilder.type(String.class);
|
optBuilder.type(String.class);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.quarkus.runtime.cli;
|
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_PREFIX;
|
|
||||||
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import picocli.CommandLine;
|
|
||||||
import picocli.CommandLine.Model.ArgSpec;
|
|
||||||
import picocli.CommandLine.Model.CommandSpec;
|
|
||||||
import picocli.CommandLine.Model.OptionSpec;
|
|
||||||
import picocli.CommandLine.ParameterException;
|
|
||||||
|
|
||||||
public final class PropertyMapperParameterConsumer implements CommandLine.IParameterConsumer {
|
|
||||||
|
|
||||||
static final CommandLine.IParameterConsumer INSTANCE = new PropertyMapperParameterConsumer();
|
|
||||||
|
|
||||||
private PropertyMapperParameterConsumer() {
|
|
||||||
// singleton
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consumeParameters(Stack<String> args, ArgSpec argSpec,
|
|
||||||
CommandSpec commandSpec) {
|
|
||||||
if (argSpec instanceof OptionSpec) {
|
|
||||||
validateOption(args, argSpec, commandSpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateOption(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec) {
|
|
||||||
OptionSpec option = (OptionSpec) argSpec;
|
|
||||||
String name = String.join(", ", option.names());
|
|
||||||
CommandLine commandLine = commandSpec.commandLine();
|
|
||||||
|
|
||||||
if (args.isEmpty() || !isOptionValue(args.peek())) {
|
|
||||||
throw new ParameterException(commandLine,
|
|
||||||
"Missing required value. " + getExpectedMessage(argSpec, option, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumes the value, actual value validation will be performed later
|
|
||||||
args.pop();
|
|
||||||
|
|
||||||
if (!args.isEmpty() && isOptionValue(args.peek())) {
|
|
||||||
throw new ParameterException(commandLine, getExpectedMessage(argSpec, option, name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getExpectedMessage(ArgSpec argSpec, OptionSpec option, String name) {
|
|
||||||
return String.format("Option '%s' (%s) expects %s.%s", name, argSpec.paramLabel(),
|
|
||||||
option.typeInfo().isMultiValue() ? "one or more comma separated values without whitespace": "a single value",
|
|
||||||
getExpectedValuesMessage(argSpec.completionCandidates(), option.completionCandidates()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOptionValue(String arg) {
|
|
||||||
return !(arg.startsWith(ARG_PREFIX) || arg.startsWith(Picocli.ARG_SHORT_PREFIX));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getExpectedValuesMessage(Iterable<String> specCandidates, Iterable<String> optionCandidates) {
|
|
||||||
return optionCandidates.iterator().hasNext() ? " Expected values are: " + String.join(", ", specCandidates) : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,26 +1,28 @@
|
||||||
package org.keycloak.quarkus.runtime.cli;
|
package org.keycloak.quarkus.runtime.cli;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
import org.keycloak.quarkus.runtime.cli.command.Start;
|
||||||
import org.keycloak.quarkus.runtime.configuration.KcUnmatchedArgumentException;
|
import org.keycloak.quarkus.runtime.configuration.KcUnmatchedArgumentException;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
import picocli.CommandLine.IParameterExceptionHandler;
|
import picocli.CommandLine.IParameterExceptionHandler;
|
||||||
|
import picocli.CommandLine.MissingParameterException;
|
||||||
|
import picocli.CommandLine.Model.ArgSpec;
|
||||||
import picocli.CommandLine.Model.CommandSpec;
|
import picocli.CommandLine.Model.CommandSpec;
|
||||||
|
import picocli.CommandLine.Model.OptionSpec;
|
||||||
import picocli.CommandLine.ParameterException;
|
import picocli.CommandLine.ParameterException;
|
||||||
import picocli.CommandLine.UnmatchedArgumentException;
|
import picocli.CommandLine.UnmatchedArgumentException;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.BooleanSupplier;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
|
|
||||||
|
|
||||||
public class ShortErrorMessageHandler implements IParameterExceptionHandler {
|
public class ShortErrorMessageHandler implements IParameterExceptionHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,6 +72,15 @@ public class ShortErrorMessageHandler implements IParameterExceptionHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (ex instanceof MissingParameterException) {
|
||||||
|
MissingParameterException mpe = (MissingParameterException)ex;
|
||||||
|
if (mpe.getMissing().size() == 1) {
|
||||||
|
ArgSpec spec = mpe.getMissing().get(0);
|
||||||
|
if (spec instanceof OptionSpec) {
|
||||||
|
OptionSpec option = (OptionSpec)spec;
|
||||||
|
errorMessage = getExpectedMessage(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.println(cmd.getColorScheme().errorText(errorMessage));
|
writer.println(cmd.getColorScheme().errorText(errorMessage));
|
||||||
|
@ -97,4 +108,15 @@ public class ShortErrorMessageHandler implements IParameterExceptionHandler {
|
||||||
private String[] getUnmatchedPartsByOptionSeparator(UnmatchedArgumentException uae, String separator) {
|
private String[] getUnmatchedPartsByOptionSeparator(UnmatchedArgumentException uae, String separator) {
|
||||||
return uae.getUnmatched().get(0).split(separator);
|
return uae.getUnmatched().get(0).split(separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getExpectedMessage(OptionSpec option) {
|
||||||
|
return String.format("Option '%s' (%s) expects %s.%s", String.join(", ", option.names()), option.paramLabel(),
|
||||||
|
option.typeInfo().isMultiValue() ? "one or more comma separated values without whitespace": "a single value",
|
||||||
|
getExpectedValuesMessage(option.completionCandidates()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getExpectedValuesMessage(Iterable<String> specCandidates) {
|
||||||
|
return specCandidates.iterator().hasNext() ? " Expected values are: " + String.join(", ", specCandidates) : "";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPer
|
||||||
public abstract class AbstractStartCommand extends AbstractCommand implements Runnable {
|
public abstract class AbstractStartCommand extends AbstractCommand implements Runnable {
|
||||||
public static final String OPTIMIZED_BUILD_OPTION_LONG = "--optimized";
|
public static final String OPTIMIZED_BUILD_OPTION_LONG = "--optimized";
|
||||||
|
|
||||||
|
private boolean skipStart;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Environment.setParsedCommand(this);
|
Environment.setParsedCommand(this);
|
||||||
|
@ -48,8 +50,10 @@ public abstract class AbstractStartCommand extends AbstractCommand implements Ru
|
||||||
executionError(spec.commandLine(), Messages.optimizedUsedForFirstStartup());
|
executionError(spec.commandLine(), Messages.optimizedUsedForFirstStartup());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skipStart) {
|
||||||
KeycloakMain.start((ExecutionExceptionHandler) cmd.getExecutionExceptionHandler(), cmd.getErr(), cmd.getParseResult().originalArgs().toArray(new String[0]));
|
KeycloakMain.start((ExecutionExceptionHandler) cmd.getExecutionExceptionHandler(), cmd.getErr(), cmd.getParseResult().originalArgs().toArray(new String[0]));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void doBeforeRun() {
|
protected void doBeforeRun() {
|
||||||
|
|
||||||
|
@ -69,4 +73,8 @@ public abstract class AbstractStartCommand extends AbstractCommand implements Ru
|
||||||
return EnumSet.of(OptionCategory.IMPORT, OptionCategory.EXPORT);
|
return EnumSet.of(OptionCategory.IMPORT, OptionCategory.EXPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSkipStart(boolean skipStart) {
|
||||||
|
this.skipStart = skipStart;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ import org.keycloak.config.Option;
|
||||||
import org.keycloak.config.OptionBuilder;
|
import org.keycloak.config.OptionBuilder;
|
||||||
import org.keycloak.config.OptionCategory;
|
import org.keycloak.config.OptionCategory;
|
||||||
import org.keycloak.quarkus.runtime.cli.PropertyException;
|
import org.keycloak.quarkus.runtime.cli.PropertyException;
|
||||||
import org.keycloak.quarkus.runtime.cli.PropertyMapperParameterConsumer;
|
import org.keycloak.quarkus.runtime.cli.ShortErrorMessageHandler;
|
||||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.KcEnvConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.KcEnvConfigSource;
|
||||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||||
|
@ -418,10 +418,14 @@ public class PropertyMapper<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isList() {
|
||||||
|
return getOption().getType() == java.util.List.class;
|
||||||
|
}
|
||||||
|
|
||||||
public void validateValues(ConfigValue configValue, BiConsumer<ConfigValue, String> singleValidator) {
|
public void validateValues(ConfigValue configValue, BiConsumer<ConfigValue, String> singleValidator) {
|
||||||
String value = configValue.getValue();
|
String value = configValue.getValue();
|
||||||
|
|
||||||
boolean multiValued = getOption().getType() == java.util.List.class;
|
boolean multiValued = isList();
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
String[] values = multiValued ? value.split(",") : new String[] { value };
|
String[] values = multiValued ? value.split(",") : new String[] { value };
|
||||||
|
@ -462,7 +466,7 @@ public class PropertyMapper<T> {
|
||||||
if (!expectedValues.isEmpty() && !expectedValues.contains(v) && getOption().isStrictExpectedValues()) {
|
if (!expectedValues.isEmpty() && !expectedValues.contains(v) && getOption().isStrictExpectedValues()) {
|
||||||
throw new PropertyException(
|
throw new PropertyException(
|
||||||
String.format("Invalid value for option %s: %s.%s", getOptionAndSourceMessage(configValue), v,
|
String.format("Invalid value for option %s: %s.%s", getOptionAndSourceMessage(configValue), v,
|
||||||
PropertyMapperParameterConsumer.getExpectedValuesMessage(expectedValues, expectedValues)));
|
ShortErrorMessageHandler.getExpectedValuesMessage(expectedValues)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,17 @@ import static org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourcePro
|
||||||
public final class PropertyMappers {
|
public final class PropertyMappers {
|
||||||
|
|
||||||
public static String VALUE_MASK = "*******";
|
public static String VALUE_MASK = "*******";
|
||||||
private static final MappersConfig MAPPERS = new MappersConfig();
|
private static MappersConfig MAPPERS;
|
||||||
private static final Logger log = Logger.getLogger(PropertyMappers.class);
|
private static final Logger log = Logger.getLogger(PropertyMappers.class);
|
||||||
|
|
||||||
private PropertyMappers(){}
|
private PropertyMappers(){}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reset() {
|
||||||
|
MAPPERS = new MappersConfig();
|
||||||
MAPPERS.addAll(CachingPropertyMappers.getClusteringPropertyMappers());
|
MAPPERS.addAll(CachingPropertyMappers.getClusteringPropertyMappers());
|
||||||
MAPPERS.addAll(DatabasePropertyMappers.getDatabasePropertyMappers());
|
MAPPERS.addAll(DatabasePropertyMappers.getDatabasePropertyMappers());
|
||||||
MAPPERS.addAll(HostnameV2PropertyMappers.getHostnamePropertyMappers());
|
MAPPERS.addAll(HostnameV2PropertyMappers.getHostnamePropertyMappers());
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package or.keycloak.quarkus.runtime.cli;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.quarkus.runtime.cli.Picocli;
|
||||||
|
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.test.AbstractConfigurationTest;
|
||||||
|
|
||||||
|
import io.smallrye.config.SmallRyeConfig;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import picocli.CommandLine.Help;
|
||||||
|
|
||||||
|
public class PicocliTest extends AbstractConfigurationTest {
|
||||||
|
|
||||||
|
// TODO: could utilize CLIResult
|
||||||
|
private class NonRunningPicocli extends Picocli {
|
||||||
|
|
||||||
|
final StringWriter err = new StringWriter();
|
||||||
|
SmallRyeConfig config;
|
||||||
|
int exitCode = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
String getErrString() {
|
||||||
|
// normalize line endings - TODO: could also normalize non-printable chars
|
||||||
|
// but for now those are part of the expected output
|
||||||
|
return System.lineSeparator().equals("\n") ? err.toString()
|
||||||
|
: err.toString().replace(System.lineSeparator(), "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PrintWriter getErrWriter() {
|
||||||
|
return new PrintWriter(err, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void exitOnFailure(int exitCode, CommandLine cmd) {
|
||||||
|
this.exitCode = exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int runReAugmentationIfNeeded(List<String> cliArgs, CommandLine cmd, CommandLine currentCommand) {
|
||||||
|
throw new AssertionError("Should not reaugment");
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int run(CommandLine cmd, String[] argArray) {
|
||||||
|
skipStart(cmd);
|
||||||
|
return super.run(cmd, argArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipStart(CommandLine cmd) {
|
||||||
|
for (CommandLine sub : cmd.getSubcommands().values()) {
|
||||||
|
if (sub.getCommand() instanceof AbstractStartCommand) {
|
||||||
|
((AbstractStartCommand) (sub.getCommand())).setSkipStart(true);
|
||||||
|
}
|
||||||
|
skipStart(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parseAndRun(List<String> cliArgs) {
|
||||||
|
ConfigArgsConfigSource.setCliArgs(cliArgs.toArray(String[]::new));
|
||||||
|
config = createConfig();
|
||||||
|
super.parseAndRun(cliArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
NonRunningPicocli pseudoLaunch(String... args) {
|
||||||
|
NonRunningPicocli nonRunningPicocli = new NonRunningPicocli();
|
||||||
|
nonRunningPicocli.parseAndRun(Arrays.asList(args));
|
||||||
|
return nonRunningPicocli;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNegativeArgument() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertEquals("1h",
|
||||||
|
nonRunningPicocli.config.getConfigValue("quarkus.http.ssl.certificate.reload-period").getValue());
|
||||||
|
|
||||||
|
nonRunningPicocli = pseudoLaunch("start-dev", "--https-certificates-reload-period=-1");
|
||||||
|
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||||
|
assertNull(nonRunningPicocli.config.getConfigValue("quarkus.http.ssl.certificate.reload-period").getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidArgumentType() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--http-port=a");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(),
|
||||||
|
containsString("Invalid value for option '--http-port': 'a' is not an int"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failWrongEnumValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--log-console-level=wrong");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString(
|
||||||
|
"Invalid value for option '--log-console-level': wrong. Expected values are: off, fatal, error, warn, info, debug, trace, all"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failMissingOptionValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--db");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString(
|
||||||
|
"Option '--db' (vendor) expects a single value. Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failMultipleOptionValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("build", "--db", "mysql", "postgres");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString("Unknown option: 'postgres'"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failMultipleMultiOptionValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("build", "--features", "linkedin-oauth", "account3");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString("Unknown option: 'account3'"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failMissingMultiOptionValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("build", "--features");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString(
|
||||||
|
"Option '--features' (feature) expects one or more comma separated values without whitespace. Expected values are:"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failInvalidMultiOptionValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("build", "--features", "xyz,account3");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(),
|
||||||
|
containsString("xyz is an unrecognized feature, it should be one of"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failUnknownOption() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("build", "--nosuch");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString("Unknown option: '--nosuch'"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failUnknownOptionWhitespaceSeparatorNotShowingValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--db-pasword", "mytestpw");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString(Help.defaultColorScheme(Help.Ansi.AUTO)
|
||||||
|
.errorText("Unknown option: '--db-pasword'")
|
||||||
|
+ "\nPossible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failUnknownOptionEqualsSeparatorNotShowingValue() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--db-pasword=mytestpw");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString(Help.defaultColorScheme(Help.Ansi.AUTO)
|
||||||
|
.errorText("Unknown option: '--db-pasword'")
|
||||||
|
+ "\nPossible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failWithFirstOptionOnMultipleUnknownOptions() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--db-username=foobar", "--db-pasword=mytestpw",
|
||||||
|
"--foobar=barfoo");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString(Help.defaultColorScheme(Help.Ansi.AUTO)
|
||||||
|
.errorText("Unknown option: '--db-pasword'")
|
||||||
|
+ "\nPossible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failSingleParamWithSpace() {
|
||||||
|
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--db postgres");
|
||||||
|
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||||
|
assertThat(nonRunningPicocli.getErrString(), containsString(
|
||||||
|
"Option: '--db postgres' is not expected to contain whitespace, please remove any unnecessary quoting/escaping"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import org.keycloak.Config;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
|
||||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -109,6 +110,7 @@ public abstract class AbstractConfigurationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallRyeConfigProviderResolver.class.cast(ConfigProviderResolver.instance()).releaseConfig(ConfigProvider.getConfig());
|
SmallRyeConfigProviderResolver.class.cast(ConfigProviderResolver.instance()).releaseConfig(ConfigProvider.getConfig());
|
||||||
|
PropertyMappers.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Config.Scope initConfig(String... scope) {
|
protected Config.Scope initConfig(String... scope) {
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.it.cli;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.keycloak.it.junit5.extension.CLIResult;
|
|
||||||
import org.keycloak.it.junit5.extension.CLITest;
|
|
||||||
import org.keycloak.it.junit5.extension.ConfigurationTestResource;
|
|
||||||
|
|
||||||
import io.quarkus.test.common.QuarkusTestResource;
|
|
||||||
import io.quarkus.test.junit.main.Launch;
|
|
||||||
import io.quarkus.test.junit.main.LaunchResult;
|
|
||||||
import org.keycloak.it.utils.KeycloakDistribution;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
@QuarkusTestResource(value = ConfigurationTestResource.class, restrictToAnnotatedClass = true)
|
|
||||||
@CLITest
|
|
||||||
public class OptionValidationTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"build", "--db"})
|
|
||||||
public void failMissingOptionValue(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertThat(cliResult.getErrorOutput(), containsString("Missing required value. Option '--db' (vendor) expects a single value. Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"build", "--db", "mysql", "postgres"})
|
|
||||||
public void failMultipleOptionValue(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertThat(cliResult.getErrorOutput(), containsString("Option '--db' (vendor) expects a single value. Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"build", "--features", "linkedin-oauth", "account3"})
|
|
||||||
public void failMultipleMultiOptionValue(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertThat(cliResult.getErrorOutput(), containsString("Option '--features' (feature) expects one or more comma separated values without whitespace. Expected values are: "));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"build", "--features", "xyz,account3"})
|
|
||||||
public void failInvalidMultiOptionValue(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertThat(cliResult.getErrorOutput(), containsString("xyz is an unrecognized feature, it should be one of"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"build", "--nosuch"})
|
|
||||||
public void failUnknownOption(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertEquals("Unknown option: '--nosuch'\n" +
|
|
||||||
"Try '" + KeycloakDistribution.SCRIPT_CMD + " build --help' for more information on the available options.", cliResult.getErrorOutput());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"start", "--db-pasword", "mytestpw"})
|
|
||||||
public void failUnknownOptionWhitespaceSeparatorNotShowingValue(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertEquals("Unknown option: '--db-pasword'\n" +
|
|
||||||
"Possible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db\n" +
|
|
||||||
"Try '" + KeycloakDistribution.SCRIPT_CMD + " start --help' for more information on the available options.", cliResult.getErrorOutput());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"start", "--db-pasword=mytestpw"})
|
|
||||||
public void failUnknownOptionEqualsSeparatorNotShowingValue(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertEquals("Unknown option: '--db-pasword'\n" +
|
|
||||||
"Possible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db\n" +
|
|
||||||
"Try '" + KeycloakDistribution.SCRIPT_CMD + " start --help' for more information on the available options.", cliResult.getErrorOutput());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"start", "--db-username=foobar", "--db-pasword=mytestpw", "--foobar=barfoo"})
|
|
||||||
public void failWithFirstOptionOnMultipleUnknownOptions(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
assertEquals("Unknown option: '--db-pasword'\n" +
|
|
||||||
"Possible solutions: --db-url, --db-url-host, --db-url-database, --db-url-port, --db-url-properties, --db-username, --db-password, --db-schema, --db-pool-initial-size, --db-pool-min-size, --db-pool-max-size, --db-driver, --db\n" +
|
|
||||||
"Try '" + KeycloakDistribution.SCRIPT_CMD + " start --help' for more information on the available options.", cliResult.getErrorOutput());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Launch({"start", "--db postgres"})
|
|
||||||
void failSingleParamWithSpace(LaunchResult result) {
|
|
||||||
CLIResult cliResult = (CLIResult) result;
|
|
||||||
cliResult.assertError("Option: '--db postgres' is not expected to contain whitespace, please remove any unnecessary quoting/escaping");
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue