package io.dropwizard.cli;
import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.dropwizard.util.JarLocation;
import net.sourceforge.argparse4j.inf.Namespace;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class CliTest {
private static final Locale DEFAULT_LOCALE = Locale.getDefault();
private final JarLocation location = mock(JarLocation.class);
private final Application<Configuration> app = new Application<Configuration>() {
@Override
public void run(Configuration configuration, Environment environment) throws Exception {
}
};
private static final class BadAppException extends Exception {
private static final long serialVersionUID = 1L;
public static final String BAD_APP_EXCEPTION_STACK_TRACE = "BadAppException stack trace";
public BadAppException() {
}
@Override
public void printStackTrace(PrintWriter writer) {
writer.println(BAD_APP_EXCEPTION_STACK_TRACE);
}
}
private final Bootstrap<Configuration> bootstrap = new Bootstrap<>(app);
private final ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
private final ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
private final CheckCommand<Configuration> command = spy(new CheckCommand<>(app));
private Cli cli;
@BeforeClass
public static void init() {
// Set default locale to English because some tests assert localized error messages
Locale.setDefault(Locale.ENGLISH);
}
@AfterClass
public static void shutdown() {
Locale.setDefault(DEFAULT_LOCALE);
}
@Before
@SuppressWarnings("unchecked")
public void setUp() throws Exception {
when(location.toString()).thenReturn("dw-thing.jar");
when(location.getVersion()).thenReturn(Optional.of("1.0.0"));
bootstrap.addCommand(command);
doNothing().when(command).run(any(Bootstrap.class), any(Namespace.class), any(Configuration.class));
this.cli = new Cli(location, bootstrap, stdOut, stdErr);
}
@Test
public void handlesShortVersionCommands() throws Exception {
assertThat(cli.run("-v"))
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format("1.0.0%n"));
assertThat(stdErr.toString())
.isEmpty();
}
@Test
public void handlesLongVersionCommands() throws Exception {
assertThat(cli.run("--version"))
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format("1.0.0%n"));
assertThat(stdErr.toString())
.isEmpty();
}
@Test
public void handlesMissingVersions() throws Exception {
when(location.getVersion()).thenReturn(Optional.empty());
final Cli newCli = new Cli(location, bootstrap, stdOut, stdErr);
assertThat(newCli.run("--version"))
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format("No application version detected. Add a Implementation-Version entry to your JAR's manifest to enable this.%n"));
assertThat(stdErr.toString())
.isEmpty();
}
@Test
public void handlesZeroArgumentsAsHelpCommand() throws Exception {
assertThat(cli.run())
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format(
"usage: java -jar dw-thing.jar [-h] [-v] {check} ...%n" +
"%n" +
"positional arguments:%n" +
" {check} available commands%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n" +
" -v, --version show the application version and exit%n"
));
assertThat(stdErr.toString())
.isEmpty();
}
@Test
public void handlesShortHelpCommands() throws Exception {
assertThat(cli.run("-h"))
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format(
"usage: java -jar dw-thing.jar [-h] [-v] {check} ...%n" +
"%n" +
"positional arguments:%n" +
" {check} available commands%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n" +
" -v, --version show the application version and exit%n"
));
assertThat(stdErr.toString())
.isEmpty();
}
@Test
public void handlesLongHelpCommands() throws Exception {
assertThat(cli.run("--help"))
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format(
"usage: java -jar dw-thing.jar [-h] [-v] {check} ...%n" +
"%n" +
"positional arguments:%n" +
" {check} available commands%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n" +
" -v, --version show the application version and exit%n"
));
assertThat(stdErr.toString())
.isEmpty();
}
@Test
@SuppressWarnings("unchecked")
public void handlesShortHelpSubcommands() throws Exception {
assertThat(cli.run("check", "-h"))
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format(
"usage: java -jar dw-thing.jar check [-h] [file]%n" +
"%n" +
"Parses and validates the configuration file%n" +
"%n" +
"positional arguments:%n" +
" file application configuration file%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n"
));
assertThat(stdErr.toString())
.isEmpty();
verify(command, never()).run(any(Bootstrap.class), any(Namespace.class), any(Configuration.class));
}
@Test
@SuppressWarnings("unchecked")
public void handlesLongHelpSubcommands() throws Exception {
assertThat(cli.run("check", "--help"))
.isTrue();
assertThat(stdOut.toString())
.isEqualTo(String.format(
"usage: java -jar dw-thing.jar check [-h] [file]%n" +
"%n" +
"Parses and validates the configuration file%n" +
"%n" +
"positional arguments:%n" +
" file application configuration file%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n"
));
assertThat(stdErr.toString())
.isEmpty();
verify(command, never()).run(any(Bootstrap.class), any(Namespace.class), any(Configuration.class));
}
@Test
public void rejectsBadCommandFlags() throws Exception {
assertThat(cli.run("--yes"))
.isFalse();
assertThat(stdOut.toString())
.isEmpty();
assertThat(stdErr.toString())
.isEqualTo(String.format(
"unrecognized arguments: '--yes'%n" +
"usage: java -jar dw-thing.jar [-h] [-v] {check} ...%n" +
"%n" +
"positional arguments:%n" +
" {check} available commands%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n" +
" -v, --version show the application version and exit%n"
));
}
@Test
public void rejectsBadSubcommandFlags() throws Exception {
assertThat(cli.run("check", "--yes"))
.isFalse();
assertThat(stdOut.toString())
.isEmpty();
assertThat(stdErr.toString())
.isEqualTo(String.format(
"unrecognized arguments: '--yes'%n" +
"usage: java -jar dw-thing.jar check [-h] [file]%n" +
"%n" +
"Parses and validates the configuration file%n" +
"%n" +
"positional arguments:%n" +
" file application configuration file%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n"
));
}
@Test
public void rejectsBadSubcommands() throws Exception {
assertThat(cli.run("plop"))
.isFalse();
assertThat(stdOut.toString())
.isEmpty();
assertThat(stdErr.toString())
.isEqualTo(String.format(
"invalid choice: 'plop' (choose from 'check')%n" +
"usage: java -jar dw-thing.jar [-h] [-v] {check} ...%n" +
"%n" +
"positional arguments:%n" +
" {check} available commands%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n" +
" -v, --version show the application version and exit%n"
));
}
@Test
public void runsCommands() throws Exception {
assertThat(cli.run("check"))
.isTrue();
assertThat(stdOut.toString())
.isEmpty();
assertThat(stdErr.toString())
.isEmpty();
verify(command).run(eq(bootstrap), any(Namespace.class), any(Configuration.class));
}
@Test
@SuppressWarnings("unchecked")
public void unhandledExceptionsAreLoggedAppropriately() throws Exception {
doThrow(new BadAppException()).when(command).run(any(Bootstrap.class), any(Namespace.class), any(Configuration.class));
assertThat(cli.run("check"))
.isFalse();
assertThat(stdOut.toString())
.isEmpty();
assertThat(stdErr.toString())
.isEqualTo(String.format("%s%n", BadAppException.BAD_APP_EXCEPTION_STACK_TRACE));
}
}