Skip to content
This repository was archived by the owner on Jul 6, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,26 @@

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.PrintStream;

import org.neo4j.driver.exceptions.ClientException;
import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.shell.cli.CliArgs;
import org.neo4j.shell.log.AnsiLogger;
import org.neo4j.shell.log.Logger;
import org.neo4j.shell.prettyprint.LinePrinter;
import org.neo4j.shell.prettyprint.PrettyConfig;
import org.neo4j.shell.prettyprint.ToStringLinePrinter;

import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

public class MainIntegrationTest
{
Expand Down Expand Up @@ -73,11 +82,10 @@ public void promptsOnWrongAuthenticationIfInteractive() throws Exception {
// should be connected
assertTrue(shell.isConnected());
// should have prompted and set the username and password
assertEquals( format( "username: neo4j%npassword: ***%n" ), baos.toString() );
assertEquals("neo4j", connectionConfig.username());
assertEquals("neo", connectionConfig.password());

String out = baos.toString();
assertEquals( String.format( "username: neo4j%npassword: ***%n" ), out );
}

@Test
Expand Down Expand Up @@ -133,9 +141,101 @@ public void wrongPortWithNeo4j() throws Exception
main.connectMaybeInteractively( shell, connectionConfig, true, true );
}

@Test
public void shouldAskForCredentialsWhenConnectingWithAFile() throws Exception {
//given

assertEquals("", connectionConfig.username());
assertEquals("", connectionConfig.password());

//when
CliArgs cliArgs = new CliArgs();
cliArgs.setInputFilename(fileFromResource("single.cypher"));
ShellAndConnection sac = getShell(cliArgs);
CypherShell shell = sac.shell;
ConnectionConfig connectionConfig = sac.connectionConfig;
main.connectMaybeInteractively( shell, connectionConfig, true, true );

// then we should have prompted and set the username and password
assertEquals( format( "username: neo4j%npassword: ***%n" ), baos.toString() );
assertEquals("neo4j", connectionConfig.username());
assertEquals("neo", connectionConfig.password());
}

@Test
public void shouldReadSingleCypherStatementsFromFile() throws Exception {
assertEquals(format( "result%n42%n" ), executeFileNonInteractively(fileFromResource("single.cypher")));
}

@Test
public void shouldReadEmptyCypherStatementsFile() throws Exception {
assertEquals("", executeFileNonInteractively(fileFromResource("empty.cypher")));
}

@Test
public void shouldReadMultipleCypherStatementsFromFile() throws Exception {
assertEquals(format( "result%n42%n" +
"result%n1337%n" +
"result%n\"done\"%n"), executeFileNonInteractively(fileFromResource("multiple.cypher")));
}

@Test
public void shouldFailIfInputFileDoesntExist() throws Exception {
// expect
exception.expect( FileNotFoundException.class);
exception.expectMessage( "what.cypher (No such file or directory)" );
executeFileNonInteractively("what.cypher");
}

@Test
public void shouldHandleInvalidCypherFromFile() throws Exception {
//given
Logger logger = mock(Logger.class);


// when
String actual = executeFileNonInteractively( fileFromResource( "invalid.cypher" ), logger);

//then we print the first valid row
assertEquals( format( "result%n42%n" ), actual );
//and print errors to the error log
verify(logger).printError(any( ClientException.class ));
verifyNoMoreInteractions(logger);
}

private String executeFileNonInteractively(String filename) throws Exception {
return executeFileNonInteractively(filename, mock(Logger.class));
}

private String executeFileNonInteractively(String filename, Logger logger) throws Exception
{
CliArgs cliArgs = new CliArgs();
cliArgs.setInputFilename(filename);

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();

return linePrinter.result();
}

private String fileFromResource(String filename)
{
return getClass().getClassLoader().getResource(filename).getFile();
}

private ShellAndConnection getShell( CliArgs cliArgs )
{
Logger logger = new AnsiLogger( cliArgs.getDebugMode() );
return getShell( cliArgs, logger );
}

private ShellAndConnection getShell( CliArgs cliArgs, LinePrinter linePrinter )
{
PrettyConfig prettyConfig = new PrettyConfig( cliArgs );
ConnectionConfig connectionConfig = new ConnectionConfig(
cliArgs.getScheme(),
Expand All @@ -146,6 +246,6 @@ private ShellAndConnection getShell( CliArgs cliArgs )
cliArgs.getEncryption(),
cliArgs.getDatabase() );

return new ShellAndConnection( new CypherShell( logger, prettyConfig, true, new ShellParameterMap() ), connectionConfig );
return new ShellAndConnection( new CypherShell( linePrinter, prettyConfig, true, new ShellParameterMap() ), connectionConfig );
}
}
Empty file.
3 changes: 3 additions & 0 deletions cypher-shell/src/integration-test/resources/invalid.cypher
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RETURN 42 AS result;
THIS IS NOT CYPHER;
RETURN 1337 AS result;
6 changes: 6 additions & 0 deletions cypher-shell/src/integration-test/resources/multiple.cypher
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//do this first
RETURN 42 AS result;
//then we do
RETURN 1337 AS result;
//and finally we do
RETURN "done" AS result;
1 change: 1 addition & 0 deletions cypher-shell/src/integration-test/resources/single.cypher
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RETURN 42 AS result;
25 changes: 23 additions & 2 deletions cypher-shell/src/main/java/org/neo4j/shell/ShellRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
import org.neo4j.shell.parser.ShellStatementParser;

