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 @@ -14,15 +14,20 @@
import org.neo4j.driver.exceptions.ClientException;
import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.shell.cli.CliArgs;
import org.neo4j.shell.commands.CommandHelper;
import org.neo4j.shell.exception.CommandException;
import org.neo4j.shell.exception.ExitException;
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.hamcrest.CoreMatchers.isA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -85,7 +90,6 @@ public void promptsOnWrongAuthenticationIfInteractive() throws Exception {
assertEquals( format( "username: neo4j%npassword: ***%n" ), baos.toString() );
assertEquals("neo4j", connectionConfig.username());
assertEquals("neo", connectionConfig.password());

}

@Test
Expand Down Expand Up @@ -203,6 +207,75 @@ public void shouldHandleInvalidCypherFromFile() throws Exception {
verifyNoMoreInteractions(logger);
}

@Test
public void shouldReadSingleCypherStatementsFromFileInteractively() throws Exception {
// given
ToStringLinePrinter linePrinter = new ToStringLinePrinter();
CypherShell shell = interactiveShell( linePrinter );

// when
shell.execute( ":source " + fileFromResource( "single.cypher" ));
exit( shell );

// then
assertEquals( format("result%n42%n"), linePrinter.result() );
}

@Test
public void shouldReadMultipleCypherStatementsFromFileInteractively() throws Exception {
// given
ToStringLinePrinter linePrinter = new ToStringLinePrinter();
CypherShell shell = interactiveShell( linePrinter );

// when
shell.execute( ":source " + fileFromResource( "multiple.cypher" ));
exit( shell );

// then
assertEquals(format( "result%n42%n" +
"result%n1337%n" +
"result%n\"done\"%n"), linePrinter.result() );
}

@Test
public void shouldReadEmptyCypherStatementsFromFileInteractively() throws Exception {
// given
ToStringLinePrinter linePrinter = new ToStringLinePrinter();
CypherShell shell = interactiveShell( linePrinter );

// when
shell.execute( ":source " + fileFromResource( "empty.cypher" ));
exit( shell );

// then
assertEquals("", linePrinter.result() );
}

@Test
public void shouldHandleInvalidCypherStatementsFromFileInteractively() throws Exception {
// given
ToStringLinePrinter linePrinter = new ToStringLinePrinter();
CypherShell shell = interactiveShell( linePrinter );

// then
exception.expect( ClientException.class );
exception.expectMessage( "Invalid input 'T':" );
shell.execute( ":source " + fileFromResource( "invalid.cypher" ));
}

@Test
public void shouldFailIfInputFileDoesntExistInteractively() throws Exception {
// given
ToStringLinePrinter linePrinter = new ToStringLinePrinter();
CypherShell shell = interactiveShell( linePrinter );

// expect
exception.expect( CommandException.class);
exception.expectMessage( "Cannot find file: 'what.cypher'" );
exception.expectCause( isA( FileNotFoundException.class ) );
shell.execute( ":source what.cypher" );
}

private String executeFileNonInteractively(String filename) throws Exception {
return executeFileNonInteractively(filename, mock(Logger.class));
}
Expand All @@ -228,6 +301,15 @@ private String fileFromResource(String filename)
return getClass().getClassLoader().getResource(filename).getFile();
}

private CypherShell interactiveShell( LinePrinter linePrinter ) throws Exception
{
PrettyConfig prettyConfig = new PrettyConfig( new CliArgs() );
CypherShell shell = new CypherShell( linePrinter, prettyConfig, true, new ShellParameterMap() );
main.connectMaybeInteractively( shell, connectionConfig, true, true );
shell.setCommandHelper( new CommandHelper( mock( Logger.class ), Historian.empty, shell) );
return shell;
}

private ShellAndConnection getShell( CliArgs cliArgs )
{
Logger logger = new AnsiLogger( cliArgs.getDebugMode() );
Expand All @@ -248,4 +330,17 @@ private ShellAndConnection getShell( CliArgs cliArgs, LinePrinter linePrinter )

return new ShellAndConnection( new CypherShell( linePrinter, prettyConfig, true, new ShellParameterMap() ), connectionConfig );
}

