John Deverall
John Deverall

Reputation: 6282

Integration testing with SDN4 repositories using neo4j-spatial cypher queries

I'm starting to use neo4j-spatial in some of my code. I thought I would be able to integration test neo4j-spatial code by including the spatial server lib as a maven dependency in my project. This has not worked for me though. I can't find any documentation anywhere on this.

How can I get my integration tests to work?

Any tips anyone? :)

Just to give an idea of what I'm doing, I've pasted a segment of my controller, service and repository code below, with the final code posting being my tests that don't work against an embedded TestServer.

Repository

package nz.co.domain.core.repository.location;

import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import nz.co.domain.model.pojos.Location;

@Repository
public interface LocationRepository extends GraphRepository<Location> {

    @Query("match (n:Location {domainId : {domainId}}) with n call spatial.addNode({layerName}, n) yield node return node;")
    public Location indexLocation(@Param("domainId") String locationId, @Param("layerName") String layerName);

    @Query("call spatial.withinDistance({layerName},{longitude: {longitude},latitude: {latitude}}, {rangeInKms});")
    public Iterable<Location> findLocationsWithinRange(@Param("longitude") String longitude, @Param("latitude") String latitude, @Param("rangeInKms") String rangeInKms, @Param("layerName") String layerName);

    @Query("call spatial.addPointLayer({layerName});")
    public void createLayer(@Param("layerName") String layerName);

    @Query("match ()-[:LAYER]->(n) where n.layer = {layerName} return count(n) > 0;")
    public boolean hasLayer(@Param("layerName") String layerName);

    public Location findByDomainSpecificId(String domainSpecificId);

    public Location findByGooglePlaceId(String googlePlaceId);

}

Service

package nz.co.domain.core.location;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import nz.co.domain.core.repository.location.LocationRepository;
import nz.co.domain.model.UniqueIDGenerator;
import nz.co.domain.model.pojos.Location;

@Service
public class LocationService {

    @Inject
    private LocationRepository locationRepository;

    public void createLayer(String layerName) { 
        locationRepository.createLayer(layerName);
    }

    public Location createLocation(double latitude, double longitude, String googlePlaceId, String layerName) { 
        Location location = new Location(UniqueIDGenerator.randomID(), googlePlaceId, latitude, longitude);
        boolean hasLayer = locationRepository.hasLayer(layerName);
        if (!hasLayer) { 
            locationRepository.createLayer(layerName);
        }

        Location preExistingLocation = locationRepository.findByGooglePlaceId(googlePlaceId);

        if (preExistingLocation == null) { 
            location = locationRepository.save(location);
            location = locationRepository.indexLocation(location.getDomainId(), layerName);
        } else { 
            location = preExistingLocation;
        }

        return location;
    }

    public Iterable<Location> findLocationsWithinRange(String latitude, String longitude, String rangeInKms, String layerName) { 
        return locationRepository.findLocationsWithinRange(longitude, latitude, rangeInKms, layerName);
    }

    public Location loadLocationByGooglePlaceId(String googlePlaceId) {
        return locationRepository.findByGooglePlaceId(googlePlaceId);
    }

    public Location loadLocationByDomainId(String domainId) {
        return locationRepository.findByDomainId(domainId);
    }

}

Controller

...

/**
     * Add location. 
     * @param profiletypes
     * @param profileId
     * @return
     */
    @RequestMapping(value = "/{profileType}/{profileId}/location", method = RequestMethod.POST, produces = "application/hal+json")
    public HttpEntity<LocationResource> addLocation(@PathVariable String profileType,
            @PathVariable String profileId, @RequestBody CreateLocationRequest locationRequest, @ApiIgnore LocationResourceAssembler locationResourceAssembler) {

        Profile profile = profileService.loadProfileByDomainId(profileId);

        Location location = locationService.createLocation(locationRequest.getLatitude(), locationRequest.getLongitude(), locationRequest.getGooglePlaceId(), profileType + "-layer");

        profile.setLocation(location);
        profileService.save(profile);

        location = locationService.loadLocationByGooglePlaceId(location.getGooglePlaceId());

        LocationResource resource = locationResourceAssembler.toResource(location);
        return new ResponseEntity<>(resource, HttpStatus.CREATED) ;
    }

