package io.innerloop.neo4j.client; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.kernel.GraphDatabaseAPI; import org.neo4j.server.CommunityNeoServer; import org.neo4j.server.WrappingNeoServerBootstrapper; import org.neo4j.server.configuration.Configurator; import org.neo4j.server.configuration.ServerConfigurator; import org.neo4j.server.configuration.ServerSettings; import org.neo4j.server.helpers.CommunityServerBuilder; import org.neo4j.test.TestGraphDatabaseFactory; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.ServerSocket; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.TestCase.fail; import static org.junit.Assert.assertTrue; /** * Created by markangrish on 11/12/2014. */ public class EndToEndTests { private static final int DEFAULT_NEO_PORT = 7575; private Neo4jClient client; private int neoServerPort = -1; private GraphDatabaseService database; private WrappingNeoServerBootstrapper bootstrapper; @BeforeClass public static void oneTimeSetUp() { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); Logger rootLogger = loggerContext.getLogger("io.innerloop.neo4j.client"); rootLogger.setLevel(Level.DEBUG); } private static int findOpenLocalPort() { try (ServerSocket socket = new ServerSocket(0)) { return socket.getLocalPort(); } catch (IOException e) { System.err.println("Unable to establish local port due to IOException: " + e.getMessage() + "\nDefaulting instead to use: " + DEFAULT_NEO_PORT); e.printStackTrace(System.err); return DEFAULT_NEO_PORT; } } @Before public void setUp() throws IOException, InterruptedException { database = new TestGraphDatabaseFactory().newImpermanentDatabase(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { database.shutdown(); } }); ServerConfigurator configurator = new ServerConfigurator((GraphDatabaseAPI) database); int port = neoServerPort(); configurator.configuration().addProperty(Configurator.WEBSERVER_PORT_PROPERTY_KEY, port); configurator.configuration().addProperty("dbms.security.auth_enabled", false); bootstrapper = new WrappingNeoServerBootstrapper((GraphDatabaseAPI) database, configurator); bootstrapper.start(); while (!bootstrapper.getServer().getDatabase().isRunning()) { // It's ok to spin here.. it's not production code. Thread.sleep(250); } client = new Neo4jClient("http://localhost:" + port + "/db/data"); } protected int neoServerPort() { if (neoServerPort < 0) { neoServerPort = findOpenLocalPort(); } return neoServerPort; } @After public void tearDown() throws IOException, InterruptedException { bootstrapper.stop(); database.shutdown(); client = null; } @Test public void testSimpleQuery() { Connection connection = client.getConnection(); RowStatement statement = new RowStatement("MATCH (n) RETURN count(n)"); connection.add(statement); connection.commit(); RowSet result = statement.getResult(); assertNotNull(result); int totalNodes = result.getInt(0); assertTrue(totalNodes >= 0); } @Test public void testInsertCompoundStatements() { Connection connection1 = client.getConnection(); RowStatement statement1 = new RowStatement("MERGE (n:Label{id:\"id1\", prop1:\"property1\"})"); RowStatement statement2 = new RowStatement("MERGE (n:Label{id:\"id2\", prop1:\"property2\"})"); connection1.add(statement1); connection1.add(statement2); connection1.commit(); //check if the nodes were inserted Connection connection2 = client.getConnection(); RowStatement statement = new RowStatement("MATCH (n:Label) RETURN count(n) as number_of_nodes"); connection2.add(statement); connection2.commit(); RowSet result = statement.getResult(); assertNotNull(result); int numberOfLabelNodes = result.getInt(0); assertEquals(2, numberOfLabelNodes); } @Test public void testGraphCreationAndRetrieval() { Connection connection1 = client.getConnection(); RowStatement statement1 = new RowStatement("MERGE (n1:Graph{id:\"id1\", prop1:\"property1\"})-[:connectedTo]-(n2:Graph{id:\"id2\", prop1:\"property2\"})"); connection1.add(statement1); connection1.commit(); //check if the nodes were inserted Connection connection2 = client.getConnection(); GraphStatement statement = new GraphStatement("MATCH (n:Graph)-[rels]-() RETURN rels"); connection2.add(statement); connection2.commit(); Graph result = statement.getResult(); assertNotNull(result); Set<Node> nodes = result.getNodes(); assertEquals(2, nodes.size()); Set<Relationship> relationships = result.getRelationships(); assertEquals(1, relationships.size()); } @Test public void testBasicLongTransaction() { Connection connection1 = client.getConnection(); try { RowStatement statement1 = new RowStatement("MERGE (n1:Graph{id:\"id1\", prop1:\"property1\"})-[:connectedTo]-(n2:Graph{id:\"id2\", prop1:\"property2\"})"); RowStatement statement2 = new RowStatement("MERGE (n2:Graph{id:\"id3\", prop1:\"property3\"})"); connection1.add(statement1); connection1.add(statement2); connection1.flush(); RowStatement statement3 = new RowStatement("MERGE (n2:Graph{id:\"id4\"}) SET n2 = {props}"); Map<String, Object> props = new HashMap<>(); props.put("id", "id4"); props.put("prop1", "property4"); props.put("random", 213); statement3.setParam("props", props); connection1.add(statement3); connection1.commit(); } catch (Neo4jClientException e) { connection1.rollback(); fail("Should have been able to commit. Rolled back to keep DB consistent."); } //check if the nodes were inserted Connection connection2 = client.getConnection(); GraphStatement statement = new GraphStatement("MATCH (n:Graph) OPTIONAL MATCH (n)-[rels]-() RETURN n, rels"); connection2.add(statement); connection2.commit(); Graph result = statement.getResult(); assertNotNull(result); Set<Node> nodes = result.getNodes(); assertEquals(4, nodes.size()); Set<Relationship> relationships = result.getRelationships(); assertEquals(1, relationships.size()); } @Test public void testMultipleThreadsInsertingCompoundStatements() throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(10); CountDownLatch latch = new CountDownLatch(1000); for (int i = 1; i <= 100; i++) { service.execute(new InsertJob(latch, i)); } latch.await(); //check if the nodes were inserted Connection connection2 = client.getConnection(); RowStatement statement = new RowStatement("MATCH (n:BulkInsert) RETURN count(n) as number_of_nodes"); connection2.add(statement); connection2.commit(); RowSet result = statement.getResult(); assertNotNull(result); int numberOfLabelNodes = result.getInt(0); assertEquals(1000, numberOfLabelNodes); } private class InsertJob implements Runnable { private final CountDownLatch latch; private final int id; public InsertJob(CountDownLatch latch, int i) { this.latch = latch; this.id = i; } @Override public void run() { Connection connection = client.getConnection(); try { for (int i = 1; i <= 10; i++) { RowStatement statement1 = new RowStatement("MERGE (n:BulkInsert{id:\"id" + id + i + "\"})"); connection.add(statement1); } connection.commit(); } catch (Neo4jClientException e) { throw new RuntimeException(e); } for (int i = 0; i < 10; i++) { latch.countDown(); } } } }