diff --git a/cypher-shell/src/integration-test/java/org/neo4j/shell/MainIntegrationTest.java b/cypher-shell/src/integration-test/java/org/neo4j/shell/MainIntegrationTest.java index 03c1a203..bb94bb4e 100644 --- a/cypher-shell/src/integration-test/java/org/neo4j/shell/MainIntegrationTest.java +++ b/cypher-shell/src/integration-test/java/org/neo4j/shell/MainIntegrationTest.java @@ -16,6 +16,7 @@ import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.exceptions.TransientException; import org.neo4j.shell.cli.CliArgs; +import org.neo4j.shell.cli.Format; import org.neo4j.shell.commands.CommandHelper; import org.neo4j.shell.exception.CommandException; import org.neo4j.shell.exception.ExitException; @@ -37,10 +38,16 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.neo4j.shell.DatabaseManager.DEFAULT_DEFAULT_DB_NAME; +import static org.neo4j.shell.DatabaseManager.SYSTEM_DB_NAME; +import static org.neo4j.shell.Main.EXIT_FAILURE; +import static org.neo4j.shell.Main.EXIT_SUCCESS; import static org.neo4j.shell.util.Versions.majorVersion; public class MainIntegrationTest { + private static String USER = "neo4j"; + private static String PASSWORD = "neo"; private static class ShellAndConnection { @@ -56,7 +63,7 @@ private static class ShellAndConnection @Rule public final ExpectedException exception = ExpectedException.none(); - private String inputString = String.format( "neo4j%nneo%n" ); + private String inputString = String.format( "%s%n%s%n", USER, PASSWORD ); private ByteArrayOutputStream baos; private ConnectionConfig connectionConfig; private CliArgs cliArgs; @@ -89,7 +96,7 @@ public void setup() { private void ensureUser() throws Exception { if (majorVersion(shell.getServerVersion() ) >= 4) { - shell.execute(":use " + DatabaseManager.SYSTEM_DB_NAME); + shell.execute(":use " + SYSTEM_DB_NAME); shell.execute("CREATE OR REPLACE USER foo SET PASSWORD 'pass';"); shell.execute("GRANT ROLE reader TO foo;"); shell.execute(":use"); @@ -134,41 +141,23 @@ public void promptsOnWrongAuthenticationIfInteractive() throws Exception { @Test public void promptsOnPasswordChangeRequired() throws Exception { - shell.setCommandHelper(new CommandHelper(mock(Logger.class), Historian.empty, shell)); - inputBuffer.put(String.format("foo%npass%nnewpass%n").getBytes()); - - assertEquals("", connectionConfig.username()); - assertEquals("", connectionConfig.password()); - - // when - main.connectMaybeInteractively(shell, connectionConfig, true, true); - - // then - // should be connected - assertTrue(shell.isConnected()); - // should have prompted and set the username and password - String expectedLoginOutput = format( "username: neo4j%npassword: ***%n" ); - assertEquals(expectedLoginOutput, baos.toString()); - assertEquals("neo4j", connectionConfig.username()); - assertEquals("neo", connectionConfig.password()); - - // Create a new user - ensureUser(); - shell.disconnect(); + int majorVersion = getVersionAndCreateUserWithPasswordChangeRequired(); connectionConfig = getConnectionConfig(cliArgs); assertEquals("", connectionConfig.username()); assertEquals("", connectionConfig.password()); // when + inputBuffer.put(String.format("foo%npass%nnewpass%n").getBytes()); + baos.reset(); main.connectMaybeInteractively(shell, connectionConfig, true, true); // then assertTrue(shell.isConnected()); - if (majorVersion(shell.getServerVersion() ) >= 4) { + if (majorVersion >= 4) { // should have prompted to change the password String expectedChangePasswordOutput = format( "username: foo%npassword: ****%nPassword change required%nnew password: *******%n" ); - assertEquals(expectedLoginOutput + expectedChangePasswordOutput, baos.toString()); + assertEquals( expectedChangePasswordOutput, baos.toString()); assertEquals("foo", connectionConfig.username()); assertEquals("newpass", connectionConfig.password()); assertNull(connectionConfig.newPassword()); @@ -178,7 +167,7 @@ public void promptsOnPasswordChangeRequired() throws Exception { } else { // in 3.x we do not get credentials expired exception on connection, but when we try to access data String expectedChangePasswordOutput = format( "username: foo%npassword: ****%n" ); - assertEquals(expectedLoginOutput + expectedChangePasswordOutput, baos.toString()); + assertEquals( expectedChangePasswordOutput, baos.toString()); assertEquals("foo", connectionConfig.username()); assertEquals("pass", connectionConfig.password()); @@ -189,6 +178,61 @@ public void promptsOnPasswordChangeRequired() throws Exception { } } + @Test + public void allowUserToUpdateExpiredPasswordInteractivelyWithoutBeingPrompted() throws Exception { + //given a user that require a password change + int majorVersion = getVersionAndCreateUserWithPasswordChangeRequired(); + + //when the user attempts a non-interactive password update + assumeTrue(majorVersion >= 4 ); + baos.reset(); + assertEquals( EXIT_SUCCESS, main.runShell( args( SYSTEM_DB_NAME, "foo", "pass", + "ALTER CURRENT USER SET PASSWORD from \"pass\" to \"pass2\";" ), shell, mock( Logger.class ) ) ); + //we shouldn't ask for a new password + assertEquals( "", baos.toString() ); + + //then the new user should be able to successfully connect, and run a command + assertEquals( format( "n%n42%n" ), + executeNonInteractively( args( DEFAULT_DEFAULT_DB_NAME, + "foo", "pass2", "RETURN 42 AS n" ) ) ); + } + + @Test + public void shouldFailIfNonInteractivelySettingPasswordOnNonSystemDb() throws Exception { + //given a user that require a password change + int majorVersion = getVersionAndCreateUserWithPasswordChangeRequired(); + + //when + assumeTrue( majorVersion >= 4 ); + + //then + assertEquals( EXIT_FAILURE, main.runShell( args( DEFAULT_DEFAULT_DB_NAME, "foo", "pass", + "ALTER CURRENT USER SET PASSWORD from \"pass\" to \"pass2\";" ), shell, mock( Logger.class ) ) ); + } + + @Test + public void shouldBePromptedIfRunningNonInteractiveCypherThatDoesntUpdatePassword() throws Exception { + //given a user that require a password change + int majorVersion = getVersionAndCreateUserWithPasswordChangeRequired(); + + //when + assumeTrue( majorVersion >= 4 ); + + //when interactively asked for a password use this + inputBuffer.put( String.format( "pass2%n" ).getBytes() ); + baos.reset(); + assertEquals( EXIT_SUCCESS, main.runShell( args( DEFAULT_DEFAULT_DB_NAME, "foo", "pass", + "MATCH (n) RETURN n" ), shell, mock( Logger.class ) ) ); + + //then should ask for a new password + assertEquals( format( "Password change required%nnew password: *****%n" ), baos.toString() ); + + //then the new user should be able to successfully connect, and run a command + assertEquals( format( "n%n42%n" ), + executeNonInteractively( args( DEFAULT_DEFAULT_DB_NAME, + "foo", "pass2", "RETURN 42 AS n" ) ) ); + } + @Test public void doesNotPromptToStdOutOnWrongAuthenticationIfOutputRedirected() throws Exception { // when @@ -296,10 +340,15 @@ public void shouldReadMultipleCypherStatementsFromFile() throws Exception { @Test public void shouldFailIfInputFileDoesntExist() throws Exception { - // expect - exception.expect( FileNotFoundException.class); - exception.expectMessage( "what.cypher (No such file or directory)" ); - executeFileNonInteractively("what.cypher"); + //given + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Logger logger = new AnsiLogger( false, Format.VERBOSE, new PrintStream( out ), new PrintStream( out )); + + //when + executeFileNonInteractively("what.cypher", logger); + + //then + assertEquals( format("what.cypher (No such file or directory)%n"), out.toString()); } @Test @@ -411,7 +460,7 @@ public void doesNotStartWhenDefaultDatabaseUnavailableIfInteractive() throws Exc assertEquals("neo", connectionConfig.password()); // Stop the default database - shell.execute(":use " + DatabaseManager.SYSTEM_DB_NAME); + shell.execute(":use " + SYSTEM_DB_NAME); shell.execute("STOP DATABASE " + DatabaseManager.DEFAULT_DEFAULT_DB_NAME); try { @@ -453,7 +502,7 @@ public void startsAgainstSystemDatabaseWhenDefaultDatabaseUnavailableIfInteracti assertEquals("neo", connectionConfig.password()); // Stop the default database - shell.execute(":use " + DatabaseManager.SYSTEM_DB_NAME); + shell.execute(":use " + SYSTEM_DB_NAME); shell.execute("STOP DATABASE " + DatabaseManager.DEFAULT_DEFAULT_DB_NAME); try { @@ -502,7 +551,7 @@ public void switchingToUnavailableDatabaseIfInteractive() throws Exception { assertEquals("neo", connectionConfig.password()); // Stop the default database - shell.execute(":use " + DatabaseManager.SYSTEM_DB_NAME); + shell.execute(":use " + SYSTEM_DB_NAME); shell.execute("STOP DATABASE " + DatabaseManager.DEFAULT_DEFAULT_DB_NAME); try { @@ -540,7 +589,7 @@ public void switchingToUnavailableDefaultDatabaseIfInteractive() throws Exceptio assertEquals("neo", connectionConfig.password()); // Stop the default database - shell.execute(":use " + DatabaseManager.SYSTEM_DB_NAME); + shell.execute(":use " + SYSTEM_DB_NAME); shell.execute("STOP DATABASE " + DatabaseManager.DEFAULT_DEFAULT_DB_NAME); try { @@ -554,23 +603,29 @@ public void switchingToUnavailableDefaultDatabaseIfInteractive() throws Exceptio } } - private String executeFileNonInteractively(String filename) throws Exception { + private String executeFileNonInteractively(String filename) { return executeFileNonInteractively(filename, mock(Logger.class)); } - private String executeFileNonInteractively(String filename, Logger logger) throws Exception - { + private String executeFileNonInteractively(String filename, Logger logger) { CliArgs cliArgs = new CliArgs(); + cliArgs.setUsername( USER, "" ); + cliArgs.setPassword( PASSWORD, "" ); cliArgs.setInputFilename(filename); + return executeNonInteractively( cliArgs, logger ); + } + + private String executeNonInteractively(CliArgs cliArgs) { + return executeNonInteractively(cliArgs, mock(Logger.class)); + } + + private String executeNonInteractively(CliArgs cliArgs, Logger logger) + { ToStringLinePrinter linePrinter = new ToStringLinePrinter(); ShellAndConnection sac = getShell( cliArgs, linePrinter ); CypherShell shell = sac.shell; - ConnectionConfig connectionConfig = sac.connectionConfig; - main.connectMaybeInteractively( shell, connectionConfig, true, true ); - ShellRunner shellRunner = ShellRunner.getShellRunner(cliArgs, shell, logger, connectionConfig); - shellRunner.runUntilEnd(); - + main.runShell(cliArgs, shell, logger); return linePrinter.result(); } @@ -626,4 +681,26 @@ private void exit( CypherShell shell ) throws CommandException //do nothing } } + + private CliArgs args(String db, String user, String pass, String cypher) + { + CliArgs cliArgs = new CliArgs(); + cliArgs.setUsername( user, "" ); + cliArgs.setPassword( pass, "" ); + cliArgs.setDatabase( db ); + cliArgs.setCypher( cypher ); + return cliArgs; + } + + private int getVersionAndCreateUserWithPasswordChangeRequired() throws Exception { + shell.setCommandHelper( new CommandHelper( mock( Logger.class ), Historian.empty, shell ) ); + + main.connectMaybeInteractively( shell, connectionConfig, true, true ); + String expectedLoginOutput = format( "username: neo4j%npassword: ***%n" ); + assertEquals( expectedLoginOutput, baos.toString() ); + ensureUser(); + int majorVersion = majorVersion( shell.getServerVersion() ); + shell.disconnect(); + return majorVersion; + } } diff --git a/cypher-shell/src/main/java/org/neo4j/shell/ConnectionConfig.java b/cypher-shell/src/main/java/org/neo4j/shell/ConnectionConfig.java index 9c937834..6ac8b977 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/ConnectionConfig.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/ConnectionConfig.java @@ -95,7 +95,7 @@ public void setPassword(@Nonnull String password) { this.password = password; } - public void setNewPassword(@Nonnull String password) { + public void setNewPassword( String password) { this.newPassword = password; } diff --git a/cypher-shell/src/main/java/org/neo4j/shell/Connector.java b/cypher-shell/src/main/java/org/neo4j/shell/Connector.java index 0355ff95..121e5e5e 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/Connector.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/Connector.java @@ -1,9 +1,10 @@ package org.neo4j.shell; -import org.neo4j.shell.exception.CommandException; - import javax.annotation.Nonnull; +import org.neo4j.function.ThrowingAction; +import org.neo4j.shell.exception.CommandException; + /** * An object with the ability to connect and disconnect. */ @@ -18,7 +19,15 @@ public interface Connector { * * @throws CommandException if connection failed */ - void connect(@Nonnull ConnectionConfig connectionConfig) throws CommandException; + default void connect(@Nonnull ConnectionConfig connectionConfig) throws CommandException { + connect( connectionConfig, null ); + } + + /** + * + * @throws CommandException if connection failed + */ + void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction action) throws CommandException; /** * Returns the version of Neo4j which the shell is connected to. If the version is before 3.1.0-M09, or we are not diff --git a/cypher-shell/src/main/java/org/neo4j/shell/CypherShell.java b/cypher-shell/src/main/java/org/neo4j/shell/CypherShell.java index 1863fe8d..d1f69c82 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/CypherShell.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/CypherShell.java @@ -1,8 +1,15 @@ package org.neo4j.shell; +import java.util.List; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; + import org.neo4j.driver.exceptions.DiscoveryException; import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.exceptions.ServiceUnavailableException; +import org.neo4j.function.ThrowingAction; import org.neo4j.shell.commands.Command; import org.neo4j.shell.commands.CommandExecutable; import org.neo4j.shell.commands.CommandHelper; @@ -14,12 +21,6 @@ import org.neo4j.shell.state.BoltResult; import org.neo4j.shell.state.BoltStateHandler; -import javax.annotation.Nonnull; -import java.util.List; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * A possibly interactive shell for evaluating cypher statements. */ @@ -137,10 +138,12 @@ protected void executeCmd(@Nonnull final CommandExecutable cmdExe) throws ExitEx * Open a session to Neo4j * * @param connectionConfig + * @param command */ @Override - public void connect(@Nonnull ConnectionConfig connectionConfig) throws CommandException { - boltStateHandler.connect(connectionConfig); + public void connect( @Nonnull ConnectionConfig connectionConfig, + ThrowingAction command) throws CommandException { + boltStateHandler.connect(connectionConfig, command ); } @Nonnull diff --git a/cypher-shell/src/main/java/org/neo4j/shell/Main.java b/cypher-shell/src/main/java/org/neo4j/shell/Main.java index bb247df1..44b36d1f 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/Main.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/Main.java @@ -10,6 +10,7 @@ import org.neo4j.driver.exceptions.AuthenticationException; import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.function.ThrowingAction; import org.neo4j.shell.build.Build; import org.neo4j.shell.cli.CliArgHelper; import org.neo4j.shell.cli.CliArgs; @@ -24,6 +25,8 @@ public class Main { static final String NEO_CLIENT_ERROR_SECURITY_UNAUTHORIZED = "Neo.ClientError.Security.Unauthorized"; + public static final int EXIT_FAILURE = 1; + public static final int EXIT_SUCCESS = 0; private final InputStream in; private final PrintStream out; private final boolean hasSpecialInteractiveOutputStream; @@ -75,8 +78,17 @@ void startShell(@Nonnull CliArgs cliArgs) { if (cliArgs.getVersion() || cliArgs.getDriverVersion()) { return; } - Logger logger = new AnsiLogger(cliArgs.getDebugMode()); - PrettyConfig prettyConfig = new PrettyConfig(cliArgs); + Logger logger = new AnsiLogger( cliArgs.getDebugMode() ); + PrettyConfig prettyConfig = new PrettyConfig( cliArgs ); + + CypherShell shell = new CypherShell( logger, prettyConfig, ShellRunner.shouldBeInteractive( cliArgs ), + cliArgs.getParameters() ); + int exitCode = runShell( cliArgs, shell, logger ); + System.exit( exitCode ); + } + + int runShell(@Nonnull CliArgs cliArgs, @Nonnull CypherShell shell, Logger logger ) + { ConnectionConfig connectionConfig = new ConnectionConfig( cliArgs.getScheme(), cliArgs.getHost(), @@ -84,35 +96,53 @@ void startShell(@Nonnull CliArgs cliArgs) { cliArgs.getUsername(), cliArgs.getPassword(), cliArgs.getEncryption(), - cliArgs.getDatabase()); - - try { - CypherShell shell = new CypherShell(logger, prettyConfig, ShellRunner.shouldBeInteractive( cliArgs ), cliArgs.getParameters()); - // Can only prompt for password if input has not been redirected - connectMaybeInteractively(shell, connectionConfig, isInputInteractive(), isOutputInteractive()); - - // Construct shellrunner after connecting, due to interrupt handling - ShellRunner shellRunner = ShellRunner.getShellRunner(cliArgs, shell, logger, connectionConfig); - - CommandHelper commandHelper = new CommandHelper(logger, shellRunner.getHistorian(), shell); + cliArgs.getDatabase() ); + try + { + //If user is passing in a cypher statement just run that and be done with it + if ( cliArgs.getCypher().isPresent() ) + { + // Can only prompt for password if input has not been redirected + connectMaybeInteractively( shell, connectionConfig, isInputInteractive(), isOutputInteractive(), + () -> shell.execute( cliArgs.getCypher().get() ) ); + return EXIT_SUCCESS; + } + else + { + // Can only prompt for password if input has not been redirected + connectMaybeInteractively( shell, connectionConfig, isInputInteractive(), isOutputInteractive()); + // Construct shellrunner after connecting, due to interrupt handling + ShellRunner shellRunner = ShellRunner.getShellRunner( cliArgs, shell, logger, connectionConfig ); + CommandHelper commandHelper = new CommandHelper( logger, shellRunner.getHistorian(), shell ); - shell.setCommandHelper(commandHelper); + shell.setCommandHelper( commandHelper ); - int code = shellRunner.runUntilEnd(); - System.exit(code); - } catch (Throwable e) { - logger.printError(e); - System.exit(1); + return shellRunner.runUntilEnd(); + } } + catch ( Throwable e ) + { + logger.printError( e ); + return EXIT_FAILURE; + } + } + + void connectMaybeInteractively(@Nonnull CypherShell shell, + @Nonnull ConnectionConfig connectionConfig, + boolean inputInteractive, + boolean outputInteractive) + throws Exception { + connectMaybeInteractively( shell, connectionConfig, inputInteractive, outputInteractive, null ); } /** * Connect the shell to the server, and try to handle missing passwords and such */ - void connectMaybeInteractively(@Nonnull CypherShell shell, - @Nonnull ConnectionConfig connectionConfig, - boolean inputInteractive, - boolean outputInteractive) + private void connectMaybeInteractively( @Nonnull CypherShell shell, + @Nonnull ConnectionConfig connectionConfig, + boolean inputInteractive, + boolean outputInteractive, + ThrowingAction command ) throws Exception { boolean didPrompt = false; @@ -126,7 +156,7 @@ void connectMaybeInteractively(@Nonnull CypherShell shell, while (true) { try { // Try to connect - shell.connect(connectionConfig); + shell.connect(connectionConfig, command); // If no exception occurred we are done break; diff --git a/cypher-shell/src/main/java/org/neo4j/shell/ShellRunner.java b/cypher-shell/src/main/java/org/neo4j/shell/ShellRunner.java index d7da5827..539904a3 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/ShellRunner.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/ShellRunner.java @@ -3,15 +3,6 @@ import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.io.output.WriterOutputStream; -import org.neo4j.shell.cli.CliArgs; -import org.neo4j.shell.cli.FileHistorian; -import org.neo4j.shell.cli.InteractiveShellRunner; -import org.neo4j.shell.cli.NonInteractiveShellRunner; -import org.neo4j.shell.cli.StringShellRunner; -import org.neo4j.shell.log.Logger; -import org.neo4j.shell.parser.ShellStatementParser; - -import javax.annotation.Nonnull; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -21,6 +12,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; +import javax.annotation.Nonnull; + +import org.neo4j.shell.cli.CliArgs; +import org.neo4j.shell.cli.FileHistorian; +import org.neo4j.shell.cli.InteractiveShellRunner; +import org.neo4j.shell.cli.NonInteractiveShellRunner; +import org.neo4j.shell.log.Logger; +import org.neo4j.shell.parser.ShellStatementParser; import static org.fusesource.jansi.internal.CLibrary.STDIN_FILENO; import static org.fusesource.jansi.internal.CLibrary.STDOUT_FILENO; @@ -57,9 +56,7 @@ static ShellRunner getShellRunner(@Nonnull CliArgs cliArgs, @Nonnull CypherShell cypherShell, @Nonnull Logger logger, @Nonnull ConnectionConfig connectionConfig) throws IOException { - if (cliArgs.getCypher().isPresent()) { - return new StringShellRunner(cliArgs, cypherShell, logger); - } else if (shouldBeInteractive(cliArgs)) { + if (shouldBeInteractive(cliArgs)) { UserMessagesHandler userMessagesHandler = new UserMessagesHandler(connectionConfig, cypherShell.getServerVersion()); return new InteractiveShellRunner(cypherShell, cypherShell, cypherShell, logger, new ShellStatementParser(), diff --git a/cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgHelper.java b/cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgHelper.java index 4688a442..7deefd37 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgHelper.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgHelper.java @@ -211,7 +211,7 @@ private static ArgumentParser setupParser(ParameterMap parameterMap) .setDefault(CliArgs.DEFAULT_NUM_SAMPLE_ROWS); parser.addArgument("--wrap") - .help("wrap table colum values if column is too narrow (only for format=VERBOSE)") + .help("wrap table column values if column is too narrow (only for format=VERBOSE)") .type(new BooleanArgumentType()) .setDefault(true); diff --git a/cypher-shell/src/main/java/org/neo4j/shell/cli/InteractiveShellRunner.java b/cypher-shell/src/main/java/org/neo4j/shell/cli/InteractiveShellRunner.java index 8553286d..9aea56c9 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/cli/InteractiveShellRunner.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/cli/InteractiveShellRunner.java @@ -1,10 +1,20 @@ package org.neo4j.shell.cli; import jline.console.ConsoleReader; +import sun.misc.Signal; +import sun.misc.SignalHandler; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.Nonnull; import org.neo4j.shell.ConnectionConfig; import org.neo4j.shell.DatabaseManager; import org.neo4j.shell.Historian; +import org.neo4j.shell.Main; import org.neo4j.shell.ShellRunner; import org.neo4j.shell.StatementExecuter; import org.neo4j.shell.TransactionHandler; @@ -16,15 +26,6 @@ import org.neo4j.shell.log.Logger; import org.neo4j.shell.parser.StatementParser; import org.neo4j.shell.prettyprint.OutputFormatter; -import sun.misc.Signal; -import sun.misc.SignalHandler; - -import javax.annotation.Nonnull; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; import static org.neo4j.shell.DatabaseManager.ABSENT_DB_NAME; import static org.neo4j.shell.DatabaseManager.DATABASE_UNAVAILABLE_ERROR_CODE; @@ -94,7 +95,7 @@ private ConsoleReader setupConsoleReader(@Nonnull Logger logger, @Override public int runUntilEnd() { - int exitCode = 0; + int exitCode = Main.EXIT_SUCCESS; boolean running = true; logger.printIfVerbose(userMessagesHandler.getWelcomeMessage()); diff --git a/cypher-shell/src/main/java/org/neo4j/shell/cli/NonInteractiveShellRunner.java b/cypher-shell/src/main/java/org/neo4j/shell/cli/NonInteractiveShellRunner.java index fb11a530..44f540cf 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/cli/NonInteractiveShellRunner.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/cli/NonInteractiveShellRunner.java @@ -1,5 +1,11 @@ package org.neo4j.shell.cli; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import javax.annotation.Nonnull; + import org.neo4j.shell.Historian; import org.neo4j.shell.ShellRunner; import org.neo4j.shell.StatementExecuter; @@ -7,11 +13,8 @@ import org.neo4j.shell.log.Logger; import org.neo4j.shell.parser.StatementParser; -import javax.annotation.Nonnull; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.List; +import static org.neo4j.shell.Main.EXIT_FAILURE; +import static org.neo4j.shell.Main.EXIT_SUCCESS; /** @@ -52,7 +55,7 @@ public int runUntilEnd() { return 1; } - int exitCode = 0; + int exitCode = EXIT_SUCCESS; for (String statement : statements) { try { executer.execute(statement); @@ -60,7 +63,7 @@ public int runUntilEnd() { // These exceptions are always fatal return e.getCode(); } catch (Throwable e) { - exitCode = 1; + exitCode = EXIT_FAILURE; logger.printError(e); if (FailBehavior.FAIL_AT_END != failBehavior) { return exitCode; diff --git a/cypher-shell/src/main/java/org/neo4j/shell/cli/StringShellRunner.java b/cypher-shell/src/main/java/org/neo4j/shell/cli/StringShellRunner.java deleted file mode 100644 index be9bbf35..00000000 --- a/cypher-shell/src/main/java/org/neo4j/shell/cli/StringShellRunner.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.neo4j.shell.cli; - -import org.neo4j.shell.Historian; -import org.neo4j.shell.ShellRunner; -import org.neo4j.shell.StatementExecuter; -import org.neo4j.shell.log.Logger; - -import javax.annotation.Nonnull; -import java.util.Optional; - -/** - * A shell runner which executes a single String and exits afterward. Any errors will throw immediately. - */ -public class StringShellRunner implements ShellRunner { - private final String cypher; - private final Logger logger; - private final StatementExecuter executer; - - public StringShellRunner(@Nonnull CliArgs cliArgs, - @Nonnull StatementExecuter executer, - @Nonnull Logger logger) { - this.executer = executer; - this.logger = logger; - if (cliArgs.isStringShell()) { - this.cypher = cliArgs.getCypher().get(); - } else { - throw new NullPointerException("No cypher string specified"); - } - } - - @Override - public int runUntilEnd() { - int exitCode = 0; - try { - executer.execute(cypher.trim()); - } catch (Throwable t) { - logger.printError(t); - exitCode = 1; - } - return exitCode; - } - - @Nonnull - @Override - public Historian getHistorian() { - return Historian.empty; - } -} diff --git a/cypher-shell/src/main/java/org/neo4j/shell/commands/Exit.java b/cypher-shell/src/main/java/org/neo4j/shell/commands/Exit.java index 26437df8..c4daa61b 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/commands/Exit.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/commands/Exit.java @@ -1,14 +1,15 @@ package org.neo4j.shell.commands; +import java.util.Arrays; +import java.util.List; +import javax.annotation.Nonnull; + import org.neo4j.shell.exception.CommandException; import org.neo4j.shell.exception.ExitException; import org.neo4j.shell.log.AnsiFormattedText; import org.neo4j.shell.log.Logger; -import javax.annotation.Nonnull; -import java.util.Arrays; -import java.util.List; - +import static org.neo4j.shell.Main.EXIT_SUCCESS; import static org.neo4j.shell.commands.CommandHelper.simpleArgParse; /** @@ -57,6 +58,6 @@ public List getAliases() { public void execute(@Nonnull final String argString) throws ExitException, CommandException { simpleArgParse(argString, 0, COMMAND_NAME, getUsage()); - throw new ExitException(0); + throw new ExitException(EXIT_SUCCESS); } } diff --git a/cypher-shell/src/main/java/org/neo4j/shell/state/BoltStateHandler.java b/cypher-shell/src/main/java/org/neo4j/shell/state/BoltStateHandler.java index 75bb930f..5511c16f 100644 --- a/cypher-shell/src/main/java/org/neo4j/shell/state/BoltStateHandler.java +++ b/cypher-shell/src/main/java/org/neo4j/shell/state/BoltStateHandler.java @@ -28,6 +28,7 @@ import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.summary.DatabaseInfo; import org.neo4j.driver.summary.ResultSummary; +import org.neo4j.function.ThrowingAction; import org.neo4j.shell.ConnectionConfig; import org.neo4j.shell.Connector; import org.neo4j.shell.DatabaseManager; @@ -148,17 +149,15 @@ public boolean isConnected() { } @Override - public void connect(@Nonnull ConnectionConfig connectionConfig) throws CommandException { + public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction command) throws CommandException { if (isConnected()) { throw new CommandException("Already connected"); } - final AuthToken authToken = AuthTokens.basic(connectionConfig.username(), connectionConfig.password()); - try { setActiveDatabase(connectionConfig.database()); driver = getDriver(connectionConfig, authToken); - reconnect(); + reconnect(command); } catch (Throwable t) { try { silentDisconnect(); @@ -169,42 +168,62 @@ public void connect(@Nonnull ConnectionConfig connectionConfig) throws CommandEx } } - private void reconnect() { - reconnect(true); + private void reconnect() throws CommandException { + reconnect(true, null); + } + + private void reconnect(ThrowingAction command) throws CommandException { + reconnect(true, command); } - private void reconnect(boolean keepBookmark) { + private void reconnect(boolean keepBokmark) throws CommandException { + reconnect(keepBokmark, null); + } + + private void reconnect( boolean keepBookmark, ThrowingAction command ) throws CommandException { SessionConfig.Builder builder = SessionConfig.builder(); - builder.withDefaultAccessMode(AccessMode.WRITE); - if (!ABSENT_DB_NAME.equals(activeDatabaseNameAsSetByUser)) { + builder.withDefaultAccessMode( AccessMode.WRITE ); + if ( !ABSENT_DB_NAME.equals( activeDatabaseNameAsSetByUser ) ) + { builder.withDatabase( activeDatabaseNameAsSetByUser ); } - if (session != null && keepBookmark) { + if ( session != null && keepBookmark ) + { // Save the last bookmark and close the session final Bookmark bookmark = session.lastBookmark(); session.close(); - builder.withBookmarks(bookmark); - } else if (systemBookmark != null) { - builder.withBookmarks(systemBookmark); + builder.withBookmarks( bookmark ); + } + else if ( systemBookmark != null ) + { + builder.withBookmarks( systemBookmark ); } - session = driver.session(builder.build()); + session = driver.session( builder.build() ); - String query = activeDatabaseNameAsSetByUser.compareToIgnoreCase(SYSTEM_DB_NAME) == 0 ? "CALL db.indexes()" : "RETURN 1"; + ThrowingAction action = command != null ? command : getPing(); resetActualDbName(); // Set this to null first in case run throws an exception - Result run = session.run(query); - ResultSummary summary = null; - try { - summary = run.consume(); - } finally { - // Since run.consume() can throw the first time we have to go through this extra hoop to get the summary - if (summary == null) { + action.apply(); + } + + private ThrowingAction getPing() { + String query = + activeDatabaseNameAsSetByUser.compareToIgnoreCase(SYSTEM_DB_NAME) == 0 ? "CALL db.indexes()" : "RETURN 1"; + return () -> { + Result run = session.run(query.trim()); + ResultSummary summary = null; + try { summary = run.consume(); + } finally { + // Since run.consume() can throw the first time we have to go through this extra hoop to get the summary + if (summary == null) { + summary = run.consume(); + } + BoltStateHandler.this.version = summary.server().version(); + updateActualDbName(summary); } - this.version = summary.server().version(); - updateActualDbName(summary); - } + }; } @Nonnull @@ -251,7 +270,8 @@ public void updateActualDbName(@Nonnull ResultSummary resultSummary) { actualDatabaseNameAsReportedByServer = getActualDbName(resultSummary); } - public void changePassword(@Nonnull ConnectionConfig connectionConfig) { + public void changePassword( @Nonnull ConnectionConfig connectionConfig ) + { if (!connectionConfig.passwordChangeRequired()) { return; } diff --git a/cypher-shell/src/test/java/org/neo4j/shell/CypherShellTest.java b/cypher-shell/src/test/java/org/neo4j/shell/CypherShellTest.java index 5c49e0b7..ead163e3 100644 --- a/cypher-shell/src/test/java/org/neo4j/shell/CypherShellTest.java +++ b/cypher-shell/src/test/java/org/neo4j/shell/CypherShellTest.java @@ -5,15 +5,14 @@ import org.junit.Test; import org.junit.rules.ExpectedException; +import java.util.Optional; + import org.neo4j.cypher.internal.evaluator.EvaluationException; import org.neo4j.driver.Driver; import org.neo4j.driver.Record; import org.neo4j.driver.Session; import org.neo4j.driver.Value; import org.neo4j.driver.summary.ResultSummary; -import org.neo4j.shell.cli.CliArgHelper; -import org.neo4j.shell.cli.CliArgs; -import org.neo4j.shell.cli.StringShellRunner; import org.neo4j.shell.commands.CommandExecutable; import org.neo4j.shell.commands.CommandHelper; import org.neo4j.shell.exception.CommandException; @@ -24,17 +23,20 @@ import org.neo4j.shell.state.BoltStateHandler; import org.neo4j.shell.state.ListBoltResult; -import java.io.IOException; -import java.util.Optional; - import static java.util.Arrays.asList; import static junit.framework.TestCase.assertTrue; -import static junit.framework.TestCase.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyObject; +import static org.mockito.Mockito.contains; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.neo4j.shell.DatabaseManager.ABSENT_DB_NAME; @SuppressWarnings("OptionalGetWithoutIsPresent") @@ -64,7 +66,7 @@ public void verifyDelegationOfConnectionMethods() throws CommandException { CypherShell shell = new CypherShell(logger, mockedBoltStateHandler, mockedPrettyPrinter, new ShellParameterMap()); shell.connect(cc); - verify(mockedBoltStateHandler).connect(cc); + verify(mockedBoltStateHandler).connect(cc, null); shell.isConnected(); verify(mockedBoltStateHandler).isConnected(); @@ -254,18 +256,6 @@ public void shouldReturnNothingOnStrangeCommand() { assertFalse(exe.isPresent()); } - @Test - public void specifyingACypherStringShouldGiveAStringRunner() throws IOException { - CliArgs cliArgs = CliArgHelper.parse("MATCH (n) RETURN n"); - - ConnectionConfig connectionConfig = mock(ConnectionConfig.class); - - ShellRunner shellRunner = ShellRunner.getShellRunner(cliArgs, offlineTestShell, logger, connectionConfig); - - if (!(shellRunner instanceof StringShellRunner)) { - fail("Expected a different runner than: " + shellRunner.getClass().getSimpleName()); - } - } @Test public void setParameterDoesNotTriggerByBoltError() throws EvaluationException, CommandException { diff --git a/cypher-shell/src/test/java/org/neo4j/shell/MainTest.java b/cypher-shell/src/test/java/org/neo4j/shell/MainTest.java index 564639d9..38ad70b8 100644 --- a/cypher-shell/src/test/java/org/neo4j/shell/MainTest.java +++ b/cypher-shell/src/test/java/org/neo4j/shell/MainTest.java @@ -57,30 +57,30 @@ public void nonEndedStringFails() throws Exception { String inputString = "no newline"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); - doThrow(authException).when(shell).connect(connectionConfig); + doThrow(authException).when(shell).connect(connectionConfig, null); thrown.expectMessage("No text could be read, exiting"); Main main = new Main(inputStream, out); main.connectMaybeInteractively(shell, connectionConfig, true, true); - verify(shell, times(1)).connect(connectionConfig); + verify(shell, times(1)).connect(connectionConfig, null); } @Test public void unrelatedErrorDoesNotPrompt() throws Exception { - doThrow(new RuntimeException("bla")).when(shell).connect(connectionConfig); + doThrow(new RuntimeException("bla")).when(shell).connect(connectionConfig, null); thrown.expect(RuntimeException.class); thrown.expectMessage("bla"); Main main = new Main(mock(InputStream.class), out); main.connectMaybeInteractively(shell, connectionConfig, true, true); - verify(shell, times(1)).connect(connectionConfig); + verify(shell, times(1)).connect(connectionConfig, null); } @Test public void promptsForUsernameAndPasswordIfNoneGivenIfInteractive() throws Exception { - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); String inputString = "bob\nsecret\n"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); @@ -96,7 +96,7 @@ public void promptsForUsernameAndPasswordIfNoneGivenIfInteractive() throws Excep assertEquals(String.format( "username: bob%npassword: ******%n" ), out); verify(connectionConfig).setUsername("bob"); verify(connectionConfig).setPassword("secret"); - verify(shell, times(2)).connect(connectionConfig); + verify(shell, times(2)).connect(connectionConfig, null); } @Test @@ -106,7 +106,7 @@ public void promptsSilentlyForUsernameAndPasswordIfNoneGivenIfOutputRedirected() return; } - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); doReturn("").doReturn("secret").when(connectionConfig).password(); String inputString = "bob\nsecret\n"; @@ -130,7 +130,7 @@ public void promptsSilentlyForUsernameAndPasswordIfNoneGivenIfOutputRedirected() assertEquals("", out); verify(connectionConfig).setUsername("bob"); verify(connectionConfig).setPassword("secret"); - verify(shell, times(2)).connect(connectionConfig); + verify(shell, times(2)).connect(connectionConfig, null); } finally { System.setIn(stdIn); System.setOut(stdOut); @@ -139,7 +139,7 @@ public void promptsSilentlyForUsernameAndPasswordIfNoneGivenIfOutputRedirected() @Test public void doesNotPromptIfInputRedirected() throws Exception { - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); String inputString = "bob\nsecret\n"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); @@ -153,13 +153,13 @@ public void doesNotPromptIfInputRedirected() throws Exception { main.connectMaybeInteractively(shell, connectionConfig, false, true); fail("Expected auth exception"); } catch (AuthenticationException e) { - verify(shell, times(1)).connect(connectionConfig); + verify(shell, times(1)).connect(connectionConfig, null); } } @Test public void promptsForUserIfPassExistsIfInteractive() throws Exception { - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); doReturn("secret").when(connectionConfig).password(); String inputString = "bob\n"; @@ -175,7 +175,7 @@ public void promptsForUserIfPassExistsIfInteractive() throws Exception { assertEquals(out, String.format( "username: bob%n" )); verify(connectionConfig).setUsername("bob"); - verify(shell, times(2)).connect(connectionConfig); + verify(shell, times(2)).connect(connectionConfig, null); } @Test @@ -185,7 +185,7 @@ public void promptsSilentlyForUserIfPassExistsIfOutputRedirected() throws Except return; } - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); doReturn("secret").when(connectionConfig).password(); String inputString = "bob\n"; @@ -208,7 +208,7 @@ public void promptsSilentlyForUserIfPassExistsIfOutputRedirected() throws Except assertEquals(out, ""); verify(connectionConfig).setUsername("bob"); - verify(shell, times(2)).connect(connectionConfig); + verify(shell, times(2)).connect(connectionConfig, null); } finally { System.setIn(stdIn); System.setOut(stdOut); @@ -232,7 +232,7 @@ public void promptsForPassBeforeConnectIfUserExistsIfInteractive() throws Except assertEquals(out, String.format("password: ******%n")); verify(connectionConfig).setPassword("secret"); - verify(shell, times(1)).connect(connectionConfig); + verify(shell, times(1)).connect(connectionConfig, null); } @Test @@ -265,7 +265,7 @@ public void promptsSilentlyForPassIfUserExistsIfOutputRedirected() throws Except assertEquals(out, ""); verify(connectionConfig).setPassword("secret"); - verify(shell, times(1)).connect(connectionConfig); + verify(shell, times(1)).connect(connectionConfig, null); } finally { System.setIn(stdIn); System.setOut(stdOut); @@ -276,7 +276,7 @@ public void promptsSilentlyForPassIfUserExistsIfOutputRedirected() throws Except public void promptsForNewPasswordIfPasswordChangeRequired() throws Exception { // Use a real ConnectionConfig instead of the mock in this test ConnectionConfig connectionConfig = new ConnectionConfig("", "", 0, "", "", false, ""); - doThrow(authException).doThrow(passwordChangeRequiredException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doThrow(passwordChangeRequiredException).doNothing().when(shell).connect(connectionConfig, null); String inputString = "bob\nsecret\nnewsecret\n"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); @@ -293,7 +293,7 @@ public void promptsForNewPasswordIfPasswordChangeRequired() throws Exception { assertEquals("bob", connectionConfig.username()); assertEquals("secret", connectionConfig.password()); assertEquals("newsecret", connectionConfig.newPassword()); - verify(shell, times(3)).connect(connectionConfig); + verify(shell, times(3)).connect(connectionConfig, null); verify(shell).changePassword(connectionConfig); } @@ -301,7 +301,7 @@ public void promptsForNewPasswordIfPasswordChangeRequired() throws Exception { public void promptsForNewPasswordIfPasswordChangeRequiredCannotBeEmpty() throws Exception { // Use a real ConnectionConfig instead of the mock in this test ConnectionConfig connectionConfig = new ConnectionConfig("", "", 0, "", "", false, ""); - doThrow(authException).doThrow(passwordChangeRequiredException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doThrow(passwordChangeRequiredException).doNothing().when(shell).connect(connectionConfig, null); String inputString = "bob\nsecret\n\nnewsecret\n"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); @@ -318,13 +318,13 @@ public void promptsForNewPasswordIfPasswordChangeRequiredCannotBeEmpty() throws assertEquals("bob", connectionConfig.username()); assertEquals("secret", connectionConfig.password()); assertEquals("newsecret", connectionConfig.newPassword()); - verify(shell, times(3)).connect(connectionConfig); + verify(shell, times(3)).connect(connectionConfig, null); verify(shell).changePassword(connectionConfig); } @Test public void promptsHandlesBang() throws Exception { - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); String inputString = "bo!b\nsec!ret\n"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); @@ -340,12 +340,12 @@ public void promptsHandlesBang() throws Exception { assertEquals(String.format("username: bo!b%npassword: *******%n"), out); verify(connectionConfig).setUsername("bo!b"); verify(connectionConfig).setPassword("sec!ret"); - verify(shell, times(2)).connect(connectionConfig); + verify(shell, times(2)).connect(connectionConfig, null); } @Test public void triesOnlyOnceIfUserPassExists() throws Exception { - doThrow(authException).doThrow(new RuntimeException("second try")).when(shell).connect(connectionConfig); + doThrow(authException).doThrow(new RuntimeException("second try")).when(shell).connect(connectionConfig, null); doReturn("bob").when(connectionConfig).username(); doReturn("secret").when(connectionConfig).password(); @@ -361,13 +361,13 @@ public void triesOnlyOnceIfUserPassExists() throws Exception { fail("Expected an exception"); } catch (Neo4jException e) { assertEquals(authException.code(), e.code()); - verify(shell, times(1)).connect(connectionConfig); + verify(shell, times(1)).connect(connectionConfig, null); } } @Test public void repromptsIfUserIsNotProvidedIfInteractive() throws Exception { - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); String inputString = "\nbob\nsecret\n"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); @@ -383,7 +383,7 @@ public void repromptsIfUserIsNotProvidedIfInteractive() throws Exception { assertEquals(String.format( "username: %nusername cannot be empty%n%nusername: bob%npassword: ******%n"), out ); verify(connectionConfig).setUsername("bob"); verify(connectionConfig).setPassword("secret"); - verify(shell, times(2)).connect(connectionConfig); + verify(shell, times(2)).connect(connectionConfig, null); } @Test @@ -393,7 +393,7 @@ public void doesNotRepromptIfUserIsNotProvidedIfOutputRedirected() throws Except return; } - doThrow(authException).doNothing().when(shell).connect(connectionConfig); + doThrow(authException).doNothing().when(shell).connect(connectionConfig, null); String inputString = "\nsecret\n"; InputStream inputStream = new ByteArrayInputStream(inputString.getBytes()); @@ -416,7 +416,7 @@ public void doesNotRepromptIfUserIsNotProvidedIfOutputRedirected() throws Except assertEquals("", out ); verify(connectionConfig).setUsername(""); verify(connectionConfig).setPassword("secret"); - verify(shell, times(2)).connect(connectionConfig); + verify(shell, times(2)).connect(connectionConfig, null); } finally { System.setIn(stdIn); System.setOut(stdOut); diff --git a/cypher-shell/src/test/java/org/neo4j/shell/cli/StringShellRunnerTest.java b/cypher-shell/src/test/java/org/neo4j/shell/cli/StringShellRunnerTest.java deleted file mode 100644 index 2369e327..00000000 --- a/cypher-shell/src/test/java/org/neo4j/shell/cli/StringShellRunnerTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.neo4j.shell.cli; - - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.shell.Historian; -import org.neo4j.shell.StatementExecuter; -import org.neo4j.shell.exception.CommandException; -import org.neo4j.shell.log.Logger; - -import java.io.IOException; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.neo4j.shell.cli.CliArgHelper.parse; - -public class StringShellRunnerTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Logger logger = mock(Logger.class); - private StatementExecuter statementExecuter = mock(StatementExecuter.class); - - @Test - public void nullCypherShouldThrowException() throws IOException { - thrown.expect(NullPointerException.class); - thrown.expectMessage("No cypher string specified"); - - new StringShellRunner(new CliArgs(), statementExecuter, logger); - } - - @Test - public void cypherShouldBePassedToRun() throws IOException, CommandException { - String cypherString = "nonsense string"; - StringShellRunner runner = new StringShellRunner(parse(cypherString), statementExecuter, logger); - - int code = runner.runUntilEnd(); - - assertEquals("Wrong exit code", 0, code); - verify(statementExecuter).execute("nonsense string"); - verifyNoMoreInteractions(statementExecuter); - } - - @Test - public void errorsShouldThrow() throws IOException, CommandException { - ClientException kaboom = new ClientException("Error kaboom"); - doThrow(kaboom).when(statementExecuter).execute(anyString()); - - StringShellRunner runner = new StringShellRunner(parse("nan anana"), statementExecuter, logger); - - int code = runner.runUntilEnd(); - - assertEquals("Wrong exit code", 1, code); - verify(logger).printError(kaboom); - } - - @Test - public void shellRunnerHasNoHistory() throws Exception { - // given - StringShellRunner runner = new StringShellRunner(parse("nan anana"), statementExecuter, logger); - - // when then - assertEquals(Historian.empty, runner.getHistorian()); - } -}