Unit test failure: test_process_predicate() in test_rdf_utils.py
**Describe the bug**

Input:
```python
 Query[0] == (
            "http://purl.org/oban/association_has_object",
            "biolink:object",
            "rdf:object",
            "OBAN:association_has_object",
            "association_has_object",
        )
```
 running test_process_predicate()

```python
    x = process_predicate(pm, query[0])
    assert x[0] == query[1]  # failure
```

tests/unit/test_rdf_utils.py:27 (test_process_predicate[query0])
None != biolink:object

Expected :biolink:object
Actual   :None
```


diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml
index c2c7140..b5e0ed1 100644
--- a/.github/workflows/run_tests.yml
+++ b/.github/workflows/run_tests.yml
@@ -39,8 +39,8 @@ jobs:
 
       - name: Setup Neo4j Docker
         run: |
-          docker run --detach --name kgx-neo4j-unit-test -p 8484:7474 -p 8888:7687 --env NEO4J_AUTH=neo4j/test neo4j:3.5.19
-          docker run --detach --name kgx-neo4j-integration-test -p 7474:7474 -p 7687:7687 --env NEO4J_AUTH=neo4j/test neo4j:3.5.19
+          docker run --detach --name kgx-neo4j-unit-test -p 8484:7474 -p 8888:7687 --env NEO4J_AUTH=neo4j/test neo4j:4.3.0
+          docker run --detach --name kgx-neo4j-integration-test -p 7474:7474 -p 7687:7687 --env NEO4J_AUTH=neo4j/test neo4j:4.3.0
           docker ps -a
 
       - name: Wait
diff --git a/README.md b/README.md
index bbbf2db..65e06cb 100644
--- a/README.md
+++ b/README.md
@@ -108,9 +108,7 @@ python setup.py install
 
 ### Setting up a testing environment for Neo4j
 
-The current release of KGX supports graph source and sink transactions with the 3.5 release of Neo4j. 
-
-**_Note:**_ Support for release 4.3 of Neo4j is pending in the next KGX release (see the [neo4j-client-upgrade](https://github.com/biolink/kgx/tree/neo4j-client-upgrade]) for the beta release of this code).
+This release of KGX supports graph source and sink transactions with the 4.3 release of Neo4j.
 
 KGX has a suite of tests that rely on Docker containers to run Neo4j specific tests.
 
@@ -120,17 +118,17 @@ on your local machine.
 Once Docker is up and running, run the following commands:
 
 ```bash
-docker run -d --name kgx-neo4j-integration-test \
+docker run -d --rm --name kgx-neo4j-integration-test \
             -p 7474:7474 -p 7687:7687 \
             --env NEO4J_AUTH=neo4j/test  \
-            neo4j:3.5.25
+            neo4j:4.3
 ```
 
 ```bash
-docker run -d --name kgx-neo4j-unit-test  \
+docker run -d --rm --name kgx-neo4j-unit-test  \
             -p 8484:7474 -p 8888:7687 \
             --env NEO4J_AUTH=neo4j/test \
