From 6518382c16826cf1582cf894e0e994e11138d3dc Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Sun, 10 May 2015 14:32:27 +0100 Subject: [PATCH] OGM-783 [Neo4j] Create DSL to assert the mapping in tests --- .../neo4j/test/dsl/GraphAssertions.java | 74 ++++++++ .../test/dsl/NodeForGraphAssertions.java | 102 +++++++++++ .../RelationshipsChainForGraphAssertions.java | 158 ++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/GraphAssertions.java create mode 100644 neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/NodeForGraphAssertions.java create mode 100644 neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/RelationshipsChainForGraphAssertions.java diff --git a/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/GraphAssertions.java b/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/GraphAssertions.java new file mode 100644 index 0000000000..8f5ca174be --- /dev/null +++ b/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/GraphAssertions.java @@ -0,0 +1,74 @@ +/* + * Hibernate OGM, Domain model persistence for NoSQL datastores + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.ogm.datastore.neo4j.test.dsl; + +import static org.fest.assertions.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.fest.assertions.Fail; +import org.neo4j.cypher.javacompat.ExecutionEngine; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.ResourceIterator; + +/** + * Assertion methods to check the mapping of nodes and relationships in Neo4j. + * + * @author Davide D'Alto + */ +public class GraphAssertions { + + public static NodeForGraphAssertions node(String alias, String... labels) { + return new NodeForGraphAssertions( alias, labels); + } + + public static void assertThatExists(ExecutionEngine engine, NodeForGraphAssertions node) throws Exception { + String nodeAsCypher = node.toCypher(); + String query = "MATCH " + nodeAsCypher + " RETURN " + node.getAlias(); + ResourceIterator columnAs = engine.execute( query, node.getParams() ).columnAs( node.getAlias() ); + assertThat( columnAs.hasNext() ).as( "Node [" + node.getAlias() + "] not found, Looked for " + nodeAsCypher + " with parameters: " + node.getParams() ).isTrue(); + + PropertyContainer propertyContainer = (PropertyContainer) columnAs.next(); + Iterable propertyKeys = propertyContainer.getPropertyKeys(); + List unexpectedProperties = new ArrayList(); + Set expectedProperties = node.getProperties().keySet(); + for ( Iterator iterator = propertyKeys.iterator(); iterator.hasNext(); ) { + String actual = iterator.next(); + if ( !expectedProperties.contains( actual ) ) { + unexpectedProperties.add( actual ); + } + } + List missingProperties = new ArrayList(); + if ( expectedProperties != null ) { + for ( String expected : expectedProperties ) { + if ( !propertyContainer.hasProperty( expected ) ) { + missingProperties.add( expected ); + } + } + } + assertThat( unexpectedProperties ).as( "Unexpected properties for node [" + node.getAlias() + "]" ).isEmpty(); + assertThat( missingProperties ).as( "Missing properties for node [" + node.getAlias() + "]" ).isEmpty(); + if ( columnAs.hasNext() ) { + Fail.fail( "Unexpected result returned: " + columnAs.next() ); + } + } + + public static void assertThatExists(ExecutionEngine engine, RelationshipsChainForGraphAssertions relationship) throws Exception { + String relationshipAsCypher = relationship.toCypher(); + NodeForGraphAssertions node = relationship.getStart(); + String query = "MATCH " + relationshipAsCypher + " RETURN " + node.getAlias(); + ResourceIterator columnAs = engine.execute( query, relationship.getParams() ).columnAs( node.getAlias() ); + assertThat( columnAs.hasNext() ).as( "Relationships not found, Looked for " + relationshipAsCypher + " with parameters: " + relationship.getParams() ).isTrue(); + columnAs.next(); + if ( columnAs.hasNext() ) { + Fail.fail( "Unexpected result returned: " + columnAs.next() ); + } + } +} diff --git a/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/NodeForGraphAssertions.java b/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/NodeForGraphAssertions.java new file mode 100644 index 0000000000..78ca916b0c --- /dev/null +++ b/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/NodeForGraphAssertions.java @@ -0,0 +1,102 @@ +/* + * Hibernate OGM, Domain model persistence for NoSQL datastores + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.ogm.datastore.neo4j.test.dsl; + +import static org.hibernate.ogm.datastore.neo4j.query.parsing.cypherdsl.impl.CypherDSL.escapeIdentifier; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * /** + * A DSL to define a node like the following: + *

+ *

+ * (n:ENTITY:StoryGame {id: {n}.id})
+ * 
+ * + * @author Davide D'Alto + */ +public class NodeForGraphAssertions { + + private final String alias; + private final String[] labels; + private final Map properties = new HashMap(); + private final Map params = new HashMap(); + + public NodeForGraphAssertions(String alias, String[] labels) { + this.alias = alias; + this.labels = labels; + this.params.put( alias, properties ); + } + + public NodeForGraphAssertions property(String property, Object value) { + properties.put( property, value ); + return this; + } + + public RelationshipsChainForGraphAssertions relationshipTo(NodeForGraphAssertions endNode, String relationshipType) { + Map emptyMap = Collections.emptyMap(); + return relationshipTo( endNode, relationshipType, emptyMap ); + } + + public RelationshipsChainForGraphAssertions relationshipTo(NodeForGraphAssertions endNode, String relationshipType, Map properties) { + return new RelationshipsChainForGraphAssertions( this, endNode, relationshipType ); + } + + public String toCypher() { + StringBuilder builder = new StringBuilder(); + builder.append( "(" ); + builder.append( alias ); + for ( String label : labels ) { + builder.append( ":" ); + builder.append( label ); + } + if ( !properties.isEmpty() ) { + builder.append( " {" ); + int index = 0; + for ( String property : properties.keySet() ) { + escapeIdentifier( builder, property ); + builder.append( ": " ); + builder.append( "{" ); + builder.append( alias ); + builder.append( "}" ); + builder.append( "." ); + escapeIdentifier( builder, property ); + index++; + if ( index < properties.size() ) { + builder.append( ", " ); + } + } + builder.append( "}" ); + } + builder.append( ")" ); + return builder.toString(); + } + + /** + * The node alias. + */ + public String getAlias() { + return alias; + } + + /** + * The map with the values for the parameters in the cypher representation of the node. + */ + public Map getParams() { + return Collections.unmodifiableMap( params ); + } + + /** + * Returns the properties for the node. + */ + public Map getProperties() { + return Collections.unmodifiableMap( properties ); + } +} diff --git a/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/RelationshipsChainForGraphAssertions.java b/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/RelationshipsChainForGraphAssertions.java new file mode 100644 index 0000000000..02181be6b4 --- /dev/null +++ b/neo4j/src/test/java/org/hibernate/ogm/datastore/neo4j/test/dsl/RelationshipsChainForGraphAssertions.java @@ -0,0 +1,158 @@ +/* + * Hibernate OGM, Domain model persistence for NoSQL datastores + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.ogm.datastore.neo4j.test.dsl; + +import static org.hibernate.ogm.datastore.neo4j.query.parsing.cypherdsl.impl.CypherDSL.escapeIdentifier; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A DSL to define a chain of relationships like the following: + *

+ *

+ * () -> [r1:type1 {property: {r1}.property}] -> () -[r2:type2]-> ()
+ * 
+ * + * @author Davide D'Alto + */ +public class RelationshipsChainForGraphAssertions { + + private final NodeForGraphAssertions start; + private final List stream = new ArrayList(); + private final Map params = new HashMap(); + + public RelationshipsChainForGraphAssertions(NodeForGraphAssertions start, NodeForGraphAssertions end, String relationshipType) { + this.start = start; + this.params.put( start.getAlias(), start.getProperties() ); + addToStream( end, relationshipType ); + } + + private void addToStream(NodeForGraphAssertions end, String relationshipType) { + stream.add( new NextRelationship( end, relationshipType, "r" + stream.size() ) ); + params.put( end.getAlias(), end.getProperties() ); + } + + public RelationshipsChainForGraphAssertions relationshipTo(NodeForGraphAssertions next, String relationshipType) { + addToStream( next, relationshipType ); + return this; + } + + /** + * Get the starting node of the current chain of relationships. + */ + public NodeForGraphAssertions getStart() { + return start; + } + + /** + * The map with the values for the parameters in the cypher representation of the current chain. + */ + public Map getParams() { + return params; + } + + /** + * The number of relationships in the chain. + */ + public int getSize() { + return stream.size(); + } + + /** + * Define a property on the last relationship of the chain. + */ + public RelationshipsChainForGraphAssertions property(String name, Object value) { + NextRelationship relationship = stream.get( stream.size() - 1 ); + @SuppressWarnings("unchecked") + Map properties = (Map) params.get( relationship.getAlias() ); + if ( properties == null ) { + params.put( relationship.getAlias(), relationship.getProperties() ); + } + relationship.addProperty( name, value ); + return this; + } + + /** + * Returns the cypher representation of the chain of relationships, + *

+ * Example: + *

+	 * (n1:ENTITY {id: {n1}.id}) -[r0:type0 {property: {r0}.property}]-> (n2:EMBEDDED {property: {n2}.property}) -[r1:type1]-> (n3:EMBEDDED)
+	 * 
+	 */
+	public String toCypher() {
+		StringBuilder builder = new StringBuilder();
+		builder.append( start.toCypher() );
+		for ( NextRelationship relationship : stream ) {
+			builder.append( " -[" );
+			if ( !relationship.getProperties().isEmpty() ) {
+				builder.append( relationship.getAlias() );
+			}
+			builder.append( ":" );
+			builder.append( relationship.getRelationshipType() );
+			if ( !relationship.getProperties().isEmpty() ) {
+				builder.append( " {" );
+				boolean first = true;
+				for ( String property : relationship.getProperties().keySet() ) {
+					if ( first ) {
+						first = false;
+					}
+					else {
+						builder.append( ", " );
+					}
+					escapeIdentifier( builder, property );
+					builder.append( ": {" );
+					builder.append( relationship.getAlias() );
+					builder.append( "}." );
+					escapeIdentifier( builder, property );
+				}
+				builder.append( " }" );
+			}
+			builder.append( "]-> " );
+			builder.append( relationship.getEnd().toCypher() );
+		}
+		return builder.toString();
+	}
+
+	private static class NextRelationship {
+
+		private final NodeForGraphAssertions end;
+		private final String relationshipType;
+		private final Map properties = new HashMap();
+		private final String alias;
+
+		public NextRelationship(NodeForGraphAssertions to, String relationshipType, String alias) {
+			this.end = to;
+			this.relationshipType = relationshipType;
+			this.alias = alias;
+		}
+
+		public String getAlias() {
+			return alias;
+		}
+
+		public Map getProperties() {
+			return properties;
+		}
+
+		public NodeForGraphAssertions getEnd() {
+			return end;
+		}
+
+		public String getRelationshipType() {
+			return relationshipType;
+		}
+
+		public void addProperty( String name, Object value ) {
+			properties.put( name, value );
+		}
+	}
+
+}