...

Service test

(It is my tests here that I can't get working as part of a standard build against an embedded TestServer)

package nz.co.domain.core.location;

import javax.inject.Inject;

import org.junit.Ignore;
import org.junit.Test;

import nz.co.domain.core.AbstractTest;
import nz.co.domain.model.pojos.Location;

public class LocationServiceTest extends AbstractTest {

    @Inject
    private LocationService locationService;

    @Test
    public void indexLocationTest() { 

        // The Rogue and Vagabond
        Location rogueAndVagabond = locationService.createLocation(51.469150, 7.23212, "ChIJmwfKGdivOG0R9eTCVFOngnU", "test-layer");

        /* more test code here */

        // Te Papa Museum
        Location tePapaMuseum = locationService.createLocation(-41.289964, 174.778354, "ChIJfxn9AdGvOG0RpLRGGO3tRX8", "test-layer");

        /* more test code here */

        // Porirua Club
        Location poriruaClub = locationService.createLocation(-41.136048, 174.836409, "ChIJ9wl16m1TP20R3G3npuEokak", "test-layer");

        /* more test code here */

        Iterable<Location> findLocationsWithinRange = locationService.findLocationsWithinRange("-41.289964", "longitude", "5", "test-layer");

       /* more test code here */

    }
}

Upvotes: 1

Views: 92

Answers (2)

Christoph M&#246;bius
Christoph M&#246;bius

Reputation: 1402

We're using SDN 4.1.2, OGM 2.0.3 and I got my tests working with this setup:

Added the maven dependency using the description on the github page. By the time of writing this comment the necessary addition to the pom.xml was

<repositories>
    <repository>
        <id>neo4j-contrib-releases</id>
        <url>https://raw.github.com/neo4j-contrib/m2/master/releases</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>neo4j-contrib-snapshots</id>
        <url>https://raw.github.com/neo4j-contrib/m2/master/snapshots</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>
<!-- ... -->
<dependency>
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-spatial</artifactId>
    <version>0.19-neo4j-3.0.3</version>
</dependency>

Defining the following abstract classes for all spatial-related tests:

@RunWith(SpringJUnit4ClassRunner::class)
@SpringApplicationConfiguration(classes = arrayOf(Application::class))
@ActiveProfiles("test")
@Transactional
abstract class AbstractIntegrationTests() {}

and

@WebAppConfiguration
abstract class AbstractIntegrationTestsWithProcedures : AbstractIntegrationTests() {

    lateinit var databaseService: GraphDatabaseService
    val layerName = "layer"

    @Before()
    open fun before() {
        if (ProcedureTestUtil.registeredProcedures.contains(SpatialProcedures::class.java)) {
            return
        }
        val driver = Components.driver()
        if (driver is EmbeddedDriver) {
            databaseService = driver.graphDatabaseService
            ProcedureTestUtil.registerProcedure(databaseService, SpatialProcedures::class.java)
            val spatialPlugin = SpatialPlugin()
            spatialPlugin.addSimplePointLayer(databaseService, layerName, "latitude", "longitude")
        } else {
            throw UnsupportedOperationException("Expected an embedded Neo4j instance, but was " + driver.javaClass.name)
        }
    }
}

and additionally

class ProcedureTestUtil {

    companion object {
        @JvmStatic
        var registeredProcedures: MutableList<Class<*>> = ArrayList()

        @JvmStatic
        @Throws(KernelException::class)
        fun registerProcedure(db: GraphDatabaseService, procedure: Class<*>) {
            val proceduresService = (db as GraphDatabaseAPI).dependencyResolver.resolveDependency(Procedures::class.java)
            proceduresService.register(procedure)
            registeredProcedures.add(procedure)
        }
    }
}

you should get your tests running if LocationServiceTest extends AbstractIntegrationTestsWithProcedures.

Upvotes: 0

Luanne
Luanne

Reputation: 19373

Spatial functionality is not provided in SDN 4 yet. If you integrate the neo4j-spatial lib then the only option you have at the moment is to use it directly- there will be no repository support etc. However, spatial integration work is currently in progress, so some basic functionality should be introduced in the next release.

Upvotes: 1

Related Questions