-            neo4j:3.5.25
+            neo4j:4.3
 ```
 
 
diff --git a/examples/README.md b/examples/README.md
index 3a97499..2463d0e 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -7,5 +7,5 @@ This folder also contains sample YAML files that can
 be used as input to KGX to drive certain operations.
 
 For additional design patterns of KGX to consider, see also the [unit tests folder](../tests).
- 
-Note: the currently supported release of Neo4j is 3.5.
+
+Note: the currently supported release of Neo4j is 4.3 (or better).
diff --git a/kgx/cli/__init__.py b/kgx/cli/__init__.py
index dcf2111..7c02c84 100644
--- a/kgx/cli/__init__.py
+++ b/kgx/cli/__init__.py
@@ -555,7 +555,6 @@ def transform_wrapper(
         exit(1)
 
 
-
 @cli.command(name="merge")
 @click.option("--merge-config", required=True, type=str)
 @click.option(
diff --git a/kgx/config.yml b/kgx/config.yml
index 37e2279..e4697a2 100644
--- a/kgx/config.yml
+++ b/kgx/config.yml
@@ -17,9 +17,6 @@ logging:
   level: INFO
   format: '[%(name)s][%(filename)s][%(funcName)20s] %(levelname)s: %(message)s'
 
-# KGX setting for the Biolink Model is now ignored; defer to the Biolink Model Toolkit default.
-# biolink-model: https://raw.githubusercontent.com/biolink/biolink-model/2.2.5/biolink-model.yaml
-
 jsonld-context:
   biolink: https://raw.githubusercontent.com/biolink/biolink-model/2.2.5/context.jsonld
   monarch_context: https://raw.githubusercontent.com/prefixcommons/biocontext/master/registry/monarch_context.jsonld
diff --git a/kgx/sink/neo_sink.py b/kgx/sink/neo_sink.py
index 0eb38b0..6d95c02 100644
--- a/kgx/sink/neo_sink.py
+++ b/kgx/sink/neo_sink.py
@@ -1,7 +1,6 @@
 from typing import List, Union, Any
 
-from neo4jrestclient.client import GraphDatabase
-from neo4jrestclient.query import CypherException
+from neo4j import GraphDatabase, Neo4jDriver, Session
 
 from kgx.config import get_logger
 from kgx.sink.sink import Sink
@@ -42,9 +41,10 @@ class NeoSink(Sink):
         super().__init__()
         if "cache_size" in kwargs:
             self.CACHE_SIZE = kwargs["cache_size"]
-        self.http_driver: GraphDatabase = GraphDatabase(
-            uri, username=username, password=password
+        self.http_driver:Neo4jDriver = GraphDatabase.driver(
+            uri, auth=(username, password)
         )
+        self.session:Session = self.http_driver.session()
 
     def _flush_node_cache(self):
         self._write_node_cache()
@@ -87,6 +87,7 @@ class NeoSink(Sink):
                 self.CATEGORY_DELIMITER, self.CYPHER_CATEGORY_DELIMITER
             )
             query = self.generate_unwind_node_query(cypher_category)
+
             log.debug(query)
             nodes = self.node_cache[category]
             for x in range(0, len(nodes), batch_size):
@@ -94,9 +95,9 @@ class NeoSink(Sink):
                 log.debug(f"Batch {x} - {y}")
                 batch = nodes[x:y]
                 try:
-                    self.http_driver.query(query, params={"nodes": batch})
-                except CypherException as ce:
-                    log.error(ce)
+                    self.session.run(query, parameters={"nodes": batch})
+                except Exception as e:
+                    log.error(e)
 
     def _flush_edge_cache(self):
         self._flush_node_cache()
@@ -140,11 +141,11 @@ class NeoSink(Sink):
                 batch = edges[x:y]
                 log.debug(f"Batch {x} - {y}")
                 try:
-                    self.http_driver.query(
-                        query, params={"relationship": predicate, "edges": batch}
+                    self.session.run(
+                        query, parameters={"relationship": predicate, "edges": batch}
                     )
-                except CypherException as ce:
-                    log.error(ce)
+                except Exception as e:
+                    log.error(e)
 
     def finalize(self) -> None:
         """
@@ -248,10 +249,10 @@ class NeoSink(Sink):
             else:
                 query = NeoSink.create_constraint_query(category)
                 try:
-                    self.http_driver.query(query)
+                    self.session.run(query)
                     self._seen_categories.add(category)
-                except CypherException as ce:
-                    log.error(ce)
+                except Exception as e:
+                    log.error(e)
 
     @staticmethod
     def create_constraint_query(category: str) -> str:
diff --git a/kgx/source/neo_source.py b/kgx/source/neo_source.py
index 0f8cff2..c9e304b 100644
--- a/kgx/source/neo_source.py
+++ b/kgx/source/neo_source.py
@@ -1,8 +1,8 @@
 import itertools
 from typing import Any, Dict, List, Optional, Iterator, Tuple, Generator
 
-from neo4jrestclient.client import Node, Relationship, GraphDatabase
-from neo4jrestclient.query import CypherException
+from neo4j import GraphDatabase, Neo4jDriver
+from neo4j.graph import Node, Relationship
 
 from kgx.config import get_logger
 from kgx.source.source import Source
@@ -26,11 +26,18 @@ class NeoSource(Source):
 
     def __init__(self):
         super().__init__()
-        self.http_driver = None
+        self.http_driver: Optional[Neo4jDriver] = None
+        self.session = None
         self.node_count = 0
         self.edge_count = 0
         self.seen_nodes = set()
 
+    def _connect_db(self, uri: str, username: str, password: str):
+        self.http_driver = GraphDatabase.driver(
+            uri, auth=(username, password)
+        )
+        self.session = self.http_driver.session()
+
     def parse(
         self,
         uri: str,
@@ -77,9 +84,7 @@ class NeoSource(Source):
             A generator for records
 
         """
-        self.http_driver: GraphDatabase = GraphDatabase(
-            uri, username=username, password=password
-        )
+        self._connect_db(uri, username, password)
 
         self.set_provenance_map(kwargs)
 
@@ -140,11 +145,11 @@ class NeoSource(Source):
         query_result: Any
         counts: int = 0
         try:
-            query_result = self.http_driver.query(query)
+            query_result = self.session.run(query)
             for result in query_result:
                 counts = result[0]
-        except CypherException as ce:
-            log.error(ce)
+        except Exception as e:
+            log.error(e)
         return counts
 
     def get_nodes(self, skip: int = 0, limit: int = 0, **kwargs: Any) -> List:
@@ -189,11 +194,19 @@ class NeoSource(Source):
         log.debug(query)
         nodes = []
         try:
-            results = self.http_driver.query(query, returns=Node, data_contents=True)
+            results = self.session.run(query)
             if results:
-                nodes = [node[0] for node in results.rows]
-        except CypherException as ce:
-            log.error(ce)
+                nodes = [
+                    {
+                        "id": node[0].get('id', f"{node[0].id}"),
+                        "name": node[0].get('name', ''),
+                        "category": node[0].get('category', ['biolink:NamedThing'])
+                    }
+                    for node in results.values()
+                ]
+
+        except Exception as e:
+            log.error(e)
         return nodes
 
     def get_edges(
@@ -251,13 +264,43 @@ class NeoSource(Source):
         log.debug(query)
         edges = []
         try:
-            results = self.http_driver.query(
-                query, returns=(Node, Relationship, Node), data_contents=True
+            results = self.session.run(
+                query
             )
             if results:
-                edges = [x for x in results.rows]
-        except CypherException as ce:
-            log.error(ce)
+                edges = list()
+                for entry in results.values():
+                    edge = list()
+                    # subject
+                    edge.append(
+                        {
+                            "id": entry[0].get('id', f"{entry[0].id}"),
+                            "name": entry[0].get('name', ''),
+                            "category": entry[0].get('category', ['biolink:NamedThing'])
+                        }
+                    )
+
+                    # edge
+                    edge.append(
+                        {
+                             "subject":  entry[1].get('subject', f"{entry[0].id}"),
+                             "predicate": entry[1].get('predicate', "biolink:related_to"),
+                             "relation": entry[1].get('relation', "biolink:related_to"),
+                             "object": entry[1].get('object', f"{entry[2].id}")
+                        }
+                    )
+
+                    # object
+                    edge.append(
+                        {
+                            "id": entry[2].get('id', f"{entry[2].id}"),
+                            "name": entry[2].get('name', ''),
+                            "category": entry[2].get('category', ['biolink:NamedThing'])
+                        }
+                    )
+                    edges.append(edge)
+        except Exception as e:
+            log.error(e)
 
         return edges
 
diff --git a/requirements.txt b/requirements.txt
index 2d1fe1e..99b3907 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,17 +1,15 @@
-bmt~=0.7.6
 prefixcommons>=0.1.4
 pip>=9.0.1
 docutils==0.16.0
-networkx>=2.5
+networkx
 SPARQLWrapper>=1.8.2
 pandas>=1.0.3
 pytest>=0.0
 mypy>=0.0
 rdflib~=5.0.0
 Click~=7.0
-neo4jrestclient>=0.0
+neo4j==4.3
 pyyaml>=0.0
-linkml>=1.1.13
 prologterms>=0.0.5
 shexjsg>=0.6.5
 terminaltables>=3.1.0
@@ -32,4 +30,7 @@ recommonmark>=0.7.1
 tox
 flake8
 black
+bmt>=0.8.1
 tox-docker
+linkml
+linkml-runtime
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
index 43e4e18..52bd856 100644
--- a/tests/integration/__init__.py
+++ b/tests/integration/__init__.py
@@ -1,12 +1,11 @@
 import docker
 import pytest
-from neo4jrestclient.client import GraphDatabase
-from neo4jrestclient.query import CypherException
+from neo4j import GraphDatabase
 
 from kgx.graph.nx_graph import NxGraph
 
 CONTAINER_NAME = "kgx-neo4j-integration-test"
-DEFAULT_NEO4J_URL = "http://localhost:7474"
+DEFAULT_NEO4J_URL = "neo4j://localhost:7687"
 DEFAULT_NEO4J_USERNAME = "neo4j"
 DEFAULT_NEO4J_PASSWORD = "test"
 
@@ -28,16 +27,15 @@ def check_container():
 
 @pytest.fixture(scope="function")
 def clean_slate():
-    http_driver = GraphDatabase(
-        DEFAULT_NEO4J_URL,
-        username=DEFAULT_NEO4J_USERNAME,
-        password=DEFAULT_NEO4J_PASSWORD,
+    http_driver = GraphDatabase.driver(
+        DEFAULT_NEO4J_URL, auth=(DEFAULT_NEO4J_USERNAME, DEFAULT_NEO4J_PASSWORD)
     )
+
     q = "MATCH (n) DETACH DELETE (n)"
     try:
-        http_driver.query(q)
-    except CypherException as ce:
-        print(ce)
+        http_driver.session().run(q)
+    except Exception as e:
+        print(e)
 
 
 def get_graph(source):
diff --git a/tests/integration/test_neo_loader.py b/tests/integration/test_neo_loader.py
index 9232593..973a48a 100644
--- a/tests/integration/test_neo_loader.py
+++ b/tests/integration/test_neo_loader.py
@@ -3,6 +3,9 @@ import pytest
 
 from kgx.transformer import Transformer
 from tests import RESOURCE_DIR, TARGET_DIR
+from tests.unit import clean_database
+from kgx.config import get_logger
+
 from tests.integration import (
     check_container,
     CONTAINER_NAME,
@@ -11,14 +14,17 @@ from tests.integration import (
     DEFAULT_NEO4J_PASSWORD,
 )
 
+logger = get_logger()
+
 
 @pytest.mark.skipif(
     not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
 )
-def test_csv_to_neo_load():
+def test_csv_to_neo4j_load_to_graph_transform(clean_database):
     """
-    Test to load a CSV to Neo4j.
+    Test to load a csv KGX file into Neo4j.
     """
+    logger.debug("test_csv_to_neo4j_load...")
     input_args1 = {
         "filename": [
             os.path.join(RESOURCE_DIR, "cm_nodes.csv"),
@@ -37,14 +43,10 @@ def test_csv_to_neo_load():
     }
     t1.save(output_args)
 
-
-@pytest.mark.skipif(
-    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
-)
-def test_neo_to_graph_transform():
     """
-    Test to read from Neo4j and write to CSV.
+    Continue sequentially to test read from Neo4j to write out back to CSV.
     """
+    logger.debug("test_neo4j_to_graph_transform")
     input_args = {
         "uri": DEFAULT_NEO4J_URL,
         "username": DEFAULT_NEO4J_USERNAME,
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
index d4a2d11..969d509 100644
--- a/tests/unit/__init__.py
+++ b/tests/unit/__init__.py
@@ -1,14 +1,14 @@
 import docker
 import pytest
-from neo4jrestclient.client import GraphDatabase
-from neo4jrestclient.query import CypherException
+from neo4j import GraphDatabase
 
 from kgx.graph.nx_graph import NxGraph
 
 CONTAINER_NAME = "kgx-neo4j-unit-test"
-DEFAULT_NEO4J_URL = "http://localhost:8484"
+DEFAULT_NEO4J_URL = "neo4j://localhost:7687"
 DEFAULT_NEO4J_USERNAME = "neo4j"
 DEFAULT_NEO4J_PASSWORD = "test"
+DEFAULT_NEO4J_DATABASE = "neo4j"
 
 
 def check_container():
@@ -31,23 +31,31 @@ def check_container():
 
 
 @pytest.fixture(scope="function")
-def clean_slate():
+def clean_database():
     """
     Delete all nodes and edges in Neo4j test container.
     """
-    http_driver = GraphDatabase(
-        DEFAULT_NEO4J_URL,
-        username=DEFAULT_NEO4J_USERNAME,
-        password=DEFAULT_NEO4J_PASSWORD,
-    )
-    q = "MATCH (n) DETACH DELETE (n)"
-    try:
-        http_driver.query(q)
-    except CypherException as ce:
-        print(ce)
+    with GraphDatabase.driver(
+            DEFAULT_NEO4J_URL,
+            auth=(DEFAULT_NEO4J_USERNAME, DEFAULT_NEO4J_PASSWORD)
+    ) as http_driver:
+        # The following Cipher command would be better to clean the database completely
+        # before reuse, but alas, it is not supported in the Neo4j Community version?
+        #
+        # q = f"CREATE OR REPLACE DATABASE {DEFAULT_NEO4J_DATABASE}"
+        #
+        q = "MATCH (n) DETACH DELETE (n)"
+        try:
+            session = http_driver.session()
+            session.run(q)
+        except Exception as e:
+            print(e)
 
 
-def process_stream(g):
+# TODO: this is a bit of misnomer: yes, it processes a stream
+#       but then loads it into in memory data structures.
+#       This could be problematic for huge graphs?
+def load_graph_dictionary(g):
     """
     Process a given stream into a nodes and edges dictionary.
     """
diff --git a/tests/unit/test_cli_utils.py b/tests/unit/test_cli_utils.py
index f6d0293..8073077 100644
--- a/tests/unit/test_cli_utils.py
+++ b/tests/unit/test_cli_utils.py
@@ -7,14 +7,15 @@ import pytest
 from click.testing import CliRunner
 
 from kgx.cli.cli_utils import validate, neo4j_upload, neo4j_download, transform, merge
-from kgx.cli import cli, get_input_file_types, graph_summary, get_report_format_types, validate_wrapper
+from kgx.cli import cli, get_input_file_types, graph_summary, get_report_format_types
 from tests import RESOURCE_DIR, TARGET_DIR
 from tests.unit import (
+    clean_database,
     check_container,
     CONTAINER_NAME,
     DEFAULT_NEO4J_URL,
     DEFAULT_NEO4J_USERNAME,
-    DEFAULT_NEO4J_PASSWORD,
+    DEFAULT_NEO4J_PASSWORD
 )
 
 
@@ -38,6 +39,117 @@ def test_get_report_format_types():
     assert "json" in format_types
 
 
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_graph_summary_wrapper():
+    output = os.path.join(TARGET_DIR, "graph_stats3.yaml")
+
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "graph-summary",
+            "-i", "tsv",
+            "-o", output,
+            os.path.join(RESOURCE_DIR, "graph_nodes.tsv")
+        ]
+    )
+    assert result.exit_code == 0
+
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_graph_summary_wrapper_error():
+    inputs = [
+        os.path.join(RESOURCE_DIR, "graph_nodes.tsv"),
+        os.path.join(RESOURCE_DIR, "graph_edges.tsv"),
+    ]
+    output = os.path.join(TARGET_DIR, "graph_stats3.yaml")
+
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "graph-summary",
+            "-i", "tsv",
+            "-o", output,
+            inputs
+        ]
+    )
+    assert result.exit_code == 1
+
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_transform_wrapper():
+    """
+        Transform graph from TSV to JSON.
+        """
+    inputs = [
+        os.path.join(RESOURCE_DIR, "graph_nodes.tsv"),
+        os.path.join(RESOURCE_DIR, "graph_edges.tsv"),
+    ]
+    output = os.path.join(TARGET_DIR, "grapht.json")
+
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "transform",
+            "-i", "tsv",
+            "-o", output,
+            "-f", "json",
+            inputs
+        ]
+    )
+
+    assert result.exit_code == 1
+
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_merge_wrapper():
+
+    """
+    Transform from test merge YAML.
+    """
+    merge_config = os.path.join(RESOURCE_DIR, "test-merge.yaml")
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "merge",
+            "--merge-config", merge_config
+        ]
+    )
+
+    assert result.exit_code == 0
+    assert os.path.join(TARGET_DIR, "merged-graph_nodes.tsv")
+    assert os.path.join(TARGET_DIR, "merged-graph_edges.tsv")
+    assert os.path.join(TARGET_DIR, "merged-graph.json")
+
+
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_merge_wrapper_error():
+
+    """
+    Transform from test merge YAML.
+    """
+    merge_config = os.path.join(RESOURCE_DIR, "test-merge.yaml")
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "merge"
+        ]
+    )
+
+    assert result.exit_code == 2
+
+
 def test_kgx_graph_summary():
     """
     Test graph summary, where the output report type is kgx-map.
@@ -237,7 +349,7 @@ def test_validate_streaming():
 @pytest.mark.skipif(
     not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
 )
-def test_neo4j_upload(clean_slate):
+def test_neo4j_upload(clean_database):
     """
     Test upload to Neo4j.
     """
@@ -259,11 +371,99 @@ def test_neo4j_upload(clean_slate):
     assert t.store.graph.number_of_edges() == 531
 
 
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_neo4j_download_wrapper(clean_database):
+    output = os.path.join(TARGET_DIR, "neo_download2")
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "neo4j-download",
+            "-l", DEFAULT_NEO4J_URL,
+            "-o", output,
+            "-f", "tsv",
+            "-u", DEFAULT_NEO4J_USERNAME,
+            "-p", DEFAULT_NEO4J_PASSWORD,
+        ]
+    )
+
+    assert os.path.exists(f"{output}_nodes.tsv")
+    assert os.path.exists(f"{output}_edges.tsv")
+
+    assert result.exit_code == 0
+
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_download_exception_triggered_error_exit_code():
+    """
+    Test graph download error exit code.
+    """
+
+    output = os.path.join(TARGET_DIR, "neo_download")
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "neo4j-download",
+            "-l", DEFAULT_NEO4J_URL,
+            "-o", output,
+            "-f", "tsvr",
+            "-u", "not a user name",
+            "-p", DEFAULT_NEO4J_PASSWORD,
+        ]
+    )
+    assert result.exit_code == 1
+
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_neo4j_upload_wrapper(clean_database):
+    inputs = [
+        os.path.join(RESOURCE_DIR, "graph_nodes.tsv"),
+        os.path.join(RESOURCE_DIR, "graph_edges.tsv"),
+    ]
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "neo4j-upload",
+            "--input-format", "tsv",
+            "--uri", DEFAULT_NEO4J_URL,
+            "--username", DEFAULT_NEO4J_USERNAME,
+            "--password", DEFAULT_NEO4J_PASSWORD,
+            os.path.join(RESOURCE_DIR, "graph_nodes.tsv")
+        ]
+    )
+
+    assert result.exit_code == 0
+
+
+@pytest.mark.skipif(
+    not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
+)
+def test_neo4j_upload_wrapper_error(clean_database):
+    runner = CliRunner()
+    result = runner.invoke(
+        cli,
+        [
+            "neo4j-upload",
+            "-i", "tsv",
+            "inputs", "not_a_path"
+            "-u", "not a user",
+            "-p", DEFAULT_NEO4J_PASSWORD,
+        ]
+    )
+
+    assert result.exit_code == 2
+
 @pytest.mark.skip()
 @pytest.mark.skipif(
     not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
 )
-def test_neo4j_download(clean_slate):
+def test_neo4j_download(clean_database):
     """
     Test download from Neo4j.
     """
diff --git a/tests/unit/test_sink/test_neo_sink.py b/tests/unit/test_sink/test_neo_sink.py
index 1aa0f57..132022e 100644
--- a/tests/unit/test_sink/test_neo_sink.py
+++ b/tests/unit/test_sink/test_neo_sink.py
@@ -1,13 +1,12 @@
 from time import sleep
 
 import pytest
-from neo4jrestclient.client import GraphDatabase, Node, Relationship
-from neo4jrestclient.query import CypherException
+from neo4j import GraphDatabase
 
 from kgx.sink import NeoSink
 from tests import print_graph
 from tests.unit import (
-    clean_slate,
+    clean_database,
     DEFAULT_NEO4J_URL,
     DEFAULT_NEO4J_USERNAME,
     DEFAULT_NEO4J_PASSWORD,
@@ -42,7 +41,7 @@ def test_create_constraint_query(category):
 @pytest.mark.skipif(
     not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
 )
-def test_write_neo1(clean_slate):
+def test_write_neo1(clean_database):
     """
     Write a graph to a Neo4j instance using NeoSink.
     """
@@ -58,25 +57,23 @@ def test_write_neo1(clean_slate):
         s.write_edge(data)
     s.finalize()
 
-    d = GraphDatabase(
-        DEFAULT_NEO4J_URL,
-        username=DEFAULT_NEO4J_USERNAME,
-        password=DEFAULT_NEO4J_PASSWORD,
+    d = GraphDatabase.driver(
+        DEFAULT_NEO4J_URL, auth=(DEFAULT_NEO4J_USERNAME, DEFAULT_NEO4J_PASSWORD)
     )
-
+    session = d.session()
     try:
-        results = d.query("MATCH (n) RETURN COUNT(*)")
+        results = session.run("MATCH (n) RETURN COUNT(*)")
         number_of_nodes = results[0][0]
         assert number_of_nodes == 3
-    except CypherException as ce:
-        print(ce)
+    except Exception as e:
+        print(e)
 
     try:
-        results = d.query("MATCH (s)-->(o) RETURN COUNT(*)")
+        results = session.run("MATCH (s)-->(o) RETURN COUNT(*)")
         number_of_edges = results[0][0]
         assert number_of_edges == 1
-    except CypherException as ce:
-        print(ce)
+    except Exception as e:
+        print(e)
 
 
 @pytest.mark.skipif(
@@ -86,7 +83,7 @@ def test_write_neo1(clean_slate):
     "query",
     [(get_graph("kgx-unit-test")[0], 3, 1), (get_graph("kgx-unit-test")[1], 6, 6)],
 )
-def test_write_neo2(clean_slate, query):
+def test_write_neo2(clean_database, query):
     """
     Test writing a graph to a Neo4j instance.
     """
@@ -102,20 +99,20 @@ def test_write_neo2(clean_slate, query):
         sink.write_edge(data)
     sink.finalize()
 
-    nr = sink.http_driver.query("MATCH (n) RETURN count(n)")
+    nr = sink.session.run("MATCH (n) RETURN count(n)")
     [node_counts] = [x for x in nr][0]
     assert node_counts >= query[1]
 
-    er = sink.http_driver.query("MATCH ()-[p]->() RETURN count(p)")
+    er = sink.session.run("MATCH ()-[p]->() RETURN count(p)")
     [edge_counts] = [x for x in er][0]
     assert edge_counts >= query[2]
-    sink.http_driver.flush()
+
 
 
 @pytest.mark.skipif(
     not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
 )
-def test_write_neo3(clean_slate):
+def test_write_neo3(clean_database):
     """
     Test writing a graph and then writing a slightly
     modified version of the graph to the same Neo4j instance.
@@ -151,16 +148,16 @@ def test_write_neo3(clean_slate):
         sink.write_edge(data)
     sink.finalize()
 
-    nr = sink.http_driver.query("MATCH (n) RETURN n")
+    nr = sink.session.run("MATCH (n) RETURN n")
     nodes = []
     for node in nr:
         nodes.append(node)
 
     edges = []
-    er = sink.http_driver.query(
+    er = sink.session.run(
         "MATCH ()-[p]-() RETURN p",
         data_contents=True,
-        returns=(Node, Relationship, Node),
+#        returns=(Node, Relationship, Node),
     )
     for edge in er:
         edges.append(edge)
diff --git a/tests/unit/test_source/test_neo_source.py b/tests/unit/test_source/test_neo_source.py
index baf5fad..fe8bed0 100644
--- a/tests/unit/test_source/test_neo_source.py
+++ b/tests/unit/test_source/test_neo_source.py
@@ -1,15 +1,15 @@
 import pytest
-from neo4jrestclient.client import GraphDatabase
+from neo4j import GraphDatabase
 
 from kgx.source import NeoSource
 from tests.unit import (
-    clean_slate,
+    clean_database,
     DEFAULT_NEO4J_URL,
     DEFAULT_NEO4J_USERNAME,
     DEFAULT_NEO4J_PASSWORD,
-    process_stream,
+    load_graph_dictionary,
     check_container,
-    CONTAINER_NAME,
+    CONTAINER_NAME
 )
 
 
@@ -35,24 +35,28 @@ queries = [
 @pytest.mark.skipif(
     not check_container(), reason=f"Container {CONTAINER_NAME} is not running"
 )
-def test_read_neo(clean_slate):
+def test_read_neo(clean_database):
     """
     Read a graph from a Neo4j instance.
     """
-    driver = GraphDatabase(
-        DEFAULT_NEO4J_URL,
-        username=DEFAULT_NEO4J_USERNAME,
-        password=DEFAULT_NEO4J_PASSWORD,
-    )
-    for q in queries:
-        driver.query(q)
+    with GraphDatabase.driver(
+            DEFAULT_NEO4J_URL,
+            auth=(DEFAULT_NEO4J_USERNAME, DEFAULT_NEO4J_PASSWORD)
+    ) as http_driver:
+        session = http_driver.session()
+        for q in queries:
+            session.run(q)
+
+    # Establish a fresh "Source" connection to the database
     s = NeoSource()
     g = s.parse(
         uri=DEFAULT_NEO4J_URL,
         username=DEFAULT_NEO4J_USERNAME,
         password=DEFAULT_NEO4J_PASSWORD,
     )
-    nodes, edges = process_stream(g)
+
+    nodes, edges = load_graph_dictionary(g)
+
     assert len(nodes.keys()) == 3
     assert len(edges.keys()) == 2
 
diff --git a/tests/unit/test_source/test_owl_source.py b/tests/unit/test_source/test_owl_source.py
index d8c4031..3f328bf 100644
--- a/tests/unit/test_source/test_owl_source.py
+++ b/tests/unit/test_source/test_owl_source.py
@@ -1,5 +1,4 @@
 import os
-import pprint
 
 from kgx.source import OwlSource
 from tests import RESOURCE_DIR
@@ -9,11 +8,12 @@ def test_read_owl1():
     """
     Read an OWL ontology using OwlSource.
     """
+
     s = OwlSource()
     g = s.parse(
         os.path.join(RESOURCE_DIR, "goslim_generic.owl"),
         provided_by="GO slim generic",
-        knowledge_source="GO slim generic",
+        knowledge_source="GO slim generic"
     )
     nodes = {}
     edges = {}
@@ -133,7 +133,6 @@ def test_read_owl3():
                 nodes[rec[0]] = rec[-1]
 
     n1 = nodes["GO:0008150"]
-    pprint.pprint(n1)
     assert n1["name"] == "biological_process"
     assert "subsets" in n1 and "GOP:goslim_generic" in n1["subsets"]
     assert "synonym" in n1 and "biological process" in n1["synonym"]
diff --git a/tests/unit/test_source/test_rdf_source.py b/tests/unit/test_source/test_rdf_source.py
index e053ce5..266210c 100644
--- a/tests/unit/test_source/test_rdf_source.py
+++ b/tests/unit/test_source/test_rdf_source.py
@@ -5,7 +5,7 @@ import pytest
 
 from kgx.source import RdfSource
 from tests import RESOURCE_DIR
-from tests.unit import process_stream
+from tests.unit import load_graph_dictionary
 
 
 def test_read_nt1():
@@ -14,7 +14,7 @@ def test_read_nt1():
     """
     s = RdfSource()
     g = s.parse(os.path.join(RESOURCE_DIR, "rdf", "test1.nt"))
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
     assert len(nodes) == 2
     assert len(edges) == 1
 
@@ -56,7 +56,7 @@ def test_read_nt2():
         provided_by="Test Dataset",
         knowledge_source="Test Dataset",
     )
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
     assert len(nodes) == 2
     assert len(edges) == 1
 
@@ -102,7 +102,7 @@ def test_read_nt3():
     g = source.parse(
         filename=os.path.join(RESOURCE_DIR, "rdf", "test2.nt"), format="nt"
     )
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
     assert len(nodes) == 4
     assert len(edges) == 3
 
@@ -153,7 +153,7 @@ def test_read_nt4():
     g = source.parse(
         filename=os.path.join(RESOURCE_DIR, "rdf", "test3.nt"), format="nt"
     )
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
     assert len(nodes.keys()) == 7
     assert len(edges.keys()) == 6
 
@@ -220,7 +220,7 @@ def test_read_nt5():
     source.set_prefix_map(prefix_map)
     source.set_node_property_predicates(node_property_predicates)
     g = source.parse(filename=filename, format="nt")
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
 
     assert len(nodes.keys()) == 14
     assert len(edges.keys()) == 7
@@ -265,7 +265,7 @@ def test_read_nt6():
     source.set_predicate_mapping(predicate_mapping)
 
     g = source.parse(filename=filename, format="nt")
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
 
     assert len(nodes.keys()) == 14
     assert len(edges.keys()) == 7
diff --git a/tests/unit/test_source/test_sssom_source.py b/tests/unit/test_source/test_sssom_source.py
index dc135b4..9ac9e77 100644
--- a/tests/unit/test_source/test_sssom_source.py
+++ b/tests/unit/test_source/test_sssom_source.py
@@ -2,7 +2,7 @@ import os
 
 from kgx.source.sssom_source import SssomSource
 from tests import RESOURCE_DIR
-from tests.unit import process_stream
+from tests.unit import load_graph_dictionary
 
 
 def test_load1():
@@ -13,7 +13,7 @@ def test_load1():
     g = source.parse(
         filename=os.path.join(RESOURCE_DIR, "sssom_example1.tsv"), format="sssom"
     )
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
     assert len(nodes.keys()) == 18
     assert len(edges.keys()) == 9
 
@@ -35,7 +35,7 @@ def test_load2():
     g = source.parse(
         filename=os.path.join(RESOURCE_DIR, "sssom_example2.tsv"), format="sssom"
     )
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
     assert len(nodes.keys()) == 18
     assert len(edges.keys()) == 9
 
@@ -63,7 +63,7 @@ def test_load3():
     g = source.parse(
         filename=os.path.join(RESOURCE_DIR, "sssom_example3.tsv"), format="sssom"
     )
-    nodes, edges = process_stream(g)
+    nodes, edges = load_graph_dictionary(g)
     assert len(nodes) == 20
     assert len(edges) == 10
 
diff --git a/tests/unit/test_validator.py b/tests/unit/test_validator.py
index 7c9883e..8895ca7 100644
--- a/tests/unit/test_validator.py
+++ b/tests/unit/test_validator.py
@@ -21,7 +21,7 @@ def test_get_required_node_properties(property):
     assert property in properties
 
 
-@pytest.mark.parametrize("property", ["subject", "object", "predicate", "relation"])
+@pytest.mark.parametrize("property", ["subject", "object", "predicate"])
 def test_get_required_edge_properties(property):
     """
     Test get_required_edge_properties in Validator.
['tests/integration/__init__.py', 'README.md', 'tests/unit/test_sink/test_neo_sink.py', '.github/workflows/run_tests.yml', 'tests/unit/test_source/test_neo_source.py', 'tests/unit/__init__.py', 'tests/unit/test_validator.py', 'kgx/source/neo_source.py', 'examples/README.md', 'tests/unit/test_cli_utils.py', 'tests/unit/test_source/test_owl_source.py', 'tests/unit/test_source/test_rdf_source.py', 'kgx/config.yml', 'kgx/sink/neo_sink.py', 'tests/unit/test_source/test_sssom_source.py', 'tests/integration/test_neo_loader.py', 'requirements.txt', 'kgx/cli/__init__.py']