import javax.annotation.Nonnull;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import static org.fusesource.jansi.internal.CLibrary.STDIN_FILENO;
import static org.fusesource.jansi.internal.CLibrary.STDOUT_FILENO;
Expand Down Expand Up @@ -54,8 +59,9 @@ static ShellRunner getShellRunner(@Nonnull CliArgs cliArgs,
return new InteractiveShellRunner(cypherShell, cypherShell, cypherShell, logger, new ShellStatementParser(),
System.in, FileHistorian.getDefaultHistoryFile(), userMessagesHandler, connectionConfig);
} else {

return new NonInteractiveShellRunner(cliArgs.getFailBehavior(), cypherShell, logger,
new ShellStatementParser(), System.in);
new ShellStatementParser(), getInputStream(cliArgs));
}
}

Expand All @@ -64,7 +70,8 @@ static ShellRunner getShellRunner(@Nonnull CliArgs cliArgs,
* @return true if an interactive shellrunner should be used, false otherwise
*/
static boolean shouldBeInteractive(@Nonnull CliArgs cliArgs) {
if (cliArgs.getNonInteractive()) {
if ( cliArgs.getNonInteractive() || cliArgs.getInputFilename() != null )
{
return false;
}

Expand Down Expand Up @@ -114,4 +121,18 @@ static boolean isOutputInteractive() {
return System.console() != null;
}
}

/**
* If an input file has been defined use that, otherwise use STDIN
* @throws FileNotFoundException if the provided input file doesn't exist
*/
static InputStream getInputStream(CliArgs cliArgs) throws FileNotFoundException
{
if ( cliArgs.getInputFilename() == null)
{
return System.in;
} else {
return new BufferedInputStream( new FileInputStream( new File(cliArgs.getInputFilename()) ));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ private static CliArgs getCliArgs( CliArgs cliArgs, ArgumentParser parser, Names
}
cliArgs.setEncryption(ns.getBoolean("encryption"));
cliArgs.setDatabase(ns.getString("database"));
cliArgs.setInputFilename(ns.getString( "file" ) );

//----------------
// Other arguments
Expand Down Expand Up @@ -167,6 +168,8 @@ private static ArgumentParser setupParser(ParameterMap parameterMap)
connGroup.addArgument("-d", "--database")
.help("database to connect to. Can also be specified using environment variable " + ConnectionConfig.DATABASE_ENV_VAR)
.setDefault("");
connGroup.addArgument("-f", "--file")
.help("Pass a file with cypher statements to be executed. After the statements have been executed cypher-shell will be shutdown");

MutuallyExclusiveGroup failGroup = parser.addMutuallyExclusiveGroup();
failGroup.addArgument("--fail-fast")
Expand Down
14 changes: 14 additions & 0 deletions cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgs.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class CliArgs {
private boolean driverVersion = false;
private int numSampleRows = DEFAULT_NUM_SAMPLE_ROWS;
private boolean wrap = true;
private String inputFilename = null;
private ParameterMap parameters = new ShellParameterMap();

/**
Expand Down Expand Up @@ -111,6 +112,14 @@ public void setNonInteractive(boolean nonInteractive) {
this.nonInteractive = nonInteractive;
}

/**
* Sets a filename where to read Cypher statements from, much like piping statements from a file.
*/
public void setInputFilename(String inputFilename)
{
this.inputFilename = inputFilename;
}

/**
* Enable/disable debug mode
*/
Expand Down Expand Up @@ -174,6 +183,11 @@ public boolean getNonInteractive() {
return nonInteractive;
}

public String getInputFilename()
{
return inputFilename;
}

public boolean getVersion() {
return version;
}
Expand Down
2 changes: 1 addition & 1 deletion cypher-shell/src/test/java/org/neo4j/shell/MainTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public void promptsForPassIfUserExistsIfInteractive() throws Exception {
}

@Test
public void promptsSielntlyForPassIfUserExistsIfOutputRedirected() throws Exception {
public void promptsSilentlyForPassIfUserExistsIfOutputRedirected() throws Exception {
doThrow(authException).doNothing().when(shell).connect(connectionConfig);
doReturn("bob").when(connectionConfig).username();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,17 @@ public void shouldFailForInvalidSyntaxForArg() throws Exception {
CliArgHelper.parseAndThrow( "-P", "foo: => 'nanana'");
}

@Test
public void testDefaultInputFileName() {
CliArgs arguments = CliArgHelper.parse();
assertNotNull( arguments );
assertNull( arguments.getInputFilename() );
}

@Test
public void testSetInputFileName() {
CliArgs arguments = CliArgHelper.parse("--file", "foo");
assertNotNull( arguments );
assertEquals( "foo", arguments.getInputFilename() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,11 @@ public void getParameters()
// so setting them cannot be tested here.
assertEquals( Collections.EMPTY_MAP, cliArgs.getParameters().allParameterValues() );
}

@Test
public void setInputFile()
{
cliArgs.setInputFilename("foo");
assertEquals("foo", cliArgs.getInputFilename());
}
}