/*
* Copyright (c) 2013 GraphAware
*
* This file is part of GraphAware.
*
* GraphAware is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of
* the GNU General Public License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
package com.graphaware.tx.event.improved;
import com.graphaware.common.policy.*;
import com.graphaware.common.policy.NodePropertyInclusionPolicy;
import com.graphaware.common.policy.RelationshipPropertyInclusionPolicy;
import com.graphaware.test.util.TestDataBuilder;
import com.graphaware.tx.event.improved.api.Change;
import com.graphaware.tx.event.improved.api.FilteredTransactionData;
import com.graphaware.tx.event.improved.api.ImprovedTransactionData;
import com.graphaware.tx.event.improved.api.LazyTransactionData;
import com.graphaware.tx.executor.single.*;
import org.junit.After;
import org.junit.Test;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.event.TransactionData;
import org.neo4j.graphdb.event.TransactionEventHandler;
import org.neo4j.graphdb.traversal.Evaluators;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Uniqueness;
import org.neo4j.test.TestGraphDatabaseFactory;
import org.neo4j.tooling.GlobalGraphOperations;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static com.graphaware.common.util.IterableUtils.count;
import static com.graphaware.common.util.IterableUtils.countNodes;
import static com.graphaware.common.util.PropertyContainerUtils.*;
import static com.graphaware.tx.event.improved.api.Change.*;
import static org.junit.Assert.*;
import static org.neo4j.graphdb.Direction.INCOMING;
import static org.neo4j.graphdb.Direction.OUTGOING;
import static org.neo4j.graphdb.DynamicRelationshipType.withName;
/**
* Integration test for {@link com.graphaware.tx.event.improved.api.FilteredTransactionData}.
*/
@SuppressWarnings("deprecation")
public class FilteredLazyTransactionDataIntegrationTest {
private static final String INTERNAL_PREFIX = "_GA_";
private static final String INTERNAL_NODE_PROPERTY = INTERNAL_PREFIX + "LABEL";
private GraphDatabaseService database;
@After
public void tearDown() {
database.shutdown();
}
@Test
public void allInternalMutationsShouldLookLikeNoMutations() {
createTestDatabaseForInternalTest();
mutateGraph(new InternalTestGraphMutation(), new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertFalse(transactionData.mutationsOccurred());
}
});
}
@Test
public void changeOfPropertyToNonInternalOnRelationshipShouldBePickedUp() {
createTestDatabaseForInternalTest();
mutateGraph(new InternalTestGraphMutation(), new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
//nothing here
}
});
final AtomicBoolean mutationsOccurred = new AtomicBoolean(false);
mutateGraph(new VoidReturningCallback() {
@Override
protected void doInTx(GraphDatabaseService database) {
Node three = database.getNodeById(3);
Relationship r = three.getSingleRelationship(withName("R4"), OUTGOING);
r.setProperty("non-internal", 4); //this makes it non-internal
}
}, new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
if (transactionData.mutationsOccurred()) {
mutationsOccurred.set(true);
}
}
}
);
assertTrue(mutationsOccurred.get());
}
@Test
public void policiesThatIgnoreEverythingShouldBeHonoured() {
createTestDatabase();
database.registerTransactionEventHandler(new TransactionEventHandler.Adapter<Void>() {
@Override
public Void beforeCommit(TransactionData data) throws Exception {
ImprovedTransactionData improvedTransactionData = new FilteredTransactionData(new LazyTransactionData(data), InclusionPolicies.none());
assertFalse(improvedTransactionData.mutationsOccurred());
assertTrue(improvedTransactionData.hasBeenChanged(database.getNodeById(1)));
assertTrue(improvedTransactionData.changedProperties(database.getNodeById(1)).isEmpty());
return null;
}
});
new SimpleTransactionExecutor(database).executeInTransaction(new TestGraphMutation());
}
@Test
public void createdRelationshipsShouldBeCorrectlyIdentified() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Relationship> created = propertyContainersToMap(transactionData.getAllCreatedRelationships());
assertEquals(2, created.size());
long r1Id = database.getNodeById(5).getSingleRelationship(withName("R2"), OUTGOING).getId();
Relationship r1 = created.get(r1Id);
assertEquals("not there", r1.getProperty("time", "not there")); //filtered out
assertEquals(0, count(r1.getPropertyKeys())); //time filtered out
long r2Id = database.getNodeById(1).getSingleRelationship(withName("R1"), OUTGOING).getId();
Relationship r2 = created.get(r2Id);
assertEquals(0, count(r2.getPropertyKeys()));
assertTrue(transactionData.hasBeenCreated(r1));
assertTrue(transactionData.hasBeenCreated(r2));
assertFalse(transactionData.hasBeenCreated(database.getNodeById(3).getSingleRelationship(withName("R1"), OUTGOING)));
assertFalse(r2.getEndNode().hasProperty("place")); //filtered out
assertEquals(4, r2.getEndNode().getSingleRelationship(withName("R1"), OUTGOING).getEndNode().getId()); //node filtered out but when accessed as a relationship end node it is present
assertNull(r2.getEndNode().getSingleRelationship(withName("R3"), OUTGOING)); //filtered out
}
}
);
}
@Test
public void startingWithCreatedRelationshipCurrentGraphVersionShouldBeTraversed() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Relationship> created = propertyContainersToMap(transactionData.getAllCreatedRelationships());
long r2Id = database.getNodeById(1).getSingleRelationship(withName("R1"), OUTGOING).getId();
Relationship r2 = created.get(r2Id);
assertEquals("NewOne", r2.getStartNode().getProperty("name"));
assertFalse(r2.getStartNode().hasProperty("count"));
assertTrue(Arrays.equals(new String[]{"one", "three"}, (String[]) r2.getStartNode().getProperty("tags")));
assertNull(r2.getEndNode().getSingleRelationship(withName("R3"), INCOMING));
assertEquals(5, r2.getEndNode().getSingleRelationship(withName("R2"), INCOMING).getStartNode().getId());
}
}
);
}
@Test
public void changedRelationshipsShouldBeCorrectlyIdentified() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Change<Relationship>> changed = changesToMap(transactionData.getAllChangedRelationships());
assertEquals(0, changed.size()); //R3 filtered out
assertTrue(transactionData.hasBeenChanged(database.getRelationshipById(changedRelId.get())));
assertFalse(transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getCurrent().hasProperty("time")); //filtered
assertTrue(transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getCurrent().hasProperty("tags"));
assertFalse(transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getPrevious().hasProperty("time")); //filtered
assertFalse(transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getPrevious().hasProperty("tags"));
}
}
);
}
@Test
public void startingWithPreviousChangedRelationshipPreviousGraphVersionShouldBeTraversedUsingNativeApi() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Relationship previous = transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getPrevious();
assertEquals("One", previous.getEndNode().getProperty("name"));
assertEquals(1, previous.getEndNode().getProperty("count", 2));
assertTrue(Arrays.equals(new String[]{"one", "two"}, (String[]) previous.getEndNode().getProperty("tags")));
assertEquals(3, count(previous.getEndNode().getPropertyKeys()));
assertEquals("Three", previous.getStartNode().getProperty("name"));
assertEquals("nothing", previous.getStartNode().getProperty("tags", "nothing"));
Node endNode = previous.getEndNode();
Relationship r1 = endNode.getSingleRelationship(withName("R1"), OUTGOING);
Node endNode1 = r1.getEndNode();
assertEquals("Two", endNode1.getProperty("name"));
}
}
);
}
@Test
public void startingWithPreviousChangedRelationshipPreviousGraphVersionShouldBeTraversedUsingTraversalApi() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Relationship previous = transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getPrevious();
TraversalDescription traversalDescription = database.traversalDescription()
.relationships(withName("R1"), OUTGOING)
.relationships(withName("R2"), OUTGOING)
.relationships(withName("R3"), INCOMING)
.depthFirst()
.uniqueness(Uniqueness.NODE_GLOBAL)
.evaluator(Evaluators.toDepth(3));
assertEquals(4, count(traversalDescription.traverse(previous.getEndNode()).nodes()));
}
}
);
}
@Test
public void startingWithCurrentChangedRelationshipCurrentGraphVersionShouldBeTraversedUsingNativeApi() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Relationship current = transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getCurrent();
current = transactionData.getChanged(current).getCurrent();
assertEquals("NewOne", current.getEndNode().getProperty("name"));
assertEquals(2, current.getEndNode().getProperty("count", 2));
assertFalse(current.getEndNode().hasProperty("count"));
assertTrue(Arrays.equals(new String[]{"one", "three"}, (String[]) current.getEndNode().getProperty("tags")));
assertEquals(2, count(current.getEndNode().getPropertyKeys()));
assertEquals("Three", current.getStartNode().getProperty("name"));
assertEquals("none", current.getStartNode().getProperty("place", "none")); //filtered
assertEquals("one", current.getStartNode().getProperty("tags"));
assertEquals("Three", current.getEndNode().getSingleRelationship(withName("R1"), OUTGOING).getEndNode().getProperty("name"));
}
}
);
}
@Test
public void startingWithCurrentChangedRelationshipCurrentGraphVersionShouldBeTraversedUsingTraversalApi() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Relationship current = transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getCurrent();
TraversalDescription traversalDescription = database.traversalDescription()
.relationships(withName("R1"), OUTGOING)
.relationships(withName("R2"), OUTGOING)
.relationships(withName("R3"), INCOMING)
.depthFirst()
.uniqueness(Uniqueness.NODE_GLOBAL);
assertEquals(3, count(traversalDescription.traverse(current.getEndNode()).nodes()));
}
}
);
}
@Test
public void deletedRelationshipsShouldBeCorrectlyIdentified() {
createTestDatabase();
final AtomicLong deletedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
deletedRelId.set(database.getNodeById(1).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Relationship> deleted = propertyContainersToMap(transactionData.getAllDeletedRelationships());
assertEquals(3, deleted.size());
long r1Id = propertyContainersToMap(transactionData.getAllDeletedNodes()).get(2L).getSingleRelationship(withName("R1"), INCOMING).getId();
Relationship r1 = deleted.get(r1Id);
try {
r1.getProperty("time");
fail();
} catch (NotFoundException e) {
//ok
}
assertEquals(0, count(r1.getPropertyKeys()));
long r2Id = propertyContainersToMap(transactionData.getAllDeletedNodes()).get(2L).getSingleRelationship(withName("R2"), INCOMING).getId();
Relationship r2 = deleted.get(r2Id);
assertEquals(0, count(r2.getPropertyKeys()));
Iterator<Relationship> relationships = propertyContainersToMap(transactionData.getAllDeletedNodes()).get(2L).getRelationships(withName("R2"), OUTGOING).iterator();
long r3Id = relationships.next().getId();
if (r3Id == r2Id) {
r3Id = relationships.next().getId();
}
Relationship r3 = deleted.get(r3Id);
assertEquals("nothing", r3.getProperty("time", "nothing"));
assertEquals(0, count(r3.getPropertyKeys()));
Relationship r4 = transactionData.getDeleted(database.getRelationshipById(deletedRelId.get()));
assertEquals(0, count(r4.getPropertyKeys()));
assertTrue(transactionData.hasBeenDeleted(r1));
assertTrue(transactionData.hasBeenDeleted(r2));
assertTrue(transactionData.hasBeenDeleted(r3));
assertTrue(transactionData.hasBeenDeleted(r4));
assertFalse(transactionData.hasBeenDeleted(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING)));
assertEquals(3, count(transactionData.getDeletedRelationships(database.getNodeById(2))));
assertEquals(3, count(transactionData.getDeletedRelationships(database.getNodeById(2), withName("R2"), withName("R1"))));
assertEquals(2, count(transactionData.getDeletedRelationships(database.getNodeById(2), withName("R2"))));
assertEquals(2, count(transactionData.getDeletedRelationships(database.getNodeById(2), OUTGOING)));
assertEquals(2, count(transactionData.getDeletedRelationships(database.getNodeById(2), OUTGOING, withName("R2"))));
assertEquals(1, count(transactionData.getDeletedRelationships(database.getNodeById(2), INCOMING, withName("R2"))));
assertEquals(0, count(transactionData.getDeletedRelationships(database.getNodeById(2), withName("R3"))));
}
}
);
}
@Test
public void startingWithDeletedRelationshipPreviousGraphVersionShouldBeTraversedUsingNativeApi() {
createTestDatabase();
final AtomicLong deletedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
deletedRelId.set(database.getNodeById(1).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Relationship r4 = transactionData.getDeleted(database.getRelationshipById(deletedRelId.get()));
Relationship deletedRel = transactionData.getDeleted(r4);
assertEquals("One", deletedRel.getStartNode().getProperty("name"));
assertEquals(1, deletedRel.getStartNode().getProperty("count", 2));
assertTrue(Arrays.equals(new String[]{"one", "two"}, (String[]) deletedRel.getStartNode().getProperty("tags")));
assertEquals(3, count(deletedRel.getStartNode().getPropertyKeys()));
assertEquals("Three", deletedRel.getEndNode().getProperty("name"));
assertEquals("nothing", deletedRel.getEndNode().getProperty("tags", "nothing"));
Node startNode = deletedRel.getStartNode();
Relationship r5 = startNode.getSingleRelationship(withName("R1"), OUTGOING);
assertEquals("Two", r5.getEndNode().getProperty("name"));
assertEquals(2, count(startNode.getRelationships()));
assertEquals(2, count(startNode.getRelationships(OUTGOING)));
assertEquals(0, count(startNode.getRelationships(withName("R3"))));
assertEquals(1, count(startNode.getRelationships(withName("R3"), withName("R1"))));
assertEquals(0, count(startNode.getRelationships(INCOMING, withName("R3"), withName("R1"))));
}
}
);
}
@Test
public void startingWithDeletedRelationshipPreviousGraphVersionShouldBeTraversedUsingTraversalApi() {
createTestDatabase();
final AtomicLong deletedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
deletedRelId.set(database.getNodeById(1).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Relationship deletedRel = transactionData.getDeleted(database.getRelationshipById(deletedRelId.get()));
TraversalDescription traversalDescription = database.traversalDescription()
.relationships(withName("R1"), OUTGOING)
.relationships(withName("R2"), OUTGOING)
.relationships(withName("R3"), INCOMING)
.depthFirst()
.uniqueness(Uniqueness.NODE_GLOBAL)
.evaluator(Evaluators.toDepth(3));
assertEquals(4, count(traversalDescription.traverse(deletedRel.getStartNode()).nodes()));
}
}
);
}
@Test
public void createdRelationshipPropertiesShouldBeCorrectlyIdentified() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Change<Relationship> change = transactionData.getChanged(database.getRelationshipById(changedRelId.get()));
assertTrue(transactionData.hasPropertyBeenCreated(change.getCurrent(), "tags"));
assertFalse(transactionData.hasPropertyBeenCreated(change.getCurrent(), "time"));
assertTrue(transactionData.hasPropertyBeenCreated(change.getPrevious(), "tags"));
assertFalse(transactionData.hasPropertyBeenCreated(change.getPrevious(), "tag"));
assertEquals(1, transactionData.createdProperties(change.getCurrent()).size());
assertEquals(1, transactionData.createdProperties(change.getPrevious()).size());
assertEquals("cool", transactionData.createdProperties(change.getCurrent()).get("tags"));
assertEquals("cool", transactionData.createdProperties(change.getPrevious()).get("tags"));
assertFalse(transactionData.hasPropertyBeenCreated(transactionData.getAllDeletedRelationships().iterator().next(), "tags"));
//created relationship should not fall into this category
assertFalse(transactionData.hasPropertyBeenCreated(database.getNodeById(5).getSingleRelationship(withName("R2"), OUTGOING), "time"));
}
}
);
}
@Test
public void changedRelationshipPropertiesShouldBeCorrectlyIdentified() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Change<Relationship> change = transactionData.getChanged(database.getRelationshipById(changedRelId.get()));
assertTrue(transactionData.hasPropertyBeenChanged(change.getCurrent(), "time"));
assertFalse(transactionData.hasPropertyBeenChanged(change.getCurrent(), "tags"));
assertTrue(transactionData.hasPropertyBeenChanged(change.getPrevious(), "time"));
assertFalse(transactionData.hasPropertyBeenChanged(change.getPrevious(), "tag"));
assertEquals(0, transactionData.changedProperties(change.getCurrent()).size()); //filtered
assertEquals(0, transactionData.changedProperties(change.getPrevious()).size()); //filtered
}
}
);
}
@Test
public void deletedRelationshipPropertiesShouldBeCorrectlyIdentified() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Change<Relationship> change = transactionData.getChanged(database.getRelationshipById(changedRelId.get()));
assertTrue(transactionData.hasPropertyBeenDeleted(change.getCurrent(), "tag"));
assertFalse(transactionData.hasPropertyBeenDeleted(change.getCurrent(), "time"));
assertTrue(transactionData.hasPropertyBeenDeleted(change.getPrevious(), "tag"));
assertFalse(transactionData.hasPropertyBeenDeleted(change.getPrevious(), "tags"));
assertEquals(1, transactionData.deletedProperties(change.getCurrent()).size());
assertEquals(1, transactionData.deletedProperties(change.getPrevious()).size());
assertEquals("cool", transactionData.deletedProperties(change.getCurrent()).get("tag"));
assertEquals("cool", transactionData.deletedProperties(change.getPrevious()).get("tag"));
//deleted relationships' props don't qualify
Iterator<Relationship> iterator = transactionData.getAllDeletedRelationships().iterator();
assertFalse(transactionData.hasPropertyBeenDeleted(iterator.next(), "time"));
assertFalse(transactionData.hasPropertyBeenDeleted(iterator.next(), "time"));
assertFalse(transactionData.hasPropertyBeenDeleted(iterator.next(), "time"));
}
}
);
}
@Test
public void createdNodesShouldBeCorrectlyIdentified() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Node> createdNodes = propertyContainersToMap(transactionData.getAllCreatedNodes());
assertEquals(1, createdNodes.size());
Node createdNode = createdNodes.get(5L);
assertEquals("Five", createdNode.getProperty("name"));
assertEquals(4L, createdNode.getProperty("size"));
assertEquals(2, count(createdNode.getPropertyKeys()));
assertTrue(transactionData.hasBeenCreated(createdNode));
assertFalse(transactionData.hasBeenCreated(database.getNodeById(3)));
}
}
);
}
@Test
public void startingWithCreatedNodeCurrentGraphVersionShouldBeTraversed() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Node> createdNodes = propertyContainersToMap(transactionData.getAllCreatedNodes());
Node createdNode = createdNodes.get(5L);
assertEquals("one", createdNode.getSingleRelationship(withName("R2"), OUTGOING).getEndNode().getProperty("tags"));
assertEquals("not there", createdNode.getSingleRelationship(withName("R2"), OUTGOING).getEndNode().getProperty("place", "not there"));
assertFalse(createdNode.getSingleRelationship(withName("R2"), OUTGOING).getEndNode().getRelationships(withName("R3"), INCOMING).iterator().hasNext());
}
}
);
}
@Test
public void changedNodesShouldBeCorrectlyIdentified() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
assertEquals(2, changed.size());
Node previous1 = changed.get(1L).getPrevious();
assertEquals(3, count(previous1.getPropertyKeys()));
assertEquals("One", previous1.getProperty("name"));
assertEquals(1, previous1.getProperty("count"));
assertTrue(Arrays.equals(new String[]{"one", "two"}, (String[]) previous1.getProperty("tags")));
Node current1 = changed.get(1L).getCurrent();
assertEquals(2, count(current1.getPropertyKeys()));
assertEquals("NewOne", current1.getProperty("name"));
assertTrue(Arrays.equals(new String[]{"one", "three"}, (String[]) current1.getProperty("tags")));
Node previous2 = changed.get(3L).getPrevious();
assertEquals(1, count(previous2.getPropertyKeys()));
assertEquals("Three", previous2.getProperty("name"));
Node current2 = changed.get(3L).getCurrent();
assertEquals(2, count(current2.getPropertyKeys()));
assertEquals("Three", current2.getProperty("name"));
assertEquals("one", current2.getProperty("tags"));
assertTrue(transactionData.hasBeenChanged(previous1));
assertTrue(transactionData.hasBeenChanged(previous2));
assertTrue(transactionData.hasBeenChanged(current1));
assertTrue(transactionData.hasBeenChanged(current2));
assertFalse(transactionData.hasBeenChanged(database.getNodeById(4)));
}
}
);
}
@Test
public void startingWithPreviousChangedNodePreviousGraphVersionShouldBeTraversedUsingNativeApi() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node current = changed.get(1L).getCurrent();
Node previous = transactionData.getChanged(current).getPrevious();
assertFalse(previous.getSingleRelationship(withName("R1"), OUTGOING).hasProperty("time"));
assertEquals("Two", previous.getRelationships(withName("R1"), OUTGOING).iterator().next().getEndNode().getProperty("name"));
assertNull(previous.getSingleRelationship(withName("R3"), OUTGOING));
assertEquals(2L, previous.getRelationships(withName("R1")).iterator().next().getEndNode().getProperty("size"));
assertNull(previous.getSingleRelationship(withName("R1"), INCOMING));
assertEquals(2, count(previous.getRelationships()));
assertEquals(1, count(previous.getRelationships(withName("R1"), withName("R3"))));
assertEquals(1, count(previous.getRelationships(withName("R1"))));
assertEquals(2, count(previous.getRelationships(OUTGOING)));
assertEquals(0, count(previous.getRelationships(INCOMING)));
assertEquals(0, count(previous.getRelationships(OUTGOING, withName("R3"))));
assertEquals(1, count(previous.getRelationships(OUTGOING, withName("R1"), withName("R3"))));
assertEquals(0, count(previous.getRelationships(withName("R3"), OUTGOING)));
assertTrue(previous.hasRelationship());
assertTrue(previous.hasRelationship(withName("R1"), withName("R3")));
assertTrue(previous.hasRelationship(withName("R1")));
assertTrue(previous.hasRelationship(OUTGOING));
assertFalse(previous.hasRelationship(INCOMING));
assertFalse(previous.hasRelationship(OUTGOING, withName("R3")));
assertTrue(previous.hasRelationship(OUTGOING, withName("R1"), withName("R3")));
assertFalse(previous.hasRelationship(withName("R3"), OUTGOING));
assertFalse(previous.hasRelationship(withName("R1"), INCOMING));
assertFalse(previous.hasRelationship(withName("R2")));
previous.createRelationshipTo(database.getNodeById(4), withName("R3"));
assertNull(previous.getSingleRelationship(withName("R3"), OUTGOING));
}
}
);
}
@Test
public void startingWithPreviousChangedNodePreviousGraphVersionShouldBeTraversedUsingTraversalApi() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node previous = changed.get(1L).getPrevious();
TraversalDescription traversalDescription = database.traversalDescription()
.relationships(withName("R1"), OUTGOING)
.relationships(withName("R2"), OUTGOING)
.relationships(withName("R3"), INCOMING)
.depthFirst()
.uniqueness(Uniqueness.NODE_GLOBAL)
.evaluator(Evaluators.toDepth(3));
assertEquals(4, count(traversalDescription.traverse(previous).nodes()));
}
}
);
}
@Test
public void startingWithCurrentChangedNodeCurrentGraphVersionShouldBeTraversedUsingNativeApi() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node current = changed.get(1L).getCurrent();
assertEquals("Three", current.getSingleRelationship(withName("R1"), OUTGOING).getEndNode().getProperty("name"));
assertEquals("not there", current.getRelationships(withName("R1"), OUTGOING).iterator().next().getEndNode().getProperty("place", "not there"));
assertEquals("one", current.getRelationships(OUTGOING, withName("R1")).iterator().next().getEndNode().getProperty("tags"));
}
}
);
}
@Test
public void startingWithCurrentChangedNodeCurrentGraphVersionShouldBeTraversed() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node current = changed.get(1L).getCurrent();
TraversalDescription traversalDescription = database.traversalDescription()
.relationships(withName("R1"), OUTGOING)
.relationships(withName("R2"), OUTGOING)
.relationships(withName("R3"), INCOMING)
.depthFirst()
.uniqueness(Uniqueness.NODE_GLOBAL);
assertEquals(3, count(traversalDescription.traverse(current).nodes()));
}
}
);
}
@Test
public void deletedNodesShouldBeCorrectlyIdentified() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Node> deletedNodes = propertyContainersToMap(transactionData.getAllDeletedNodes());
assertEquals(1, deletedNodes.size());
Node deleted = deletedNodes.get(2L);
Node one = transactionData.getDeleted(deleted).getSingleRelationship(withName("R1"), INCOMING).getStartNode();
assertEquals("Two", deleted.getProperty("name"));
assertEquals(2L, deleted.getProperty("size"));
assertEquals(2, count(deleted.getPropertyKeys()));
assertTrue(transactionData.hasBeenDeleted(deleted));
assertFalse(transactionData.hasBeenDeleted(one));
}
}
);
}
@Test
public void startingWithDeletedNodePreviousGraphVersionShouldBeTraversedUsingNativeApi() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Node> deletedNodes = propertyContainersToMap(transactionData.getAllDeletedNodes());
Node deleted = deletedNodes.get(2L);
Node one = transactionData.getDeleted(deleted).getSingleRelationship(withName("R1"), INCOMING).getStartNode();
assertEquals("bla", one.getSingleRelationship(withName("R1"), OUTGOING).getProperty("time", "bla"));
assertEquals("Two", one.getRelationships(withName("R1"), OUTGOING).iterator().next().getEndNode().getProperty("name"));
assertNull(one.getSingleRelationship(withName("R3"), OUTGOING));
assertEquals(2L, one.getRelationships(withName("R1")).iterator().next().getEndNode().getProperty("size"));
}
}
);
}
@Test
public void startingWithDeletedNodePreviousGraphVersionShouldBeTraversedUsingTraversalApi() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Map<Long, Node> deletedNodes = propertyContainersToMap(transactionData.getAllDeletedNodes());
Node deleted = deletedNodes.get(2L);
Node one = transactionData.getDeleted(deleted).getSingleRelationship(withName("R1"), INCOMING).getStartNode();
TraversalDescription traversalDescription = database.traversalDescription()
.relationships(withName("R1"), OUTGOING)
.relationships(withName("R2"), OUTGOING)
.relationships(withName("R3"), INCOMING)
.depthFirst()
.uniqueness(Uniqueness.NODE_GLOBAL)
.evaluator(Evaluators.toDepth(3));
assertEquals(4, count(traversalDescription.traverse(one).nodes()));
}
}
);
}
@Test
public void createdNodePropertiesShouldBeCorrectlyIdentified() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Change<Node> changed = changesToMap(transactionData.getAllChangedNodes()).get(3L);
assertTrue(transactionData.hasPropertyBeenCreated(changed.getCurrent(), "tags"));
assertFalse(transactionData.hasPropertyBeenCreated(changed.getCurrent(), "name"));
assertTrue(transactionData.hasPropertyBeenCreated(changed.getPrevious(), "tags"));
assertFalse(transactionData.hasPropertyBeenCreated(changed.getPrevious(), "name"));
assertEquals(1, transactionData.createdProperties(changed.getCurrent()).size());
assertEquals(1, transactionData.createdProperties(changed.getPrevious()).size());
assertEquals("one", transactionData.createdProperties(changed.getCurrent()).get("tags"));
assertEquals("one", transactionData.createdProperties(changed.getPrevious()).get("tags"));
assertFalse(transactionData.hasPropertyBeenCreated(changesToMap(transactionData.getAllChangedNodes()).get(1L).getCurrent(), "tags"));
}
}
);
}
@Test
public void changedNodePropertiesShouldBeCorrectlyIdentified() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Change<Node> changed = changesToMap(transactionData.getAllChangedNodes()).get(1L);
assertTrue(transactionData.hasPropertyBeenChanged(changed.getCurrent(), "name"));
assertTrue(transactionData.hasPropertyBeenChanged(changed.getCurrent(), "tags"));
assertFalse(transactionData.hasPropertyBeenChanged(changed.getCurrent(), "count"));
assertTrue(transactionData.hasPropertyBeenChanged(changed.getPrevious(), "name"));
assertTrue(transactionData.hasPropertyBeenChanged(changed.getPrevious(), "tags"));
assertFalse(transactionData.hasPropertyBeenChanged(changed.getPrevious(), "count"));
assertEquals(2, transactionData.changedProperties(changed.getCurrent()).size());
assertEquals(2, transactionData.changedProperties(changed.getPrevious()).size());
assertEquals("One", transactionData.changedProperties(changed.getCurrent()).get("name").getPrevious());
assertEquals("One", transactionData.changedProperties(changed.getPrevious()).get("name").getPrevious());
assertEquals("NewOne", transactionData.changedProperties(changed.getCurrent()).get("name").getCurrent());
assertEquals("NewOne", transactionData.changedProperties(changed.getPrevious()).get("name").getCurrent());
assertTrue(Arrays.equals(new String[]{"one", "three"}, (String[]) transactionData.changedProperties(changed.getCurrent()).get("tags").getCurrent()));
assertTrue(Arrays.equals(new String[]{"one", "two"}, (String[]) transactionData.changedProperties(changed.getPrevious()).get("tags").getPrevious()));
assertTrue(Arrays.equals(new String[]{"one", "three"}, (String[]) transactionData.changedProperties(changed.getCurrent()).get("tags").getCurrent()));
assertTrue(Arrays.equals(new String[]{"one", "two"}, (String[]) transactionData.changedProperties(changed.getPrevious()).get("tags").getPrevious()));
assertEquals(3, count(changed.getPrevious().getPropertyKeys()));
assertEquals(2, count(changed.getCurrent().getPropertyKeys()));
changed = changesToMap(transactionData.getAllChangedNodes()).get(3L);
assertEquals(0, transactionData.changedProperties(changed.getCurrent()).size());
assertEquals(0, transactionData.changedProperties(changed.getPrevious()).size());
assertFalse(transactionData.hasPropertyBeenChanged(changed.getCurrent(), "name"));
assertFalse(transactionData.hasPropertyBeenChanged(changed.getCurrent(), "tags"));
assertFalse(transactionData.hasPropertyBeenChanged(changed.getCurrent(), "place"));
assertFalse(transactionData.hasPropertyBeenChanged(changed.getPrevious(), "name"));
assertFalse(transactionData.hasPropertyBeenChanged(changed.getPrevious(), "tags"));
assertFalse(transactionData.hasPropertyBeenChanged(changed.getPrevious(), "place"));
assertFalse(transactionData.hasPropertyBeenChanged(transactionData.getAllDeletedNodes().iterator().next(), "name"));
assertFalse(transactionData.hasPropertyBeenChanged(transactionData.getAllCreatedNodes().iterator().next(), "name"));
assertEquals(1, count(changed.getPrevious().getPropertyKeys()));
assertEquals(2, count(changed.getCurrent().getPropertyKeys()));
//one that isn't changed
Node unchanged = changesToMap(transactionData.getAllChangedNodes()).get(1L).getPrevious().getSingleRelationship(withName("WHATEVER"), OUTGOING).getEndNode();
assertEquals(1, count(unchanged.getPropertyKeys()));
assertEquals("name", unchanged.getPropertyKeys().iterator().next());
assertEquals("Four", unchanged.getProperty("name"));
assertEquals("Four", unchanged.getProperty("name", "nothing"));
assertEquals("nothing", unchanged.getProperty("non-existing", "nothing"));
}
}
);
}
@Test
public void deletedNodePropertiesShouldBeCorrectlyIdentified() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertTrue(transactionData.mutationsOccurred());
Change<Node> changed = changesToMap(transactionData.getAllChangedNodes()).get(1L);
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getCurrent(), "name"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getCurrent(), "tags"));
assertTrue(transactionData.hasPropertyBeenDeleted(changed.getCurrent(), "count"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getPrevious(), "name"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getPrevious(), "tags"));
assertTrue(transactionData.hasPropertyBeenDeleted(changed.getPrevious(), "count"));
assertEquals(1, transactionData.deletedProperties(changed.getCurrent()).size());
assertEquals(1, transactionData.deletedProperties(changed.getPrevious()).size());
assertEquals(1, transactionData.deletedProperties(changed.getCurrent()).get("count"));
assertEquals(1, transactionData.deletedProperties(changed.getPrevious()).get("count"));
changed = changesToMap(transactionData.getAllChangedNodes()).get(3L);
assertEquals(0, transactionData.deletedProperties(changed.getCurrent()).size());
assertEquals(0, transactionData.deletedProperties(changed.getPrevious()).size());
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getCurrent(), "name"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getCurrent(), "tags"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getCurrent(), "place"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getPrevious(), "name"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getPrevious(), "tags"));
assertFalse(transactionData.hasPropertyBeenDeleted(changed.getPrevious(), "place"));
assertFalse(transactionData.hasPropertyBeenDeleted(transactionData.getAllDeletedNodes().iterator().next(), "name"));
assertFalse(transactionData.hasPropertyBeenDeleted(transactionData.getAllCreatedNodes().iterator().next(), "name"));
}
}
);
}
//mutations
@Test
public void shouldBeAbleToChangeCreatedRelationshipBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
if (!transactionData.mutationsOccurred()) {
return;
}
Map<Long, Relationship> created = propertyContainersToMap(transactionData.getAllCreatedRelationships());
long r1Id = database.getNodeById(5).getSingleRelationship(withName("R2"), OUTGOING).getId();
Relationship r1 = created.get(r1Id);
r1.setProperty("additional", "someValue");
r1.removeProperty("time");
}
}
);
try (Transaction tx = database.beginTx()) {
Relationship r1 = database.getNodeById(5).getSingleRelationship(withName("R2"), OUTGOING);
assertEquals(1, count(r1.getPropertyKeys()));
assertEquals("someValue", r1.getProperty("additional"));
assertFalse(r1.hasProperty("time"));
}
}
@Test
public void shouldBeAbleToChangeCreatedNodeBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
if (!transactionData.mutationsOccurred()) {
return;
}
Map<Long, Node> createdNodes = propertyContainersToMap(transactionData.getAllCreatedNodes());
Node createdNode = createdNodes.get(5L);
createdNode.setProperty("name", "NewFive");
createdNode.setProperty("additional", "something");
createdNode.removeProperty("size");
}
}
);
try (Transaction tx = database.beginTx()) {
Node createdNode = database.getNodeById(5L);
assertEquals("NewFive", createdNode.getProperty("name"));
assertEquals("something", createdNode.getProperty("additional"));
assertEquals(2, count(createdNode.getPropertyKeys()));
assertFalse(createdNode.hasProperty("size"));
}
}
@Test
public void shouldBeAbleToChangeCurrentChangedNodeBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
if (!transactionData.mutationsOccurred()) {
return;
}
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node node = changed.get(1L).getCurrent();
node.setProperty("name", "YetAnotherOne");
node.setProperty("additional", "something");
node.removeProperty("tags");
}
}
);
try (Transaction tx = database.beginTx()) {
Node node = database.getNodeById(1L);
assertEquals(2, count(node.getPropertyKeys()));
assertEquals("YetAnotherOne", node.getProperty("name"));
assertEquals("something", node.getProperty("additional"));
assertFalse(node.hasProperty("tags"));
}
}
@Test
public void shouldBeAbleToChangePreviousChangedNodeBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
if (!transactionData.mutationsOccurred()) {
return;
}
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node node = changed.get(1L).getPrevious();
node.setProperty("name", "YetAnotherOne");
node.setProperty("additional", "something");
node.removeProperty("tags");
}
}
);
try (Transaction tx = database.beginTx()) {
Node node = database.getNodeById(1L);
assertEquals(2, count(node.getPropertyKeys()));
assertEquals("YetAnotherOne", node.getProperty("name"));
assertEquals("something", node.getProperty("additional"));
assertFalse(node.hasProperty("tags"));
}
}
@Test(expected = TransactionFailureException.class)
public void shouldNotBeAbleToChangeDeletedRelationshipBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
Map<Long, Relationship> deleted = propertyContainersToMap(transactionData.getAllDeletedRelationships());
long r1Id = propertyContainersToMap(transactionData.getAllDeletedNodes()).get(2L).getSingleRelationship(withName("R1"), INCOMING).getId();
Relationship r1 = deleted.get(r1Id);
try {
r1.setProperty("irrelevant", "irrelevant");
return;
} catch (IllegalStateException e) {
//OK
}
r1.removeProperty("irrelevant");
}
}
);
}
@Test(expected = TransactionFailureException.class)
public void shouldNotBeAbleToChangeDeletedNodeBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
Map<Long, Node> deletedNodes = propertyContainersToMap(transactionData.getAllDeletedNodes());
Node deleted = deletedNodes.get(2L);
try {
deleted.setProperty("irrelevant", "irrelevant");
return;
} catch (IllegalStateException e) {
//OK
}
deleted.removeProperty("irrelevant");
}
}
);
}
@Test(expected = TransactionFailureException.class)
public void shouldNotBeAbleToCreateARelationshipFromDeletedNodeBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
Map<Long, Node> deletedNodes = propertyContainersToMap(transactionData.getAllDeletedNodes());
Node deleted = deletedNodes.get(2L);
deleted.createRelationshipTo(database.getNodeById(3), withName("illegal"));
}
}
);
}
@Test(expected = TransactionFailureException.class)
public void shouldNotBeAbleToCreateARelationshipToDeletedNodeBeforeCommit() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
Map<Long, Node> deletedNodes = propertyContainersToMap(transactionData.getAllDeletedNodes());
Node deleted = deletedNodes.get(2L);
try {
database.getNodeById(3).createRelationshipTo(deleted, withName("illegal"));
fail();
} catch (IllegalStateException e) {
//OK
}
}
}
);
}
@Test
public void shouldChangeNothingIfTxRollsBack() {
createTestDatabase();
mutateGraph(
new TestGraphMutation(),
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
throw new RuntimeException("Deliberate testing exception");
}
},
KeepCalmAndCarryOn.getInstance()
);
try (Transaction tx = database.beginTx()) {
long r4Id = database.getNodeById(3L).getSingleRelationship(withName("R3"), INCOMING).getId();
Relationship r4 = database.getRelationshipById(r4Id);
assertEquals("One", r4.getStartNode().getProperty("name"));
assertEquals(1, r4.getStartNode().getProperty("count", 2));
assertTrue(Arrays.equals(new String[]{"one", "two"}, (String[]) r4.getStartNode().getProperty("tags")));
assertEquals(3, count(r4.getStartNode().getPropertyKeys()));
assertEquals("Three", r4.getEndNode().getProperty("name"));
assertEquals("London", r4.getEndNode().getProperty("place"));
assertEquals("nothing", r4.getEndNode().getProperty("tags", "nothing"));
Node startNode = r4.getStartNode();
Relationship r5 = startNode.getSingleRelationship(withName("R1"), OUTGOING);
assertEquals("Two", r5.getEndNode().getProperty("name"));
assertEquals(4, count(startNode.getRelationships()));
assertEquals(3, count(startNode.getRelationships(OUTGOING)));
assertEquals(2, count(startNode.getRelationships(withName("R3"))));
assertEquals(3, count(startNode.getRelationships(withName("R3"), withName("R1"))));
assertEquals(1, count(startNode.getRelationships(INCOMING, withName("R3"), withName("R1"))));
}
}
@Test
public void shouldBeAbleToDeleteChangedNodeCommittingTransaction() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
Change<Node> change = transactionData.getChanged(database.getNodeById(1));
//must first delete the new relationship
change.getCurrent().getSingleRelationship(withName("R1"), OUTGOING).delete();
deleteNodeAndRelationships(change.getPrevious().getGraphDatabase().getNodeById(change.getPrevious().getId()));
}
}
);
try (Transaction tx = database.beginTx()) {
assertEquals(4, countNodes(database));
}
}
@Test
public void shouldBeAbleToWipeTheGraphBeforeCommittingTransaction() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
for (Node node : GlobalGraphOperations.at(database).getAllNodes()) {
deleteNodeAndRelationships(node);
}
}
}
);
try (Transaction tx = database.beginTx()) {
assertEquals(0, countNodes(database));
}
}
@Test
public void shouldNotChangeAnythingWhenDeletingAlreadyDeletedNodeAndRelationships() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
Map<Long, Node> deletedNodes = propertyContainersToMap(transactionData.getAllDeletedNodes());
Node deleted = deletedNodes.get(2L);
deleteNodeAndRelationships(deleted);
}
}
);
try (Transaction tx = database.beginTx()) {
assertEquals(5, countNodes(database));
}
}
@Test
public void shouldBeAbleToCreateAdditionalNodesAndRelationshipsFromCurrentGraphVersion() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
if (!transactionData.mutationsOccurred()) {
return;
}
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node node = changed.get(1L).getCurrent();
Node newNode = database.createNode();
newNode.setProperty("name", "Six");
node.createRelationshipTo(newNode, withName("R4")).setProperty("new", true);
}
}
);
try (Transaction tx = database.beginTx()) {
Relationship newRelationship = database.getNodeById(1).getSingleRelationship(withName("R4"), OUTGOING);
assertNotNull(newRelationship);
assertEquals("Six", newRelationship.getEndNode().getProperty("name"));
assertEquals(true, newRelationship.getProperty("new"));
}
}
@Test
public void shouldBeAbleToCreateAdditionalNodesAndRelationshipsFromPreviousGraphVersion() {
createTestDatabase();
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
if (!transactionData.mutationsOccurred()) {
return;
}
Map<Long, Change<Node>> changed = changesToMap(transactionData.getAllChangedNodes());
Node node = changed.get(1L).getPrevious();
Node newNode = database.createNode();
newNode.setProperty("name", "Six");
node.createRelationshipTo(newNode, withName("R4")).setProperty("new", true);
}
}
);
try (Transaction tx = database.beginTx()) {
Relationship newRelationship = database.getNodeById(1).getSingleRelationship(withName("R4"), OUTGOING);
assertNotNull(newRelationship);
assertEquals("Six", newRelationship.getEndNode().getProperty("name"));
assertEquals(true, newRelationship.getProperty("new"));
}
}
@Test
public void propertyExtractionStrategySmokeTest() {
createTestDatabase();
final AtomicLong changedRelId = new AtomicLong();
try (Transaction tx = database.beginTx()) {
changedRelId.set(database.getNodeById(3).getSingleRelationship(withName("R3"), OUTGOING).getId());
}
mutateGraph(
new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
Relationship previous = transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getPrevious();
Relationship current = transactionData.getChanged(database.getRelationshipById(changedRelId.get())).getCurrent();
Map<String, Object> previousProps = new OtherNodeNameIncludingRelationshipPropertiesExtractor().extractProperties(previous, previous.getStartNode());
assertEquals(2, previousProps.size());
assertEquals("One", previousProps.get("otherNodeName"));
assertEquals("cool", previousProps.get("tag"));
Map<String, Object> currentProps = new OtherNodeNameIncludingRelationshipPropertiesExtractor().extractProperties(current, current.getStartNode());
assertEquals(2, currentProps.size());
assertEquals("NewOne", currentProps.get("otherNodeName"));
assertEquals("cool", currentProps.get("tags"));
}
}
);
}
@Test
public void shouldIndicateNoMutationWhenNothingHasBeenChanged() {
createTestDatabase();
mutateGraph(new VoidReturningCallback() {
@Override
protected void doInTx(GraphDatabaseService database) {
//change that should not be picked up as a change
Node four = database.getNodeById(4);
four.setProperty("name", "Three");
four.setProperty("name", "Four");
}
}, new BeforeCommitCallback() {
@Override
public void doBeforeCommit(ImprovedTransactionData transactionData) {
assertFalse(transactionData.mutationsOccurred());
}
}
);
}
//test helpers
private void mutateGraph(BeforeCommitCallback beforeCommitCallback) {
mutateGraph(new TestGraphMutation(), beforeCommitCallback);
}
private void mutateGraph(VoidReturningCallback transactionCallback, BeforeCommitCallback beforeCommitCallback) {
mutateGraph(transactionCallback, beforeCommitCallback, RethrowException.getInstance());
}
private void mutateGraph(TransactionCallback<Void> transactionCallback, BeforeCommitCallback beforeCommitCallback, ExceptionHandlingStrategy exceptionHandlingStrategy) {
TestingTxEventHandler handler = new TestingTxEventHandler(beforeCommitCallback);
database.registerTransactionEventHandler(handler);
new SimpleTransactionExecutor(database).executeInTransaction(transactionCallback, exceptionHandlingStrategy);
}
private class TestingTxEventHandler implements TransactionEventHandler {
private final BeforeCommitCallback beforeCommitCallback;
private TestingTxEventHandler(BeforeCommitCallback beforeCommitCallback) {
this.beforeCommitCallback = beforeCommitCallback;
}
@Override
public Object beforeCommit(TransactionData data) throws Exception {
LazyTransactionData lazyTransactionData = new LazyTransactionData(data);
beforeCommitCallback.doBeforeCommit(new FilteredTransactionData(lazyTransactionData, new InclusionPolicies(
new NodeInclusionPolicy() {
@Override
public boolean include(Node node) {
return !node.getProperty("name", "").equals("Four") && !node.hasProperty(INTERNAL_NODE_PROPERTY);
}
},
new NodePropertyInclusionPolicy() {
@Override
public boolean include(String key, Node propertyContainer) {
return !"place".equals(key) && !key.startsWith(INTERNAL_PREFIX);
}
},
new RelationshipInclusionPolicy.Adapter() {
@Override
public boolean include(Relationship relationship) {
return !relationship.isType(withName("R3")) && !relationship.getType().name().startsWith(INTERNAL_PREFIX);
}
}
, new RelationshipPropertyInclusionPolicy() {
@Override
public boolean include(String key, Relationship propertyContainer) {
return !"time".equals(key) && !key.startsWith(INTERNAL_PREFIX);
}
}
)));
return null;
}
@Override
public void afterCommit(TransactionData data, Object state) {
//do nothing
}
@Override
public void afterRollback(TransactionData data, Object state) {
//do nothing
}
}
private interface BeforeCommitCallback {
void doBeforeCommit(ImprovedTransactionData transactionData);
}
private class OtherNodeNameIncludingRelationshipPropertiesExtractor {
public Map<String, Object> extractProperties(Relationship relationship, Node pointOfView) {
Map<String, Object> result = new HashMap<>();
result.putAll(propertiesToMap(relationship));
result.put("otherNodeName", relationship.getOtherNode(pointOfView).getProperty("name").toString());
return result;
}
}
private void createTestDatabase() {
database = new TestGraphDatabaseFactory().newImpermanentDatabase();
new TestDataBuilder(database)
.node() //ID=0
.node().setProp("name", "One").setProp("count", 1).setProp("tags", new String[]{"one", "two"})
.node().setProp("name", "Two").setProp("size", 2L)
.relationshipFrom(1, "R1").setProp("time", 1)
.relationshipFrom(2, "R2")
.node().setProp("name", "Three").setProp("place", "London")
.relationshipFrom(2, "R2").setProp("time", 2)
.relationshipTo(1, "R3").setProp("time", 3).setProp("tag", "cool")
.relationshipFrom(1, "R3")
.node().setProp("name", "Four")
.relationshipFrom(3, "R1").setProp("time", 1)
.relationshipFrom(1, "WHATEVER");
}
private class TestGraphMutation extends VoidReturningCallback {
@Override
public void doInTx(GraphDatabaseService database) {
Node one = database.getNodeById(1);
one.setProperty("name", "NewOne");
one.removeProperty("count");
one.setProperty("tags", new String[]{"one"});
one.setProperty("tags", new String[]{"one", "three"});
Node two = database.getNodeById(2);
deleteNodeAndRelationships(two);
Node three = database.getNodeById(3);
three.setProperty("tags", "one");
three.setProperty("place", "Rome");
three.setProperty("place", "London");
Node five = database.createNode();
five.setProperty("name", "Five");
five.setProperty("size", 3L);
five.setProperty("size", 4L);
Relationship r = five.createRelationshipTo(three, withName("R2"));
r.setProperty("time", 4);
r = three.getSingleRelationship(withName("R3"), OUTGOING);
r.setProperty("time", 4);
r.removeProperty("tag");
r.setProperty("tags", "cool");
three.getSingleRelationship(withName("R3"), INCOMING).delete();
one.createRelationshipTo(three, withName("R1"));
//change that should not be picked up as a change
Node four = database.getNodeById(4);
four.setProperty("name", "Three");
four.setProperty("name", "Four");
}
}
private void createTestDatabaseForInternalTest() {
database = new TestGraphDatabaseFactory().newImpermanentDatabase();
new TestDataBuilder(database)
.node() //ID=0
.node().setProp(INTERNAL_NODE_PROPERTY, "whatever").setProp(INTERNAL_PREFIX + "name", "One").setProp(INTERNAL_PREFIX + "count", 1).setProp(INTERNAL_PREFIX + "tags", new String[]{"one", "two"})
.node().setProp(INTERNAL_NODE_PROPERTY, "whatever").setProp("name", "Two").setProp("size", 2L)
.relationshipFrom(1, INTERNAL_PREFIX + "R1").setProp("time", 1)
.relationshipFrom(2, INTERNAL_PREFIX + "R2")
.node().setProp("name", "Three").setProp("place", "London")
.relationshipFrom(2, INTERNAL_PREFIX + "R2").setProp("time", 2)
.relationshipTo(1, "R4").setProp(INTERNAL_PREFIX + "time", 3).setProp(INTERNAL_PREFIX + "tag", "cool")
.relationshipFrom(1, INTERNAL_PREFIX + "R3")
.node().setProp(INTERNAL_NODE_PROPERTY, "whatever").setProp("name", "Four")
.relationshipFrom(3, INTERNAL_PREFIX + "R1").setProp("time", 1);
}
private class InternalTestGraphMutation extends VoidReturningCallback {
@Override
public void doInTx(GraphDatabaseService database) {
Node one = database.getNodeById(1);
one.setProperty(INTERNAL_PREFIX + "name", "NewOne");
one.removeProperty(INTERNAL_PREFIX + "count");
one.setProperty(INTERNAL_PREFIX + "tags", new String[]{"one"});
one.setProperty(INTERNAL_PREFIX + "tags", new String[]{"one", "three"});
Node two = database.getNodeById(2);
deleteNodeAndRelationships(two);
Node three = database.getNodeById(3);
three.setProperty(INTERNAL_PREFIX + "tags", "one");
three.setProperty("place", "Rome");
three.setProperty("place", "London");
Node five = database.createNode();
five.setProperty("name", "Five");
five.setProperty("size", 3L);
five.setProperty("size", 4L);
five.setProperty(INTERNAL_NODE_PROPERTY, "anything");
Relationship r = five.createRelationshipTo(three, withName(INTERNAL_PREFIX + "R2"));
r.setProperty("time", 4);
r = three.getSingleRelationship(withName("R4"), OUTGOING);
r.setProperty(INTERNAL_PREFIX + "time", 4);
r.removeProperty(INTERNAL_PREFIX + "tag");
r.setProperty(INTERNAL_PREFIX + "tags", "cool");
three.getSingleRelationship(withName(INTERNAL_PREFIX + "R3"), INCOMING).delete();
one.createRelationshipTo(three, withName(INTERNAL_PREFIX + "R1"));
//change that should not be picked up as a change
Node four = database.getNodeById(4);
four.setProperty("name", "Three");
four.setProperty("name", "Four");
}
}
}