private void exit( CypherShell shell ) throws CommandException
{
try
{
shell.execute( ":exit" );
fail("Should have exited");
}
catch ( ExitException e )
{
//do nothing
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
import javax.annotation.Nullable;

import org.neo4j.shell.CypherShell;
import org.neo4j.shell.DatabaseManager;
import org.neo4j.shell.Historian;
import org.neo4j.shell.TransactionHandler;
import org.neo4j.shell.ParameterMap;
import org.neo4j.shell.exception.CommandException;
import org.neo4j.shell.exception.DuplicateCommandException;
import org.neo4j.shell.log.AnsiFormattedText;
import org.neo4j.shell.log.Logger;
import org.neo4j.shell.parser.ShellStatementParser;

/**
* Utility methods for dealing with commands
Expand All @@ -23,23 +21,22 @@ public class CommandHelper {
private final TreeMap<String, Command> commands = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

public CommandHelper(Logger logger, Historian historian, CypherShell cypherShell) {
registerAllCommands(logger, historian, cypherShell, cypherShell, cypherShell.getParameterMap());
registerAllCommands(logger, historian, cypherShell);
}

private void registerAllCommands(Logger logger,
Historian historian,
DatabaseManager databaseManager,
TransactionHandler transactionHandler,
ParameterMap parameterMap) {
CypherShell cypherShell) {
registerCommand(new Exit(logger));
registerCommand(new Help(logger, this));
registerCommand(new History(logger, historian));
registerCommand(new Use(databaseManager));
registerCommand(new Begin(transactionHandler));
registerCommand(new Commit(transactionHandler));
registerCommand(new Rollback(transactionHandler));
registerCommand(new Param(parameterMap));
registerCommand(new Params(logger, parameterMap));
registerCommand(new Use(cypherShell));
registerCommand(new Begin(cypherShell));
registerCommand(new Commit(cypherShell));
registerCommand(new Rollback(cypherShell));
registerCommand(new Param(cypherShell.getParameterMap()));
registerCommand(new Params(logger, cypherShell.getParameterMap()));
registerCommand(new Source(cypherShell, new ShellStatementParser() ));
}

private void registerCommand(@Nonnull final Command command) throws DuplicateCommandException {
Expand Down
81 changes: 81 additions & 0 deletions cypher-shell/src/main/java/org/neo4j/shell/commands/Source.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.neo4j.shell.commands;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;

import org.neo4j.shell.CypherShell;
import org.neo4j.shell.exception.CommandException;
import org.neo4j.shell.exception.ExitException;
import org.neo4j.shell.parser.StatementParser;

import static java.lang.String.format;
import static org.neo4j.shell.commands.CommandHelper.simpleArgParse;

/**
* This command reads a cypher file frome the filesystem and executes the statements therein.
*/
public class Source implements Command {
private static final String COMMAND_NAME = ":source";
private final CypherShell cypherShell;
private final StatementParser statementParser;

public Source( CypherShell cypherShell, StatementParser statementParser ) {
this.cypherShell = cypherShell;
this.statementParser = statementParser;
}

@Nonnull
@Override
public String getName() {
return COMMAND_NAME;
}

@Nonnull
@Override
public String getDescription() {
return "Interactively executes cypher statements from a file";
}

@Nonnull
@Override
public String getUsage() {
return "[filename]";
}

@Nonnull
@Override
public String getHelp() {
return "Executes Cypher statements from a file";
}

@Nonnull
@Override
public List<String> getAliases() {
return Collections.emptyList();
}

@Override
public void execute(@Nonnull final String argString) throws ExitException, CommandException {
String filename = simpleArgParse(argString, 1, 1, COMMAND_NAME, getUsage())[0];

try ( BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream( new File(filename) )))) {
bufferedReader.lines()
.forEach(line -> statementParser.parseMoreText(line + "\n"));
List<String> statements = statementParser.consumeStatements();
for ( String statement : statements )
{
cypherShell.execute( statement );
}
}
catch ( IOException e )
{
throw new CommandException( format("Cannot find file: '%s'", filename), e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.neo4j.shell.commands;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.io.FileNotFoundException;

import org.neo4j.shell.CypherShell;
import org.neo4j.shell.exception.CommandException;
import org.neo4j.shell.log.Logger;
import org.neo4j.shell.parser.ShellStatementParser;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.isA;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

public class SourceTest
{
@Rule
public final ExpectedException thrown = ExpectedException.none();

private Logger logger;
private Source cmd;
private CypherShell shell;

@Before
public void setup()
{
logger = mock(Logger.class);
shell = mock(CypherShell.class);
cmd = new Source(shell, new ShellStatementParser() );
}

@Test
public void descriptionNotNull() {
assertNotNull(cmd.getDescription());
}

@Test
public void usageNotNull() {
assertNotNull(cmd.getUsage());
}

@Test
public void helpNotNull() {
assertNotNull(cmd.getHelp());
}

@Test
public void runCommand() throws CommandException {
// given
cmd.execute( fileFromResource( "test.cypher" ) );
verify(shell).execute( "RETURN 42;" );
verifyNoMoreInteractions( shell );
}

@Test
public void shouldFailIfFileNotThere() throws CommandException
{
thrown.expect( CommandException.class );
thrown.expectMessage(containsString("Cannot find file: 'not.there'"));
thrown.expectCause(isA(FileNotFoundException.class));
cmd.execute( "not.there" );
}

@Test
public void shouldNotAcceptMoreThanOneArgs() throws CommandException {
thrown.expect(CommandException.class);
thrown.expectMessage(containsString("Incorrect number of arguments"));

cmd.execute("bob sob");
}

@Test
public void shouldNotAcceptZeroArgs() throws CommandException {
thrown.expect(CommandException.class);
thrown.expectMessage(containsString("Incorrect number of arguments"));

cmd.execute("");
}

private String fileFromResource(String filename)
{
return getClass().getResource(filename).getFile();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RETURN 42;