package org.gbif.checklistbank.neo; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.assertj.core.util.Files; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.helpers.collection.Iterables; import static org.junit.Assert.assertEquals; /** * Neo4j changes its behavior between 1.9, 2.0 and 2.2 version when it comes to the state of modified nodes and * relations during transactions. This test makes sure the behavior coded for in this project matches the currently * used neo4j version. * * In particular we expect the getAllNodes() method to iterate over all nodes but the ones created during the iteration * if the transaction was not yet committed. */ public class TransactionTest { private File dbf; private GraphDatabaseService db; @Before public void init() throws IOException { dbf = Files.newTemporaryFolder(); db = new GraphDatabaseFactory().newEmbeddedDatabase(dbf); } @After public void cleanup() { db.shutdown(); FileUtils.deleteQuietly(dbf); } @Test public void testTransactionsGetAll() { Transaction tx = db.beginTx(); // initial 100 nodes without relations for (int idx=1; idx<=100; idx++) { db.createNode(Labels.ROOT); } tx.success(); tx.close(); assertEquals(100, countAll()); // now iterate over those 100 nodes, persistent a new node and link them // expect those nodes not to be shown in the iterator which opened at the beginning of the tx. tx = db.beginTx(); int counter = 0; for (Node n : db.getAllNodes()) { Node n2 = db.createNode(Labels.TAXON); n.createRelationshipTo(n2, RelType.PARENT_OF); counter++; } tx.success(); tx.close(); assertEquals(100, counter); assertEquals(200, countAll()); // now commit in batches. We expect the newly created nodes to appear in the iterator as we reopen the tx in between ... tx = db.beginTx(); counter = 0; for (Node n : db.getAllNodes()) { if (counter < 100) { Node n2 = db.createNode(Labels.SYNONYM); n2.createRelationshipTo(n, RelType.SYNONYM_OF); } counter++; if (counter % 10 == 0) { tx.success(); tx.close(); tx = db.beginTx(); } } tx.success(); tx.close(); assertEquals(300, counter); assertEquals(300, countAll()); // now commit at the end, but mark tx as success every time... tx = db.beginTx(); counter = 0; for (Node n : db.getAllNodes()) { if (counter < 100) { Node n2 = db.createNode(Labels.SYNONYM); n2.createRelationshipTo(n, RelType.SYNONYM_OF); } counter++; tx.success(); } tx.success(); tx.close(); assertEquals(300, counter); assertEquals(400, countAll()); // now use outer and inner transactions. // When committing the inner transactions they dont affect the outer one tx = db.beginTx(); counter = 0; for (Node n : db.getAllNodes()) { if (counter < 100) { Transaction tx2 = db.beginTx(); Node n2 = db.createNode(Labels.SYNONYM); n2.createRelationshipTo(n, RelType.SYNONYM_OF); tx2.success(); tx2.close(); } counter++; } tx.success(); tx.close(); assertEquals(400, counter); assertEquals(500, countAll()); } private long countAll() { try (Transaction tx = db.beginTx()) { return Iterables.count(db.getAllNodes()); } } }