Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Neo4j and Django testing

I'm using Django and Neo4j together with neomodel as the OGM (ORM for graphs). It's working nicely but when it comes to testing, Neomodel doesn't support the usual Django behavior with the relational databases. I mean that it doesn't create a temporal Neo4j instance that is created at the beginning of testing and destroyed once it ends.

I've been doing some research and I've found two possible solutions:

  • The first, is creating a custom discover runner where you stop the development database, then start a test database from another path, run your tests and finally, stop the test instance and start the development instance again. This solution is proposed in the thread Django testing of neo4j database. The following code has been adapted for 3.1.1 Neo4j version:

    from time import sleep
    from subprocess import call
    
    from django.test.runner import DiscoverRunner
    from py2neo import authenticate
    
    class DiscoverRunner(DiscoverRunner):
    def setup_databases(self, *args, **kwargs):
        # Stop your development instance
        call("sudo neo4j stop", shell=True)
        # Sleep to ensure the service has completely stopped
        sleep(1)
        # Start your test instance (see section below for more details)
        success = call("neo4j_test/neo4j-community-3.1.1/bin/neo4j"
                       " start", shell=True)
        # Need to sleep to wait for the test instance to completely come up
        sleep(10)
        if success != 0:
            return False
    
        # These lines have been commented because I've set the configuration
        # dbms.security.auth_enabled=false
        #try:
        #    # For neo4j 2.2.x you'll need to set a password or deactivate auth
        #    # Nigel Small's py2neo gives us an easy way to accomplish this
        #    # call("source /path/to/virtualenv/bin/activate && "
        #    #      "/path/to/virtualenv/bin/neoauth "
        #    #      "neo4j neo4j my-p4ssword")
    
        #    authenticate("localhost:7474", "neo4j", "my-password")
    
        #except OSError:
        #    pass
        # Don't import neomodel until we get here because we need to wait
        # for the new db to be spawned
        from neomodel import db
        # Delete all previous entries in the db prior to running tests
        query = "match (n)-[r]-() delete n,r"
        db.cypher_query(query)
        super(DiscoverRunner, self).__init__(*args, **kwargs)
    
    def teardown_databases(self, old_config, **kwargs):
        from neomodel import db
        # Delete all previous entries in the db after running tests
        query = "match (n)-[r]-() delete n,r"
        db.cypher_query(query)
        sleep(1)
        # Shut down test neo4j instance
        success = call("neo4j_test/neo4j-community-3.1.1/bin/neo4j"
                       " stop", shell=True)
        if success != 0:
            return False
        sleep(1)
        # start back up development instance
        call("sudo neo4j start", shell=True)
    

    It works fine until it tries to connect to the test database and make a query, because it uses an encrypt connection and looks up in '~/.neo4/known_hosts' for the connection key, but it conflicts with the one of the development database and crash with the following error:

    neo4j.v1.exceptions.ProtocolError: Server certificate does not match known certificate for 'localhost'; check details in file '~/.neo4j/known_hosts'
    

    So, is there a way to avoid this certification checking?

  • The second solution is creating the same discover runner and set up an ImpermanentDatabase available in Neo4j. The problem is that all information I've found is in Java exceot this one, Neo4j ImpermanentDatabase in python unittests, and it doesn't make me clear how to implement this in python. Does someone has some notion of how to use it in python (it doesn't matter if it's with neomodel, py2neo or even directly with the python driver)

Thanks very much in advance.

like image 984
ainsausti Avatar asked Nov 08 '22 02:11

ainsausti


1 Answers

After some researching, I've come up with a solution using the first approach.

The official python driver for Neo4j has a configurable parameter (encrypted) when connecting to the driver:

GraphDatabase.driver('bolt://' + hostname, auth=basic_auth(username, password), encrypted=True)

If the encrypted parameter is set to False then the '~/.neo4/known_hosts' will be ignored and there will be no errors. Sadly, the neomodel framework doesn't support yet the tunning of that parameter, but I've forked the repository in github and added a variable in the global settings called ENCRYPTED_CONNECTION that can be override to achieve this goal.

I'm waiting the pull request to be accepted, I'll inform either is accepted or not.

By the way, if anyone can give us some ligth of the second approach commented in the question that would be awesome.

Thanks.

like image 175
ainsausti Avatar answered Nov 14 '22 22:11

ainsausti