From a605e2c4159acaab852d9c07e04c4b42f2a54ec7 Mon Sep 17 00:00:00 2001 From: Nicki Watt Date: Tue, 15 Oct 2013 07:06:51 +0100 Subject: [PATCH] DATAGRAPH-389 : Derived Queries for Labels - part 2 --- ...odeTypeRepresentationStrategyTestBase.java | 203 ++++++++++++++++ ...edNodeTypeRepresentationStrategyTests.java | 227 ++++++++---------- ...edNodeTypeRepresentationStrategyTests.java | 178 ++------------ ...ceNodeTypeRepresentationStrategyTests.java | 1 + .../query/IndexBasedStartClause.java | 2 +- .../repository/query/StartClauseFactory.java | 26 +- .../query/StartClauseFactoryTest.java | 130 ++++++++++ 7 files changed, 472 insertions(+), 295 deletions(-) create mode 100644 spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/AbstractNodeTypeRepresentationStrategyTestBase.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/StartClauseFactoryTest.java diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/AbstractNodeTypeRepresentationStrategyTestBase.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/AbstractNodeTypeRepresentationStrategyTestBase.java new file mode 100644 index 000000000..f25e6227b --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/AbstractNodeTypeRepresentationStrategyTestBase.java @@ -0,0 +1,203 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.neo4j.aspects.support.typerepresentation; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Transaction; +import org.neo4j.helpers.collection.ClosableIterable; +import org.neo4j.helpers.collection.IteratorUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.neo4j.annotation.NodeEntity; +import org.springframework.data.neo4j.aspects.support.EntityTestBase; +import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy; +import org.springframework.data.neo4j.support.Neo4jTemplate; +import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.support.mapping.StoredEntityType; +import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy; +import org.springframework.test.context.CleanContextCacheTestExecutionListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.HashSet; + +import static org.junit.Assert.assertEquals; + +public abstract class AbstractNodeTypeRepresentationStrategyTestBase extends EntityTestBase { + + @Autowired + protected NodeTypeRepresentationStrategy nodeTypeRepresentationStrategy; + + @Autowired + protected Neo4jTemplate neo4jTemplate; + @Autowired + protected Neo4jMappingContext ctx; + + protected Thing thing; + protected SubThing subThing; + protected SubThing subSubThing; + protected StoredEntityType thingType; + protected StoredEntityType subThingType; + protected StoredEntityType subSubThingType; + + @BeforeTransaction + public void cleanDb() { + super.cleanDb(); + } + + @Before + public void setUp() throws Exception { + if (thing == null) { + createThingsAndLinks(); + } + thingType = typeOf(Thing.class); + subThingType = typeOf(SubThing.class); + subSubThingType = typeOf(SubSubThing.class); + } + + @Test + @Transactional + public abstract void testPostEntityCreation() throws Exception; + + @Test + @Transactional + public abstract void testPreEntityRemoval() throws Exception; + + @Test + @Transactional + public void testFindAll() throws Exception { + + ClosableIterable allThings = nodeTypeRepresentationStrategy.findAll(thingType); + assertEquals("Did not find all things.", + new HashSet(Arrays.asList(neo4jTemplate.getPersistentState(subSubThing), neo4jTemplate.getPersistentState(subThing), neo4jTemplate.getPersistentState(thing))), + IteratorUtil.addToCollection(allThings, new HashSet())); + } + + @Test + @Transactional + public void testCountOfSuperTypeIncludesSubTypes() throws Exception { + final int EXPECTED_NUM_THINGS = 1; + final int EXPECTED_NUM_SUBTHINGS = 1; + final int EXPECTED_NUM_SUBSUBTHINGS = 1; + final int TOTAL_EXPECTED = EXPECTED_NUM_THINGS + EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS; + assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(thingType)); + } + + @Test + @Transactional + public void testCountOfSubTypeExcludesConcreteParents() throws Exception { + final int EXPECTED_NUM_SUBTHINGS = 1; + final int EXPECTED_NUM_SUBSUBTHINGS = 1; + final int TOTAL_EXPECTED = EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS; + assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(subThingType)); + } + + @Test + @Transactional + public void testGetJavaType() throws Exception { + assertEquals(thingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(thing))); + assertEquals(subThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subThing))); + assertEquals(subSubThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subSubThing))); + assertEquals(Thing.class, neo4jTemplate.getStoredJavaType(node(thing))); + assertEquals(SubThing.class, neo4jTemplate.getStoredJavaType(node(subThing))); + assertEquals(SubSubThing.class, neo4jTemplate.getStoredJavaType(node(subSubThing))); + } + + @Test + @Transactional + public void testCreateEntityAndInferType() throws Exception { + Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing)); + assertEquals(thing, newThing); + } + + @Test + @Transactional + public void testCreateEntityAndSpecifyType() throws Exception { + Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing)); + assertEquals(subThing, newThing); + } + + @Test + @Transactional + public void testProjectEntity() throws Exception { + Unrelated other = neo4jTemplate.projectTo(node(thing), Unrelated.class); + assertEquals("thing", other.getName()); + } + + protected Node node(Thing thing) { + return getNodeState(thing); + } + + protected Thing createThingsAndLinks() { + Transaction tx = graphDatabaseService.beginTx(); + try { + Node n1 = graphDatabaseService.createNode(); + thing = neo4jTemplate.setPersistentState(new Thing(),n1); + nodeTypeRepresentationStrategy.writeTypeTo(n1, neo4jTemplate.getEntityType(Thing.class)); + thing.setName("thing"); + Node n2 = graphDatabaseService.createNode(); + subThing = neo4jTemplate.setPersistentState(new SubThing(),n2); + nodeTypeRepresentationStrategy.writeTypeTo(n2, neo4jTemplate.getEntityType(SubThing.class)); + subThing.setName("subThing"); + Node n3 = graphDatabaseService.createNode(); + subSubThing = neo4jTemplate.setPersistentState(new SubSubThing(),n3); + nodeTypeRepresentationStrategy.writeTypeTo(n3, neo4jTemplate.getEntityType(SubSubThing.class)); + subThing.setName("subSubThing"); + tx.success(); + return thing; + } finally { + tx.finish(); + } + } + + @NodeEntity + public static class Unrelated { + String name; + + public String getName() { + return name; + } + } + + @NodeEntity + public static class Thing { + String name; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + public static class SubThing extends Thing { + } + + public static class SubSubThing extends SubThing { + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/IndexBasedNodeTypeRepresentationStrategyTests.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/IndexBasedNodeTypeRepresentationStrategyTests.java index 4b467d40d..753fc1083 100644 --- a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/IndexBasedNodeTypeRepresentationStrategyTests.java +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/IndexBasedNodeTypeRepresentationStrategyTests.java @@ -32,6 +32,7 @@ import org.springframework.data.neo4j.support.Neo4jTemplate; import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.support.mapping.StoredEntityType; import org.springframework.data.neo4j.support.typerepresentation.IndexBasedNodeTypeRepresentationStrategy; +import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy; import org.springframework.test.context.CleanContextCacheTestExecutionListener; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; @@ -44,83 +45,86 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.HashSet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +/** + * Tests to ensure that all scenarios involved in entity creation / reading etc + * behave as expected, specifically where the Index Based Type Representation Strategy + * is being used. + * + * The common scenarios/tests are defined in the superclass and each subclass, which + * represents a specific strategy, needs to ensure that all is when then they + * are used + */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml", "classpath:org/springframework/data/neo4j/aspects/support/IndexingTypeRepresentationStrategyOverride-context.xml"}) @TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class}) -public class IndexBasedNodeTypeRepresentationStrategyTests extends EntityTestBase { +public class IndexBasedNodeTypeRepresentationStrategyTests extends AbstractNodeTypeRepresentationStrategyTestBase { - @Autowired - private IndexBasedNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy; - @Autowired - Neo4jTemplate neo4jTemplate; - @Autowired - Neo4jMappingContext ctx; - - private Thing thing; - private SubThing subThing; - private StoredEntityType thingType; - private StoredEntityType subThingType; - - @BeforeTransaction - public void cleanDb() { - super.cleanDb(); - } - - @Before - public void setUp() throws Exception { - if (thing == null) { - createThingsAndLinks(); - } - thingType = typeOf(Thing.class); - subThingType = typeOf(SubThing.class); + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + assertThat("The tests in this class should be configured to use the Index " + + "based Type Representation Strategy, however it is not ... ", + nodeTypeRepresentationStrategy, + instanceOf(IndexBasedNodeTypeRepresentationStrategy.class)); } @Test @Transactional + @Override public void testPostEntityCreation() throws Exception { Index typesIndex = graphDatabaseService.index().forNodes(IndexBasedNodeTypeRepresentationStrategy.INDEX_NAME); - IndexHits thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias()); - assertEquals(set(node(thing), node(subThing)), IteratorUtil.addToCollection((Iterable)thingHits, new HashSet())); - IndexHits subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias()); - assertEquals(node(subThing), subThingHits.getSingle()); - assertEquals(thingType.getAlias(), node(thing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME)); - assertEquals(subThingType.getAlias(), node(subThing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME)); + + // Things + IndexHits thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias()); + assertEquals(set(node(thing), node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable)thingHits, new HashSet())); + + // SubThings + IndexHits subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias()); + assertEquals(set(node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable)subThingHits, new HashSet())); + + // SubSubThings + IndexHits subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias()); + assertEquals(node(subSubThing), subSubThingHits.getSingle()); + + // General + assertEquals(thingType.getAlias(), node(thing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME)); + assertEquals(subSubThingType.getAlias(), node(subSubThing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME)); thingHits.close(); - subThingHits.close(); + subSubThingHits.close(); } @Test + @Override public void testPreEntityRemoval() throws Exception { manualCleanDb(); createThingsAndLinks(); Index typesIndex; - IndexHits thingHits; - IndexHits subThingHits; try (Transaction tx = graphDatabaseService.beginTx()) { typesIndex = graphDatabaseService.index().forNodes(IndexBasedNodeTypeRepresentationStrategy.INDEX_NAME); tx.success(); } - try (Transaction tx = graphDatabaseService.beginTx()) { - nodeTypeRepresentationStrategy.preEntityRemoval(node(thing)); - tx.success(); - } + testPreEntityRemovalOfThing(typesIndex); + testPreEntityRemovalOfSubThing(typesIndex); + testPreEntityRemovalOfSubSubThing(typesIndex); + } - try (Transaction tx = graphDatabaseService.beginTx()) { - thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias()); - assertEquals(node(subThing), thingHits.getSingle()); - subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias()); - assertEquals(node(subThing), subThingHits.getSingle()); - tx.success(); - } + private void testPreEntityRemovalOfSubSubThing(Index typesIndex) { + IndexHits thingHits; + IndexHits subThingHits; + IndexHits subSubThingHits; + // 3. Remove SubSubThing try (Transaction tx = graphDatabaseService.beginTx()) { - nodeTypeRepresentationStrategy.preEntityRemoval(node(subThing)); + nodeTypeRepresentationStrategy.preEntityRemoval(node(subSubThing)); tx.success(); } @@ -129,99 +133,60 @@ public class IndexBasedNodeTypeRepresentationStrategyTests extends EntityTestBas assertNull(thingHits.getSingle()); subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias()); assertNull(subThingHits.getSingle()); + subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias()); + assertNull(subSubThingHits.getSingle()); tx.success(); } - } - - @Test - @Transactional - public void testFindAll() throws Exception { - - assertEquals("Did not find all things.", - new HashSet(Arrays.asList(neo4jTemplate.getPersistentState(subThing), neo4jTemplate.getPersistentState(thing))), - IteratorUtil.addToCollection(nodeTypeRepresentationStrategy.findAll(thingType), new HashSet())); - } - - @Test - @Transactional - public void testCount() throws Exception { - assertEquals(2, nodeTypeRepresentationStrategy.count(thingType)); - } - - @Test - @Transactional - public void testGetJavaType() throws Exception { - assertEquals(thingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(thing))); - assertEquals(subThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subThing))); - assertEquals(Thing.class, neo4jTemplate.getStoredJavaType(node(thing))); - assertEquals(SubThing.class, neo4jTemplate.getStoredJavaType(node(subThing))); - } - - @Test - @Transactional - public void testCreateEntityAndInferType() throws Exception { - Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing)); - assertEquals(thing, newThing); } + private void testPreEntityRemovalOfSubThing(Index typesIndex) { + IndexHits thingHits; + IndexHits subThingHits; + IndexHits subSubThingHits; - @Test - @Transactional - public void testCreateEntityAndSpecifyType() throws Exception { - Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing)); - assertEquals(subThing, newThing); - } - - @Test - @Transactional - public void testProjectEntity() throws Exception { - Unrelated other = neo4jTemplate.projectTo(node(thing), Unrelated.class); - assertEquals("thing", other.getName()); - } - - private Node node(Thing thing) { - return getNodeState(thing); - } - - private Thing createThingsAndLinks() { - Transaction tx = graphDatabaseService.beginTx(); - try { - Node n1 = graphDatabaseService.createNode(); - thing = neo4jTemplate.setPersistentState(new Thing(),n1); - nodeTypeRepresentationStrategy.writeTypeTo(n1, neo4jTemplate.getEntityType(Thing.class)); - thing.setName("thing"); - Node n2 = graphDatabaseService.createNode(); - subThing = neo4jTemplate.setPersistentState(new SubThing(),n2); - nodeTypeRepresentationStrategy.writeTypeTo(n2, neo4jTemplate.getEntityType(SubThing.class)); - subThing.setName("subThing"); - tx.success(); - return thing; - } finally { - tx.finish(); - } - } - - @NodeEntity - public static class Unrelated { - String name; - - public String getName() { - return name; - } - } - - @NodeEntity - public static class Thing { - String name; - - public void setName(String name) { - this.name = name; + // 1. Remove SubThing + try (Transaction tx = graphDatabaseService.beginTx()) { + nodeTypeRepresentationStrategy.preEntityRemoval(node(subThing)); + tx.success(); } - public String getName() { - return name; + try (Transaction tx = graphDatabaseService.beginTx()) { + thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias()); + assertEquals(node(subSubThing), thingHits.getSingle()); + + subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias()); + assertEquals(node(subSubThing), subThingHits.getSingle()); + + subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias()); + assertEquals(node(subSubThing), subSubThingHits.getSingle()); + tx.success(); } + } - public static class SubThing extends Thing { + private void testPreEntityRemovalOfThing(Index typesIndex) { + IndexHits thingHits; + IndexHits subThingHits; + IndexHits subSubThingHits; + + // 1. Remove Thing + try (Transaction tx = graphDatabaseService.beginTx()) { + nodeTypeRepresentationStrategy.preEntityRemoval(node(thing)); + tx.success(); + } + + try (Transaction tx = graphDatabaseService.beginTx()) { + thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias()); + assertEquals(set(node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable)thingHits, new HashSet())); + + subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias()); + assertEquals(set(node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable)subThingHits, new HashSet())); + + subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias()); + assertEquals(node(subSubThing), subSubThingHits.getSingle()); + tx.success(); + } + } + + } diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/LabelBasedNodeTypeRepresentationStrategyTests.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/LabelBasedNodeTypeRepresentationStrategyTests.java index d92bbcd62..1f82cb775 100644 --- a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/LabelBasedNodeTypeRepresentationStrategyTests.java +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/LabelBasedNodeTypeRepresentationStrategyTests.java @@ -19,66 +19,42 @@ package org.springframework.data.neo4j.aspects.support.typerepresentation; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.PropertyContainer; -import org.neo4j.graphdb.Transaction; -import org.neo4j.helpers.collection.ClosableIterable; -import org.neo4j.helpers.collection.IteratorUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.neo4j.annotation.NodeEntity; -import org.springframework.data.neo4j.aspects.support.EntityTestBase; -import org.springframework.data.neo4j.support.Neo4jTemplate; -import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext; -import org.springframework.data.neo4j.support.mapping.StoredEntityType; import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy; import org.springframework.test.context.CleanContextCacheTestExecutionListener; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; -import org.springframework.test.context.transaction.BeforeTransaction; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.transaction.annotation.Transactional; -import java.util.Arrays; -import java.util.HashSet; - -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +/** + * Tests to ensure that all scenarios involved in entity creation / reading etc + * behave as expected, specifically where the Label Type Representation Strategy + * is being used. + * + * The common scenarios/tests are defined in the superclass and each subclass, which + * represents a specific strategy, needs to ensure that all is when then they + * are used + */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml", "classpath:org/springframework/data/neo4j/aspects/support/LabelingTypeRepresentationStrategyOverride-context.xml"}) @TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class}) -public class LabelBasedNodeTypeRepresentationStrategyTests extends EntityTestBase { +public class LabelBasedNodeTypeRepresentationStrategyTests extends AbstractNodeTypeRepresentationStrategyTestBase { - @Autowired - private LabelBasedNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy; - @Autowired - Neo4jTemplate neo4jTemplate; - @Autowired - Neo4jMappingContext ctx; - - private Thing thing; - private SubThing subThing; - private SubThing subSubThing; - private StoredEntityType thingType; - private StoredEntityType subThingType; - private StoredEntityType subSubThingType; - - @BeforeTransaction - public void cleanDb() { - super.cleanDb(); - } - - @Before - public void setUp() throws Exception { - if (thing == null) { - createThingsAndLinks(); - } - thingType = typeOf(Thing.class); - subThingType = typeOf(SubThing.class); - subSubThingType = typeOf(SubSubThing.class); + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + assertThat("The tests in this class should be configured to use the Label " + + "based Type Representation Strategy, however it is not ... ", + nodeTypeRepresentationStrategy, + instanceOf(LabelBasedNodeTypeRepresentationStrategy.class)); } @Test @@ -88,122 +64,10 @@ public class LabelBasedNodeTypeRepresentationStrategyTests extends EntityTestBas } @Test + @Transactional public void testPreEntityRemoval() throws Exception { // preEntityRemoval is a no op method, so nothing to test here! } - @Test - @Transactional - public void testFindAll() throws Exception { - ClosableIterable allThings = nodeTypeRepresentationStrategy.findAll(thingType); - assertEquals("Did not find all things.", - new HashSet(Arrays.asList(neo4jTemplate.getPersistentState(subSubThing), neo4jTemplate.getPersistentState(subThing), neo4jTemplate.getPersistentState(thing))), - IteratorUtil.addToCollection(allThings, new HashSet())); - } - - @Test - @Transactional - public void testCountOfSuperTypeIncludesSubTypes() throws Exception { - final int EXPECTED_NUM_THINGS = 1; - final int EXPECTED_NUM_SUBTHINGS = 1; - final int EXPECTED_NUM_SUBSUBTHINGS = 1; - final int TOTAL_EXPECTED = EXPECTED_NUM_THINGS + EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS; - assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(thingType)); - } - - @Test - @Transactional - public void testCountOfSubTypeExcludesConcreteParents() throws Exception { - final int EXPECTED_NUM_SUBTHINGS = 1; - final int EXPECTED_NUM_SUBSUBTHINGS = 1; - final int TOTAL_EXPECTED = EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS; - assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(subThingType)); - } - - @Test - @Transactional - public void testGetJavaType() throws Exception { - assertEquals(thingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(thing))); - assertEquals(subThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subThing))); - assertEquals(subSubThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subSubThing))); - assertEquals(Thing.class, neo4jTemplate.getStoredJavaType(node(thing))); - assertEquals(SubThing.class, neo4jTemplate.getStoredJavaType(node(subThing))); - assertEquals(SubSubThing.class, neo4jTemplate.getStoredJavaType(node(subSubThing))); - } - - @Test - @Transactional - public void testCreateEntityAndInferType() throws Exception { - Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing)); - assertEquals(thing, newThing); - } - - @Test - @Transactional - public void testCreateEntityAndSpecifyType() throws Exception { - Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing)); - assertEquals(subThing, newThing); - } - - @Test - @Transactional - public void testProjectEntity() throws Exception { - Unrelated other = neo4jTemplate.projectTo(node(thing), Unrelated.class); - assertEquals("thing", other.getName()); - } - - private Node node(Thing thing) { - return getNodeState(thing); - } - - private Thing createThingsAndLinks() { - Transaction tx = graphDatabaseService.beginTx(); - try { - Node n1 = graphDatabaseService.createNode(); - thing = neo4jTemplate.setPersistentState(new Thing(),n1); - nodeTypeRepresentationStrategy.writeTypeTo(n1, neo4jTemplate.getEntityType(Thing.class)); - thing.setName("thing"); - Node n2 = graphDatabaseService.createNode(); - subThing = neo4jTemplate.setPersistentState(new SubThing(),n2); - nodeTypeRepresentationStrategy.writeTypeTo(n2, neo4jTemplate.getEntityType(SubThing.class)); - subThing.setName("subThing"); - Node n3 = graphDatabaseService.createNode(); - subSubThing = neo4jTemplate.setPersistentState(new SubSubThing(),n3); - nodeTypeRepresentationStrategy.writeTypeTo(n3, neo4jTemplate.getEntityType(SubSubThing.class)); - subThing.setName("subSubThing"); - tx.success(); - return thing; - } finally { - tx.finish(); - } - } - - @NodeEntity - public static class Unrelated { - String name; - - public String getName() { - return name; - } - } - - @NodeEntity - public static class Thing { - String name; - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - - public static class SubThing extends Thing { - } - - public static class SubSubThing extends SubThing { - } } diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategyTests.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategyTests.java index 1af327fd1..a0a3123da 100644 --- a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategyTests.java +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategyTests.java @@ -59,6 +59,7 @@ import static org.springframework.data.neo4j.aspects.Person.persistedPerson; @ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml", "classpath:org/springframework/data/neo4j/aspects/support/SubReferenceTypeRepresentationStrategyOverride-context.xml"}) @TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class}) +// TODO extend AbstractNodeTypeRepresentationStrategyTestBase public class SubReferenceNodeTypeRepresentationStrategyTests extends EntityTestBase { @Autowired diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/IndexBasedStartClause.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/IndexBasedStartClause.java index 38d61ff87..4ad36f913 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/IndexBasedStartClause.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/IndexBasedStartClause.java @@ -17,7 +17,7 @@ package org.springframework.data.neo4j.repository.query; /** * Abstract class which represents a start clause which makes - * use of an indexof some sort. + * use of an index of some sort. * * @author Nicki Watt */ diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClauseFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClauseFactory.java index 59562f8a2..4ebb1e47e 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClauseFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/StartClauseFactory.java @@ -17,6 +17,7 @@ package org.springframework.data.neo4j.repository.query; import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.repository.query.parser.Part; +import org.springframework.util.Assert; import java.util.EnumSet; import java.util.List; @@ -30,16 +31,29 @@ import java.util.List; public class StartClauseFactory { /** - * At present, Multiple parts are always assumed to result in a - * a FullTextIndexBasedStartClause + * Create a start clause from multiple parts * @param partInfos The various parts which the start clause * needs to be created around/for. - * @return A appropriate StartClause + * @return An appropriate StartClause */ public static StartClause create(List partInfos) { - return (partInfos.size() == 1) - ? create(partInfos.get(0)) - : new FullTextIndexBasedStartClause(partInfos); + Assert.notEmpty(partInfos); + if (partInfos.size() == 1) { + return create(partInfos.get(0)); + } else if (areAllIndexedAndHaveSameIdentifiers(partInfos)) { + return new FullTextIndexBasedStartClause(partInfos); + } + throw new IllegalArgumentException("Cannot determine an appropriate Start Clause for multiple partInfos provided"); + } + + private static boolean areAllIndexedAndHaveSameIdentifiers(List partInfos) { + // We use the first part to compare the others against + PartInfo firstPart = partInfos.get(0); + for (PartInfo partInfo : partInfos) { + if (!partInfo.isIndexed()) return false; + if (!partInfo.sameIdentifier(firstPart)) return false; + } + return true; } /** diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/StartClauseFactoryTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/StartClauseFactoryTest.java new file mode 100644 index 000000000..fe0546bb8 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/StartClauseFactoryTest.java @@ -0,0 +1,130 @@ +package org.springframework.data.neo4j.repository.query; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.repository.query.parser.Part; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +/** + * Unit Test(s) of the StartClauseFactory + */ +public class StartClauseFactoryTest { + + PartInfo simpleIndexedPartInfo1; + PartInfo simpleIndexedPartInfo2; + PartInfo searchableIndexedPartInfo; + PartInfo fullTextIndexedPartInfo; + PartInfo idBasedPartInfo; + PartInfo relBasedPartInfo; + + @Before + public void setUp() throws Exception { + + // TODO - Perhaps rather use Spring to setup rather than mock + simpleIndexedPartInfo1 = Mockito.mock(PartInfo.class); + when(simpleIndexedPartInfo1.isIndexed()).thenReturn(true); + when(simpleIndexedPartInfo1.isFullText()).thenReturn(false); + + simpleIndexedPartInfo2 = Mockito.mock(PartInfo.class); + when(simpleIndexedPartInfo2.isIndexed()).thenReturn(true); + when(simpleIndexedPartInfo2.isFullText()).thenReturn(false); + + searchableIndexedPartInfo = Mockito.mock(PartInfo.class); + when(searchableIndexedPartInfo.isIndexed()).thenReturn(true); + when(searchableIndexedPartInfo.isFullText()).thenReturn(false); + when(searchableIndexedPartInfo.getType()).thenReturn(Part.Type.STARTING_WITH); + + fullTextIndexedPartInfo = Mockito.mock(PartInfo.class); + when(fullTextIndexedPartInfo.isIndexed()).thenReturn(true); + when(fullTextIndexedPartInfo.isFullText()).thenReturn(true); + + Neo4jPersistentProperty leafProperty1 = Mockito.mock(Neo4jPersistentProperty.class); + idBasedPartInfo = Mockito.mock(PartInfo.class); + when(idBasedPartInfo.isIndexed()).thenReturn(false); + when(idBasedPartInfo.getLeafProperty()).thenReturn(leafProperty1); + when(leafProperty1.isRelationship()).thenReturn(false); + when(leafProperty1.isIdProperty()).thenReturn(true); + + Neo4jPersistentProperty leafProperty2 = Mockito.mock(Neo4jPersistentProperty.class); + relBasedPartInfo = Mockito.mock(PartInfo.class); + when(relBasedPartInfo.isIndexed()).thenReturn(false); + when(relBasedPartInfo.getLeafProperty()).thenReturn(leafProperty2); + when(leafProperty2.isRelationship()).thenReturn(true); + when(leafProperty2.isIdProperty()).thenReturn(false); + + + } + + @Test + public void testCreateExactIndexBasedStartClause() { + StartClause startClause = StartClauseFactory.create(simpleIndexedPartInfo1); + assertTrue(startClause instanceof ExactIndexBasedStartClause); + } + + @Test + public void testCreateFullTextIndexBasedStartClauseWhenFullyIndexed() { + StartClause startClause = StartClauseFactory.create(fullTextIndexedPartInfo); + assertTrue(startClause instanceof FullTextIndexBasedStartClause); + } + + @Test + public void testCreateFullTextIndexBasedStartClauseWhenNormallyIndexedButWithSearchLikePart() { + StartClause startClause = StartClauseFactory.create(searchableIndexedPartInfo); + assertTrue(startClause instanceof FullTextIndexBasedStartClause); + } + + @Test + public void testCreateGraphIdStartClauseWhenIdPropertyPathInfo() { + StartClause startClause = StartClauseFactory.create(idBasedPartInfo); + assertTrue(startClause instanceof GraphIdStartClause); + } + + @Test + public void testCreateGraphIdStartClauseWhenIsRelationship() { + // This is how it was in the original code, not sure why + // relationships should always result in a GraphIdStartClause + // TODO - find out why + StartClause startClause = StartClauseFactory.create(relBasedPartInfo); + assertTrue(startClause instanceof GraphIdStartClause); + } + + @Test + public void testCreateFullTextIndexBasedStartClauseWhenMultiplePartsProvidedWhichAreAllIndexedWithSameId() { + List partInfos = new ArrayList(); + partInfos.add(simpleIndexedPartInfo1); + partInfos.add(simpleIndexedPartInfo2); + when(simpleIndexedPartInfo1.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true); + when(simpleIndexedPartInfo2.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true); + + StartClause startClause = StartClauseFactory.create(partInfos); + assertTrue(startClause instanceof FullTextIndexBasedStartClause); + } + + @Test(expected = IllegalArgumentException.class) + public void testExceptionThrownWhenProvidingMultiplePartsWhichAreAllIndexedButDiffId() { + List partInfos = new ArrayList(); + partInfos.add(simpleIndexedPartInfo1); + partInfos.add(simpleIndexedPartInfo2); + when(simpleIndexedPartInfo1.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true); + when(simpleIndexedPartInfo2.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(false); + StartClauseFactory.create(partInfos); + } + + @Test(expected = IllegalArgumentException.class) + public void testExceptionThrownWhenProvidingMultiplePartsWhichAreNotAllIndexed() { + List partInfos = new ArrayList(); + partInfos.add(simpleIndexedPartInfo1); + partInfos.add(idBasedPartInfo); + when(simpleIndexedPartInfo1.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true); + StartClauseFactory.create(partInfos); + } + + +}