about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/fixtures/rdf.py7
-rw-r--r--tests/integration/conftest.py15
-rw-r--r--tests/integration/test_gemma.py66
-rw-r--r--tests/integration/test_lmdb_sample_data.py31
-rw-r--r--tests/integration/test_partial_correlations.py2
-rwxr-xr-xtests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/data.mdbbin0 -> 32768 bytes
-rwxr-xr-xtests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/lock.mdbbin0 -> 8192 bytes
-rw-r--r--tests/test_data/ttl-files/test-data.ttl273
-rw-r--r--tests/unit/computations/test_partial_correlations.py4
-rw-r--r--tests/unit/computations/test_wgcna.py10
-rw-r--r--tests/unit/conftest.py14
-rw-r--r--tests/unit/db/rdf/data.py199
-rw-r--r--tests/unit/db/rdf/test_wiki.py50
-rw-r--r--tests/unit/db/test_case_attributes.py471
-rw-r--r--tests/unit/db/test_gen_menu.py4
-rw-r--r--tests/unit/test_db_utils.py70
-rw-r--r--tests/unit/test_llm.py138
-rw-r--r--tests/unit/test_rqtl2.py123
18 files changed, 1229 insertions, 248 deletions
diff --git a/tests/fixtures/rdf.py b/tests/fixtures/rdf.py
index 98c4058..0811d3c 100644
--- a/tests/fixtures/rdf.py
+++ b/tests/fixtures/rdf.py
@@ -59,7 +59,8 @@ def rdf_setup():
 
             # Make sure this graph does not exist before running anything
             requests.delete(
-                SPARQL_CONF["sparql_crud_auth_uri"], params=params, auth=auth
+                SPARQL_CONF["sparql_crud_auth_uri"], params=params, auth=auth,
+                timeout=300
             )
 
             # Open the file in binary mode and send the request
@@ -69,9 +70,11 @@ def rdf_setup():
                     params=params,
                     auth=auth,
                     data=file,
+                    timeout=300
                 )
             yield response
             requests.delete(
-                SPARQL_CONF["sparql_crud_auth_uri"], params=params, auth=auth
+                SPARQL_CONF["sparql_crud_auth_uri"], params=params, auth=auth,
+                timeout=300
             )
             pid.terminate()
diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py
index 8e39726..bdbab09 100644
--- a/tests/integration/conftest.py
+++ b/tests/integration/conftest.py
@@ -1,4 +1,5 @@
 """Module that holds fixtures for integration tests"""
+from pathlib import Path
 import pytest
 import MySQLdb
 
@@ -6,19 +7,25 @@ from gn3.app import create_app
 from gn3.chancy import random_string
 from gn3.db_utils import parse_db_url, database_connection
 
+
 @pytest.fixture(scope="session")
 def client():
     """Create a test client fixture for tests"""
     # Do some setup
-    app = create_app()
-    app.config.update({"TESTING": True})
-    app.testing = True
+    app = create_app({
+        "TESTING": True,
+        "LMDB_DATA_PATH": str(
+            Path(__file__).parent.parent /
+            Path("test_data/lmdb-test-data")
+        ),
+        "AUTH_SERVER_URL": "http://127.0.0.1:8081",
+    })
     yield app.test_client()
     # Do some teardown/cleanup
 
 
 @pytest.fixture(scope="session")
-def db_conn(client): # pylint: disable=[redefined-outer-name]
+def db_conn(client):  # pylint: disable=[redefined-outer-name]
     """Create a db connection fixture for tests"""
     # 01) Generate random string to append to all test db artifacts for the session
     live_db_uri = client.application.config["SQL_URI"]
diff --git a/tests/integration/test_gemma.py b/tests/integration/test_gemma.py
index 53a1596..7bc1df9 100644
--- a/tests/integration/test_gemma.py
+++ b/tests/integration/test_gemma.py
@@ -63,10 +63,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_k_compute(self, mock_ipfs_cache,
-                       mock_redis,
-                       mock_path_exist, mock_json, mock_hash,
-                       mock_queue_cmd):
+    def test_k_compute(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/k-compute/<token>"""
         mock_ipfs_cache.return_value = ("/tmp/cache/"
                                         "QmQPeNsJPyVWPFDVHb"
@@ -106,9 +105,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_k_compute_loco(self, mock_ipfs_cache,
-                            mock_redis, mock_path_exist, mock_json,
-                            mock_hash, mock_queue_cmd):
+    def test_k_compute_loco(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/k-compute/loco/<chromosomes>/<token>"""
         mock_ipfs_cache.return_value = ("/tmp/cache/"
                                         "QmQPeNsJPyVWPFDVHb"
@@ -150,9 +149,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_gwa_compute(self, mock_ipfs_cache,
-                         mock_redis, mock_path_exist, mock_json,
-                         mock_hash, mock_queue_cmd):
+    def test_gwa_compute(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/gwa-compute/<k-inputfile>/<token>"""
         mock_ipfs_cache.return_value = ("/tmp/cache/"
                                         "QmQPeNsJPyVWPFDVHb"
@@ -201,9 +200,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_gwa_compute_with_covars(self, mock_ipfs_cache,
-                                     mock_redis, mock_path_exist,
-                                     mock_json, mock_hash, mock_queue_cmd):
+    def test_gwa_compute_with_covars(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/gwa-compute/covars/<k-inputfile>/<token>"""
         mock_ipfs_cache.return_value = ("/tmp/cache/"
                                         "QmQPeNsJPyVWPFDVHb"
@@ -255,9 +254,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_gwa_compute_with_loco_only(self, mock_ipfs_cache,
-                                        mock_redis, mock_path_exist,
-                                        mock_json, mock_hash, mock_queue_cmd):
+    def test_gwa_compute_with_loco_only(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/gwa-compute/<k-inputfile>/loco/maf/<maf>/<token>
 
         """
@@ -308,10 +307,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_gwa_compute_with_loco_covars(self, mock_ipfs_cache,
-                                          mock_redis, mock_path_exist,
-                                          mock_json, mock_hash,
-                                          mock_queue_cmd):
+    def test_gwa_compute_with_loco_covars(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/gwa-compute/<k-inputfile>/loco/covars/maf/<maf>/<token>
 
         """
@@ -363,10 +361,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_k_gwa_compute_without_loco_covars(self, mock_ipfs_cache,
-                                               mock_redis,
-                                               mock_path_exist, mock_json,
-                                               mock_hash, mock_queue_cmd):
+    def test_k_gwa_compute_without_loco_covars(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/k-gwa-compute/<token>
 
         """
@@ -419,10 +416,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_k_gwa_compute_with_covars_only(self, mock_ipfs_cache,
-                                            mock_redis, mock_path_exist,
-                                            mock_json, mock_hash,
-                                            mock_queue_cmd):
+    def test_k_gwa_compute_with_covars_only(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/k-gwa-compute/covars/<token>
 
         """
@@ -484,10 +480,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_k_gwa_compute_with_loco_only(self, mock_ipfs_cache,
-                                          mock_redis, mock_path_exist,
-                                          mock_json, mock_hash,
-                                          mock_queue_cmd):
+    def test_k_gwa_compute_with_loco_only(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /gemma/k-gwa-compute/loco/<chromosomes>/maf/<maf>/<token>
 
         """
@@ -550,10 +545,9 @@ class GemmaAPITest(unittest.TestCase):
     @mock.patch("gn3.api.gemma.assert_paths_exist")
     @mock.patch("gn3.api.gemma.redis.Redis")
     @mock.patch("gn3.api.gemma.cache_ipfs_file")
-    def test_k_gwa_compute_with_loco_and_covar(self, mock_ipfs_cache,
-                                               mock_redis,
-                                               mock_path_exist, mock_json,
-                                               mock_hash, mock_queue_cmd):
+    def test_k_gwa_compute_with_loco_and_covar(# pylint: disable=[too-many-positional-arguments]
+            self, mock_ipfs_cache, mock_redis, mock_path_exist, mock_json,
+            mock_hash, mock_queue_cmd):
         """Test /k-gwa-compute/covars/loco/<chromosomes>/maf/<maf>/<token>
 
         """
diff --git a/tests/integration/test_lmdb_sample_data.py b/tests/integration/test_lmdb_sample_data.py
new file mode 100644
index 0000000..30a23f4
--- /dev/null
+++ b/tests/integration/test_lmdb_sample_data.py
@@ -0,0 +1,31 @@
+"""Tests for the LMDB sample data API endpoint"""
+import pytest
+
+
+@pytest.mark.unit_test
+def test_nonexistent_data(client):
+    """Test endpoint returns 404 when data doesn't exist"""
+    response = client.get("/api/lmdb/sample-data/nonexistent/123")
+    assert response.status_code == 404
+    assert response.json["error"] == "No data found for given dataset and trait"
+
+
+@pytest.mark.unit_test
+def test_successful_retrieval(client):
+    """Test successful data retrieval using test LMDB data"""
+    # Use known test data hash: 7308efbd84b33ad3d69d14b5b1f19ccc
+    response = client.get("/api/lmdb/sample-data/BXDPublish/10007")
+    assert response.status_code == 200
+
+    data = response.json
+    assert len(data) == 31
+    # Verify some known values from the test database
+    assert data["BXD1"] == 18.700001
+    assert data["BXD11"] == 18.9
+
+
+@pytest.mark.unit_test
+def test_invalid_trait_id(client):
+    """Test endpoint handles invalid trait IDs appropriately"""
+    response = client.get("/api/lmdb/sample-data/BXDPublish/999999")
+    assert response.status_code == 404
diff --git a/tests/integration/test_partial_correlations.py b/tests/integration/test_partial_correlations.py
index fc9f64f..56af260 100644
--- a/tests/integration/test_partial_correlations.py
+++ b/tests/integration/test_partial_correlations.py
@@ -221,4 +221,4 @@ def test_part_corr_api_with_mix_of_existing_and_non_existing_control_traits(
     criteria = 10
     with pytest.warns(UserWarning):
         partial_correlations_with_target_db(
-            db_conn, primary, controls, method, criteria, target)
+            db_conn, primary, controls, method, criteria, target, "/tmp")
diff --git a/tests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/data.mdb b/tests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/data.mdb
new file mode 100755
index 0000000..5fa213b
--- /dev/null
+++ b/tests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/data.mdb
Binary files differdiff --git a/tests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/lock.mdb b/tests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/lock.mdb
new file mode 100755
index 0000000..116d824
--- /dev/null
+++ b/tests/test_data/lmdb-test-data/7308efbd84b33ad3d69d14b5b1f19ccc/lock.mdb
Binary files differdiff --git a/tests/test_data/ttl-files/test-data.ttl b/tests/test_data/ttl-files/test-data.ttl
index 3e27652..c570484 100644
--- a/tests/test_data/ttl-files/test-data.ttl
+++ b/tests/test_data/ttl-files/test-data.ttl
@@ -1054,3 +1054,276 @@ gn:wiki-7273-0 dct:created "2022-08-24 18:34:41"^^xsd:datetime  .
 gn:wiki-7273-0 foaf:mbox <XXX@XXX.com> .
 gn:wiki-7273-0 dct:identifier "7273"^^xsd:integer .
 gn:wiki-7273-0 dct:hasVersion "0"^^xsd:integer .
+
+gnc:NCBIWikiEntry rdfs:subClassOf gnc:GeneWikiEntry .
+gnc:NCBIWikiEntry rdfs:comment "Represents GeneRIF Entries obtained from NCBI" .
+gn:rif-12709-37156912-2023-05-17T20:43:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Creatine kinase B suppresses ferroptosis by phosphorylating GPX4 through a moonlighting function.'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Ckb" ;
+	gnt:hasGeneId generif:12709 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37156912 ;
+	dct:created "2023-05-17 20:43:00"^^xsd:datetime  .
+gn:rif-13176-36456775-2023-03-01T20:36:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'DCC/netrin-1 regulates cell death in oligodendrocytes after brain injury.'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Dcc" ;
+	gnt:hasGeneId generif:13176 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36456775 ;
+	dct:created "2023-03-01 20:36:00"^^xsd:datetime  .
+gn:rif-13176-37541362-2023-09-21T20:40:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Prefrontal cortex-specific Dcc deletion induces schizophrenia-related behavioral phenotypes and fail to be rescued by olanzapine treatment.'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Dcc" ;
+	gnt:hasGeneId generif:13176 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37541362 ;
+	dct:created "2023-09-21 20:40:00"^^xsd:datetime  .
+gn:rif-16956-36519761-2023-04-27T20:33:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Parkin regulates neuronal lipid homeostasis through SREBP2-lipoprotein lipase pathway-implications for Parkinson\'s disease.'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Lpl" ;
+	gnt:hasGeneId generif:16956 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36519761 ;
+	dct:created "2023-04-27 20:33:00"^^xsd:datetime  .
+gn:rif-20423-36853961-2023-03-08T20:38:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'IHH, SHH, and primary cilia mediate epithelial-stromal cross-talk during decidualization in mice.'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Shh" ;
+	gnt:hasGeneId generif:20423 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36853961 ;
+	dct:created "2023-03-08 20:38:00"^^xsd:datetime  .
+gn:rif-20423-37190906-2023-07-12T09:09:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'The SHH-GLI1 pathway is required in skin expansion and angiogenesis.'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Shh" ;
+	gnt:hasGeneId generif:20423 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37190906 ;
+	dct:created "2023-07-12 09:09:00"^^xsd:datetime  .
+gn:rif-20423-37460185-2023-07-20T20:39:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label '[Effect study of Sonic hedgehog overexpressed hair follicle stem cells in hair follicle regeneration].'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Shh" ;
+	gnt:hasGeneId generif:20423 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37460185 ;
+	dct:created "2023-07-20 20:39:00"^^xsd:datetime  .
+gn:rif-20423-37481204-2023-09-26T20:37:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Sonic Hedgehog and WNT Signaling Regulate a Positive Feedback Loop Between Intestinal Epithelial and Stromal Cells to Promote Epithelial Regeneration.'@en ;
+	gnt:belongsToSpecies gn:Mus_musculus ;
+	gnt:symbol "Shh" ;
+	gnt:hasGeneId generif:20423 ;
+	skos:notation taxon:10090 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37481204 ;
+	dct:created "2023-09-26 20:37:00"^^xsd:datetime  .
+gn:rif-24539-38114521-2023-12-29T20:33:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Developing a model to predict the early risk of hypertriglyceridemia based on inhibiting lipoprotein lipase (LPL): a translational study.'@en ;
+	gnt:belongsToSpecies gn:Rattus_norvegicus ;
+	gnt:symbol "Lpl" ;
+	gnt:hasGeneId generif:24539 ;
+	skos:notation taxon:10116 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:38114521 ;
+	dct:created "2023-12-29 20:33:00"^^xsd:datetime  .
+gn:rif-29499-36906487-2023-06-23T20:38:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Regulation of Shh/Bmp4 Signaling Pathway by DNA Methylation in Rectal Nervous System Development of Fetal Rats with Anorectal Malformation.'@en ;
+	gnt:belongsToSpecies gn:Rattus_norvegicus ;
+	gnt:symbol "Shh" ;
+	gnt:hasGeneId generif:29499 ;
+	skos:notation taxon:10116 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36906487 ;
+	dct:created "2023-06-23 20:38:00"^^xsd:datetime  .
+gn:rif-29499-37815888-2023-10-24T20:38:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Sonic hedgehog signaling promotes angiogenesis of endothelial progenitor cells to improve pressure ulcers healing by PI3K/AKT/eNOS signaling.'@en ;
+	gnt:belongsToSpecies gn:Rattus_norvegicus ;
+	gnt:symbol "Shh" ;
+	gnt:hasGeneId generif:29499 ;
+	skos:notation taxon:10116 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37815888 ;
+	dct:created "2023-10-24 20:38:00"^^xsd:datetime  .
+gn:rif-1152-37156912-2023-05-17T20:43:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Creatine kinase B suppresses ferroptosis by phosphorylating GPX4 through a moonlighting function.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "CKB" ;
+	gnt:hasGeneId generif:1152 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37156912 ;
+	dct:created "2023-05-17 20:43:00"^^xsd:datetime  .
+gn:rif-1630-36889039-2023-04-04T09:45:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Mirror movements and callosal dysgenesis in a family with a DCC mutation: Neuropsychological and neuroimaging outcomes.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "DCC" ;
+	gnt:hasGeneId generif:1630 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36889039 ;
+	dct:created "2023-04-04 09:45:00"^^xsd:datetime  .
+gn:rif-1630-36852451-2023-07-07T20:37:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'An imbalance of netrin-1 and DCC during nigral degeneration in experimental models and patients with Parkinson\'s disease.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "DCC" ;
+	gnt:hasGeneId generif:1630 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36852451 ;
+	dct:created "2023-07-07 20:37:00"^^xsd:datetime  .
+gn:rif-4023-36763533-2023-02-23T20:40:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Angiopoietin-like protein 4/8 complex-mediated plasmin generation leads to cleavage of the complex and restoration of LPL activity.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36763533 ;
+	dct:created "2023-02-23 20:40:00"^^xsd:datetime  .
+gn:rif-4023-36652113-2023-04-07T20:39:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'The breast cancer microenvironment and lipoprotein lipase: Another negative notch for a beneficial enzyme?'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36652113 ;
+	dct:created "2023-04-07 20:39:00"^^xsd:datetime  .
+gn:rif-4023-36519761-2023-04-27T20:33:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Parkin regulates neuronal lipid homeostasis through SREBP2-lipoprotein lipase pathway-implications for Parkinson\'s disease.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36519761 ;
+	dct:created "2023-04-27 20:33:00"^^xsd:datetime  .
+gn:rif-4023-36708756-2023-05-22T20:32:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Plasma Lipoprotein Lipase Is Associated with Risk of Future Major Adverse Cardiovascular Events in Patients Following Carotid Endarterectomy.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:36708756 ;
+	dct:created "2023-05-22 20:32:00"^^xsd:datetime  .
+gn:rif-4023-37155355-2023-07-04T21:12:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Inverse association between apolipoprotein C-II and cardiovascular mortality: role of lipoprotein lipase activity modulation.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37155355 ;
+	dct:created "2023-07-04 21:12:00"^^xsd:datetime  .
+gn:rif-4023-37432202-2023-07-13T20:35:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Effect of the Interaction between Seaweed Intake and LPL Polymorphisms on Metabolic Syndrome in Middle-Aged Korean Adults.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37432202 ;
+	dct:created "2023-07-13 20:35:00"^^xsd:datetime  .
+gn:rif-4023-37568214-2023-08-14T20:37:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Frameshift coding sequence variants in the LPL gene: identification of two novel events and exploration of the genotype-phenotype relationship for variants reported to date.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37568214 ;
+	dct:created "2023-08-14 20:37:00"^^xsd:datetime  .
+gn:rif-4023-37550668-2023-08-22T20:29:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'The East Asian-specific LPL p.Ala288Thr (c.862G > A) missense variant exerts a mild effect on protein function.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37550668 ;
+	dct:created "2023-08-22 20:29:00"^^xsd:datetime  .
+gn:rif-4023-37128695-2023-09-12T20:35:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Interaction between APOE, APOA1, and LPL Gene Polymorphisms and Variability in Changes in Lipid and Blood Pressure following Orange Juice Intake: A Pilot Study.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37128695 ;
+	dct:created "2023-09-12 20:35:00"^^xsd:datetime  .
+gn:rif-4023-37427758-2023-09-25T09:33:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Variants within the LPL gene confer susceptility to diabetic kidney disease and rapid decline in kidney function in Chinese patients with type 2 diabetes.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37427758 ;
+	dct:created "2023-09-25 09:33:00"^^xsd:datetime  .
+gn:rif-4023-37901192-2023-11-01T08:55:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'The Association of Adipokines and Myokines in the Blood of Obese Children and Adolescents with Lipoprotein Lipase rs328 Gene Variants.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37901192 ;
+	dct:created "2023-11-01 08:55:00"^^xsd:datetime  .
+gn:rif-4023-37871217-2023-11-10T08:44:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'The lipoprotein lipase that is shuttled into capillaries by GPIHBP1 enters the glycocalyx where it mediates lipoprotein processing.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37871217 ;
+	dct:created "2023-11-10 08:44:00"^^xsd:datetime  .
+gn:rif-4023-37858495-2023-12-28T20:33:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Clinical profile, genetic spectrum and therapy evaluation of 19 Chinese pediatric patients with lipoprotein lipase deficiency.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37858495 ;
+	dct:created "2023-12-28 20:33:00"^^xsd:datetime  .
+gn:rif-4023-38114521-2023-12-29T20:33:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Developing a model to predict the early risk of hypertriglyceridemia based on inhibiting lipoprotein lipase (LPL): a translational study.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "LPL" ;
+	gnt:hasGeneId generif:4023 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:38114521 ;
+	dct:created "2023-12-29 20:33:00"^^xsd:datetime  .
+gn:rif-6469-37511358-2023-08-19T08:35:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Low Expression of the NRP1 Gene Is Associated with Shorter Overall Survival in Patients with Sonic Hedgehog and Group 3 Medulloblastoma.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "SHH" ;
+	gnt:hasGeneId generif:6469 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37511358 ;
+	dct:created "2023-08-19 08:35:00"^^xsd:datetime  .
+gn:rif-6469-37307020-2023-09-21T20:40:00-5 rdf:type gnc:NCBIWikiEntry ;
+	rdfs:label 'Activation of Sonic Hedgehog Signaling Pathway Regulates Human Trabecular Meshwork Cell Function.'@en ;
+	gnt:belongsToSpecies gn:Homo_sapiens ;
+	gnt:symbol "SHH" ;
+	gnt:hasGeneId generif:6469 ;
+	skos:notation taxon:9606 ;
+	dct:hasVersion "5"^^xsd:integer ;
+	dct:references pubmed:37307020 ;
+	dct:created "2023-09-21 20:40:00"^^xsd:datetime  .
diff --git a/tests/unit/computations/test_partial_correlations.py b/tests/unit/computations/test_partial_correlations.py
index 066c650..6364701 100644
--- a/tests/unit/computations/test_partial_correlations.py
+++ b/tests/unit/computations/test_partial_correlations.py
@@ -159,8 +159,8 @@ class TestPartialCorrelations(TestCase):
                                    "variance": None}}},
                 dictified_control_samples),
             (("BXD2",), (7.80944,),
-             (7.51879, 7.77141, 8.39265, 8.17443, 8.30401, 7.80944, 8.39265,
-              8.17443, 8.30401, 7.80944, 7.51879, 7.77141, 7.80944),
+             ((7.51879, 7.77141, 8.39265, 8.17443, 8.30401, 7.80944, 8.39265,
+               8.17443, 8.30401, 7.80944, 7.51879, 7.77141, 7.80944),),
              (None,),
              (None, None, None, None, None, None, None, None, None, None, None,
               None, None)))
diff --git a/tests/unit/computations/test_wgcna.py b/tests/unit/computations/test_wgcna.py
index 55432af..325bd5a 100644
--- a/tests/unit/computations/test_wgcna.py
+++ b/tests/unit/computations/test_wgcna.py
@@ -85,9 +85,9 @@ class TestWgcna(TestCase):
             mock_img.return_value = b"AFDSFNBSDGJJHH"
 
             results = call_wgcna_script(
-                "Rscript/GUIX_PATH/scripts/r_file.R", request_data)
+                "Rscript/GUIX_PATH/scripts/r_file.R", request_data, "/tmp")
 
-            mock_dumping_data.assert_called_once_with(request_data)
+            mock_dumping_data.assert_called_once_with(request_data, "/tmp")
 
             mock_compose_wgcna.assert_called_once_with(
                 "Rscript/GUIX_PATH/scripts/r_file.R",
@@ -119,7 +119,7 @@ class TestWgcna(TestCase):
 
             mock_run_cmd.return_value = expected_error
             self.assertEqual(call_wgcna_script(
-                "input_file.R", ""), expected_error)
+                "input_file.R", "", "/tmp"), expected_error)
 
     @pytest.mark.skip(
         "This test assumes that the system will always be invoked from the root"
@@ -134,7 +134,6 @@ class TestWgcna(TestCase):
             wgcna_cmd, "Rscript scripts/wgcna.r /tmp/wgcna.json")
 
     @pytest.mark.unit_test
-    @mock.patch("gn3.computations.wgcna.TMPDIR", "/tmp")
     @mock.patch("gn3.computations.wgcna.uuid.uuid4")
     def test_create_json_file(self, file_name_generator):
         """test for writing the data to a csv file"""
@@ -166,8 +165,7 @@ class TestWgcna(TestCase):
 
             file_name_generator.return_value = "facb73ff-7eef-4053-b6ea-e91d3a22a00c"
 
-            results = dump_wgcna_data(
-                expected_input)
+            results = dump_wgcna_data(expected_input, "/tmp")
 
             file_handler.assert_called_once_with(
                 "/tmp/facb73ff-7eef-4053-b6ea-e91d3a22a00c.json", 'w', encoding='utf-8')
diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py
index 8005c8e..5526d16 100644
--- a/tests/unit/conftest.py
+++ b/tests/unit/conftest.py
@@ -7,6 +7,7 @@ import pytest
 
 from gn3.app import create_app
 
+
 @pytest.fixture(scope="session")
 def fxtr_app():
     """Fixture: setup the test app"""
@@ -15,7 +16,12 @@ def fxtr_app():
         testdb = Path(testdir).joinpath(
             f'testdb_{datetime.now().strftime("%Y%m%dT%H%M%S")}')
         app = create_app({
-            "TESTING": True, "AUTH_DB": testdb,
+            "TESTING": True,
+            "LMDB_DATA_PATH": str(
+                Path(__file__).parent.parent /
+                Path("test_data/lmdb-test-data")
+            ),
+            "AUTH_SERVER_URL": "http://127.0.0.1:8081",
             "OAUTH2_ACCESS_TOKEN_GENERATOR": "tests.unit.auth.test_token.gen_token"
         })
         app.testing = True
@@ -23,13 +29,15 @@ def fxtr_app():
         # Clean up after ourselves
         testdb.unlink(missing_ok=True)
 
+
 @pytest.fixture(scope="session")
-def client(fxtr_app): # pylint: disable=redefined-outer-name
+def client(fxtr_app):  # pylint: disable=redefined-outer-name
     """Create a test client fixture for tests"""
     with fxtr_app.app_context():
         yield fxtr_app.test_client()
 
+
 @pytest.fixture(scope="session")
-def fxtr_app_config(client): # pylint: disable=redefined-outer-name
+def fxtr_app_config(client):  # pylint: disable=redefined-outer-name
     """Return the test application's configuration object"""
     return client.application.config
diff --git a/tests/unit/db/rdf/data.py b/tests/unit/db/rdf/data.py
new file mode 100644
index 0000000..6bc612f
--- /dev/null
+++ b/tests/unit/db/rdf/data.py
@@ -0,0 +1,199 @@
+"""Some test data to be used in RDF data."""
+
+LPL_RIF_ENTRIES = {
+    "@context": {
+        "dct": "http://purl.org/dc/terms/",
+        "gnt": "http://genenetwork.org/term/",
+        "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+        "skos": "http://www.w3.org/2004/02/skos/core#",
+        "symbol": "gnt:symbol",
+        "species": "gnt:species",
+        "taxonomic_id": "skos:notation",
+        "gene_id": "gnt:hasGeneId",
+        "pubmed_id": "dct:references",
+        "created": "dct:created",
+        "comment": "rdfs:comment",
+        "version": "dct:hasVersion",
+    },
+    "data": [
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-02-23 20:40:00",
+            "pubmed_id": 36763533,
+            "comment": "Angiopoietin-like protein 4/8 complex-mediated plasmin generation \
+leads to cleavage of the complex and restoration of LPL activity.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-04-07 20:39:00",
+            "pubmed_id": 36652113,
+            "comment": "The breast cancer microenvironment and lipoprotein lipase: \
+Another negative notch for a beneficial enzyme?",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-04-27 20:33:00",
+            "pubmed_id": 36519761,
+            "comment": "Parkin regulates neuronal lipid homeostasis through \
+SREBP2-lipoprotein lipase pathway-implications for Parkinson's disease.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-05-22 20:32:00",
+            "pubmed_id": 36708756,
+            "comment": "Plasma Lipoprotein Lipase Is Associated with Risk of \
+Future Major Adverse Cardiovascular Events in Patients Following Carotid Endarterectomy.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-07-04 21:12:00",
+            "pubmed_id": 37155355,
+            "comment": "Inverse association between apolipoprotein C-II and \
+cardiovascular mortality: role of lipoprotein lipase activity modulation.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-07-13 20:35:00",
+            "pubmed_id": 37432202,
+            "comment": "Effect of the Interaction between Seaweed Intake and LPL \
+Polymorphisms on Metabolic Syndrome in Middle-Aged Korean Adults.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-08-14 20:37:00",
+            "pubmed_id": 37568214,
+            "comment": "Frameshift coding sequence variants in the LPL gene: identification \
+of two novel events and exploration of the genotype-phenotype relationship for \
+variants reported to date.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-08-22 20:29:00",
+            "pubmed_id": 37550668,
+            "comment": "The East Asian-specific LPL p.Ala288Thr (c.862G > A) missense \
+variant exerts a mild effect on protein function.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-09-12 20:35:00",
+            "pubmed_id": 37128695,
+            "comment": "Interaction between APOE, APOA1, and LPL Gene Polymorphisms \
+and Variability in Changes in Lipid and Blood Pressure following Orange Juice Intake: \
+A Pilot Study.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-09-25 09:33:00",
+            "pubmed_id": 37427758,
+            "comment": "Variants within the LPL gene confer susceptility to \
+diabetic kidney disease and rapid decline in kidney function in Chinese patients \
+with type 2 diabetes.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-11-01 08:55:00",
+            "pubmed_id": 37901192,
+            "comment": "The Association of Adipokines and Myokines in the \
+Blood of Obese Children and Adolescents with Lipoprotein Lipase rs328 Gene Variants.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-11-10 08:44:00",
+            "pubmed_id": 37871217,
+            "comment": "The lipoprotein lipase that is shuttled into \
+capillaries by GPIHBP1 enters the glycocalyx where it mediates lipoprotein processing.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-12-28 20:33:00",
+            "pubmed_id": 37858495,
+            "comment": "Clinical profile, genetic spectrum and therapy \
+evaluation of 19 Chinese pediatric patients with lipoprotein lipase deficiency.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 4023,
+            "version": 5,
+            "species": "Homo sapiens",
+            "symbol": "LPL",
+            "created": "2023-12-29 20:33:00",
+            "pubmed_id": 38114521,
+            "comment": "Developing a model to predict the early risk of \
+hypertriglyceridemia based on inhibiting lipoprotein lipase (LPL): a translational study.",
+            "taxonomic_id": 9606,
+        },
+        {
+            "gene_id": 16956,
+            "version": 5,
+            "species": "Mus musculus",
+            "symbol": "Lpl",
+            "created": "2023-04-27 20:33:00",
+            "pubmed_id": 36519761,
+            "comment": "Parkin regulates neuronal lipid homeostasis through \
+SREBP2-lipoprotein lipase pathway-implications for Parkinson's disease.",
+            "taxonomic_id": 10090,
+        },
+        {
+            "gene_id": 24539,
+            "version": 5,
+            "species": "Rattus norvegicus",
+            "symbol": "Lpl",
+            "created": "2023-12-29 20:33:00",
+            "pubmed_id": 38114521,
+            "comment": "Developing a model to predict the early risk of \
+hypertriglyceridemia based on inhibiting lipoprotein lipase (LPL): a translational study.",
+            "taxonomic_id": 10116,
+        },
+    ],
+}
diff --git a/tests/unit/db/rdf/test_wiki.py b/tests/unit/db/rdf/test_wiki.py
index 3abf3ad..bab37ce 100644
--- a/tests/unit/db/rdf/test_wiki.py
+++ b/tests/unit/db/rdf/test_wiki.py
@@ -22,11 +22,15 @@ from tests.fixtures.rdf import (
     SPARQL_CONF,
 )
 
+from tests.unit.db.rdf.data import LPL_RIF_ENTRIES
+
 from gn3.db.rdf.wiki import (
     __sanitize_result,
     get_wiki_entries_by_symbol,
     get_comment_history,
     update_wiki_comment,
+    get_rif_entries_by_symbol,
+    delete_wiki_entries_by_id,
 )
 
 GRAPH = "<http://cd-test.genenetwork.org>"
@@ -396,3 +400,49 @@ def test_update_wiki_comment(rdf_setup):  # pylint: disable=W0613,W0621
         "version": 3,
         "web_url": "http://some-website.com",
     })
+
+
+@pytest.mark.rdf
+def test_get_rif_entries_by_symbol(rdf_setup):  # pylint: disable=W0613,W0621
+    """Test fetching NCBI Rif Metadata from RDF"""
+    sparql_conf = SPARQL_CONF
+    entries = get_rif_entries_by_symbol(
+        symbol="Lpl",
+        sparql_uri=sparql_conf["sparql_endpoint"],
+        graph=GRAPH,
+    )
+    assert len(LPL_RIF_ENTRIES["data"]) == len(entries["data"])
+    for result, expected in zip(LPL_RIF_ENTRIES["data"], entries["data"]):
+        TestCase().assertDictEqual(result, expected)
+
+
+@pytest.mark.rdf
+def test_delete_wiki_entries_by_id(rdf_setup):  # pylint: disable=W0613,W0621
+    """Test deleting a given RIF Wiki entry"""
+    sparql_conf = SPARQL_CONF
+    delete_wiki_entries_by_id(
+        230,
+        sparql_user=sparql_conf["sparql_user"],
+        sparql_password=sparql_conf["sparql_password"],
+        sparql_auth_uri=sparql_conf["sparql_auth_uri"],
+        graph=GRAPH)
+    entries = get_comment_history(
+        comment_id=230,
+        sparql_uri=sparql_conf["sparql_endpoint"],
+        graph=GRAPH,
+    )
+    assert len(entries["data"]) == 0
+
+    # Deleting a non-existent entry has no effect
+    delete_wiki_entries_by_id(
+        199999,
+        sparql_user=sparql_conf["sparql_user"],
+        sparql_password=sparql_conf["sparql_password"],
+        sparql_auth_uri=sparql_conf["sparql_auth_uri"],
+        graph=GRAPH)
+    entries = get_comment_history(
+        comment_id=230,
+        sparql_uri=sparql_conf["sparql_endpoint"],
+        graph=GRAPH,
+    )
+    assert len(entries["data"]) == 0
diff --git a/tests/unit/db/test_case_attributes.py b/tests/unit/db/test_case_attributes.py
index 97a0703..998b58d 100644
--- a/tests/unit/db/test_case_attributes.py
+++ b/tests/unit/db/test_case_attributes.py
@@ -1,205 +1,326 @@
 """Test cases for gn3.db.case_attributes.py"""
 
+import pickle
+import tempfile
+import os
+import json
+from pathlib import Path
 import pytest
 from pytest_mock import MockFixture
-from gn3.db.case_attributes import get_unreviewed_diffs
-from gn3.db.case_attributes import get_case_attributes
-from gn3.db.case_attributes import insert_case_attribute_audit
-from gn3.db.case_attributes import approve_case_attribute
-from gn3.db.case_attributes import reject_case_attribute
+from gn3.db.case_attributes import queue_edit
+from gn3.db.case_attributes import (
+    CaseAttributeEdit,
+    EditStatus,
+    apply_change,
+    get_changes,
+    view_change
+)
 
 
 @pytest.mark.unit_test
-def test_get_case_attributes(mocker: MockFixture) -> None:
-    """Test that all the case attributes are fetched correctly"""
+def test_queue_edit(mocker: MockFixture) -> None:
+    """Test queueing an edit."""
     mock_conn = mocker.MagicMock()
     with mock_conn.cursor() as cursor:
-        cursor.fetchall.return_value = (
-            (1, "Condition", None),
-            (2, "Tissue", None),
-            (3, "Age", "Cum sociis natoque penatibus et magnis dis"),
-            (4, "Condition", "Description A"),
-            (5, "Condition", "Description B"),
-        )
-        results = get_case_attributes(mock_conn)
+        type(cursor).lastrowid = 28
+        tmpdir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
+        caseattr_id = queue_edit(
+            cursor,
+            directory=tmpdir,
+            edit=CaseAttributeEdit(
+                inbredset_id=1, status=EditStatus.review,
+                user_id="xxxx", changes={"a": 1, "b": 2}
+            ))
         cursor.execute.assert_called_once_with(
-            "SELECT Id, Name, Description FROM CaseAttribute"
-        )
-        assert results == (
-            (1, "Condition", None),
-            (2, "Tissue", None),
-            (3, "Age", "Cum sociis natoque penatibus et magnis dis"),
-            (4, "Condition", "Description A"),
-            (5, "Condition", "Description B"),
-        )
+            "INSERT INTO "
+            "caseattributes_audit(status, editor, json_diff_data) "
+            "VALUES (%s, %s, %s) "
+            "ON DUPLICATE KEY UPDATE status=%s",
+            ('review', 'xxxx', '{"a": 1, "b": 2}', 'review'))
+        assert 28 == caseattr_id
 
 
 @pytest.mark.unit_test
-def test_get_unreviewed_diffs(mocker: MockFixture) -> None:
-    """Test that the correct query is called when fetching unreviewed
-    case-attributes diff"""
-    mock_conn = mocker.MagicMock()
-    with mock_conn.cursor() as cursor:
-        _ = get_unreviewed_diffs(mock_conn)
-        cursor.fetchall.return_value = ((1, "editor", "diff_data_1"),)
-        cursor.execute.assert_called_once_with(
-            "SELECT id, editor, json_diff_data FROM "
-            "caseattributes_audit WHERE status = 'review'"
-        )
+def test_view_change(mocker: MockFixture) -> None:
+    """Test view_change function."""
+    sample_json_diff = {
+        "inbredset_id": 1,
+        "Modifications": {
+            "Original": {
+                "B6D2F1": {"Epoch": "10au"},
+                "BXD100": {"Epoch": "3b"},
+                "BXD101": {"SeqCvge": "29"},
+                "BXD102": {"Epoch": "3b"},
+                "BXD108": {"SeqCvge": ""}
+            },
+            "Current": {
+                "B6D2F1": {"Epoch": "10"},
+                "BXD100": {"Epoch": "3"},
+                "BXD101": {"SeqCvge": "2"},
+                "BXD102": {"Epoch": "3"},
+                "BXD108": {"SeqCvge": "oo"}
+            }
+        }
+    }
+    change_id = 28
+    mock_cursor, mock_conn = mocker.MagicMock(), mocker.MagicMock()
+    mock_conn.cursor.return_value = mock_cursor
+    mock_cursor.fetchone.return_value = (json.dumps(sample_json_diff), None)
+    assert view_change(mock_cursor, change_id) == sample_json_diff
+    mock_cursor.execute.assert_called_once_with(
+        "SELECT json_diff_data FROM caseattributes_audit WHERE id = %s",
+        (change_id,))
+    mock_cursor.fetchone.assert_called_once()
 
 
 @pytest.mark.unit_test
-def test_insert_case_attribute_audit(mocker: MockFixture) -> None:
-    """Test that the updating case attributes uses the correct query"""
-    mock_conn = mocker.MagicMock()
-    with mock_conn.cursor() as cursor:
-        _ = insert_case_attribute_audit(
-            mock_conn, status="review", author="Author", data="diff_data"
-        )
-        cursor.execute.assert_called_once_with(
-            "INSERT INTO caseattributes_audit "
-            "(status, editor, json_diff_data) "
-            "VALUES (%s, %s, %s)",
-            ("review", "Author", "diff_data"),
-        )
+def test_view_change_invalid_json(mocker: MockFixture) -> None:
+    """Test invalid json when view_change is called"""
+    change_id = 28
+    mock_cursor, mock_conn = mocker.MagicMock(), mocker.MagicMock()
+    mock_conn.cursor.return_value = mock_cursor
+    mock_cursor.fetchone.return_value = ("invalid_json_string", None)
+    with pytest.raises(json.JSONDecodeError):
+        view_change(mock_cursor, change_id)
+    mock_cursor.execute.assert_called_once_with(
+        "SELECT json_diff_data FROM caseattributes_audit WHERE id = %s",
+        (change_id,))
 
 
 @pytest.mark.unit_test
-def test_reject_case_attribute(mocker: MockFixture) -> None:
-    """Test rejecting a case-attribute"""
-    mock_conn = mocker.MagicMock()
-    with mock_conn.cursor() as cursor:
-        _ = reject_case_attribute(
-            mock_conn,
-            case_attr_audit_id=1,
-        )
-        cursor.execute.assert_called_once_with(
-            "UPDATE caseattributes_audit SET "
-            "status = 'rejected' WHERE id = %s",
-            (1,),
-        )
+def test_view_change_no_data(mocker: MockFixture) -> None:
+    "Test no result when view_change is called"
+    change_id = 28
+    mock_cursor, mock_conn = mocker.MagicMock(), mocker.MagicMock()
+    mock_conn.cursor.return_value = mock_cursor
+    mock_cursor.fetchone.return_value = (None, None)
+    assert view_change(mock_cursor, change_id) == {}
+    mock_cursor.execute.assert_called_once_with(
+        "SELECT json_diff_data FROM caseattributes_audit WHERE id = %s",
+        (change_id,))
 
 
 @pytest.mark.unit_test
-def test_approve_inserting_case_attribute(mocker: MockFixture) -> None:
-    """Test approving inserting a case-attribute"""
-    mock_conn = mocker.MagicMock()
-    with mock_conn.cursor() as cursor:
-        type(cursor).rowcount = 1
-        cursor.fetchone.return_value = (
-            """
-        {"Insert": {"name": "test", "description": "Random Description"}}
-        """,
-        )
-        _ = approve_case_attribute(
-            mock_conn,
-            case_attr_audit_id=3,
-        )
-        calls = [
-            mocker.call(
-                "SELECT json_diff_data FROM caseattributes_audit "
-                "WHERE id = %s",
-                (3,),
-            ),
-            mocker.call(
-                "INSERT INTO CaseAttribute "
-                "(Name, Description) VALUES "
-                "(%s, %s)",
-                (
-                    "test",
-                    "Random Description",
-                ),
-            ),
-            mocker.call(
-                "UPDATE caseattributes_audit SET "
-                "status = 'approved' WHERE id = %s",
-                (3,),
-            ),
+def test_apply_change_approved(mocker: MockFixture) -> None:
+    """Test approving a change"""
+    mock_cursor, mock_conn = mocker.MagicMock(), mocker.MagicMock()
+    mock_conn.cursor.return_value = mock_cursor
+    mock_lmdb = mocker.patch("gn3.db.case_attributes.lmdb")
+    mock_env, mock_txn = mocker.MagicMock(), mocker.MagicMock()
+    mock_lmdb.open.return_value = mock_env
+    mock_env.begin.return_value.__enter__.return_value = mock_txn
+    change_id, review_ids = 1, {1, 2, 3}
+    mock_txn.get.side_effect = (
+        pickle.dumps(review_ids),  # b"review" key
+        None,                      # b"approved" key
+    )
+    tmpdir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
+    mock_cursor.fetchone.return_value = (json.dumps({
+        "inbredset_id": 1,
+        "Modifications": {
+            "Current": {
+                "B6D2F1": {"Epoch": "10"},
+                "BXD100": {"Epoch": "3"},
+                "BXD101": {"SeqCvge": "2"},
+                "BXD102": {"Epoch": "3"},
+                "BXD108": {"SeqCvge": "oo"}
+            }
+        }
+    }), None)
+    mock_cursor.fetchall.side_effect = [
+        [  # Strain query
+            ("B6D2F1", 1), ("BXD100", 2),
+            ("BXD101", 3), ("BXD102", 4),
+            ("BXD108", 5)],
+        [  # CaseAttribute query
+            ("Epoch", 101), ("SeqCvge", 102)]
+    ]
+    assert apply_change(mock_cursor, EditStatus.approved,
+                        change_id, tmpdir) is True
+    assert mock_cursor.execute.call_count == 4
+    mock_cursor.execute.assert_has_calls([
+        mocker.call(
+            "SELECT json_diff_data FROM caseattributes_audit WHERE id = %s",
+            (change_id,)),
+        mocker.call(
+            "SELECT Name, Id FROM Strain WHERE Name IN (%s, %s, %s, %s, %s)",
+            ("B6D2F1", "BXD100", "BXD101", "BXD102", "BXD108")),
+        mocker.call(
+            "SELECT Name, CaseAttributeId FROM CaseAttribute "
+            "WHERE InbredSetId = %s AND Name IN (%s, %s)",
+            (1, "Epoch", "SeqCvge")),
+        mocker.call(
+            "UPDATE caseattributes_audit SET status = %s WHERE id = %s",
+            ("approved", change_id))
+    ])
+    mock_cursor.executemany.assert_called_once_with(
+        "INSERT INTO CaseAttributeXRefNew (InbredSetId, StrainId, CaseAttributeId, Value) "
+        "VALUES (%(inbredset_id)s, %(strain_id)s, %(caseattr_id)s, %(value)s) "
+        "ON DUPLICATE KEY UPDATE Value = VALUES(Value)",
+        [
+            {"inbredset_id": 1, "strain_id": 1, "caseattr_id": 101, "value": "10"},
+            {"inbredset_id": 1, "strain_id": 2, "caseattr_id": 101, "value": "3"},
+            {"inbredset_id": 1, "strain_id": 3, "caseattr_id": 102, "value": "2"},
+            {"inbredset_id": 1, "strain_id": 4, "caseattr_id": 101, "value": "3"},
+            {"inbredset_id": 1, "strain_id": 5, "caseattr_id": 102, "value": "oo"}
         ]
-        cursor.execute.assert_has_calls(calls, any_order=False)
+    )
 
 
 @pytest.mark.unit_test
-def test_approve_deleting_case_attribute(mocker: MockFixture) -> None:
-    """Test deleting a case-attribute"""
-    mock_conn = mocker.MagicMock()
-    with mock_conn.cursor() as cursor:
-        type(cursor).rowcount = 1
-        cursor.fetchone.return_value = (
-            """
-        {"Deletion": {"id": "12", "name": "test", "description": ""}}
-        """,
-        )
-        _ = approve_case_attribute(
-            mock_conn,
-            case_attr_audit_id=3,
-        )
-        calls = [
-            mocker.call(
-                "SELECT json_diff_data FROM caseattributes_audit "
-                "WHERE id = %s",
-                (3,),
-            ),
-            mocker.call("DELETE FROM CaseAttribute WHERE Id = %s", ("12",)),
-            mocker.call(
-                "UPDATE caseattributes_audit SET "
-                "status = 'approved' WHERE id = %s",
-                (3,),
-            ),
-        ]
-        cursor.execute.assert_has_calls(calls, any_order=False)
+def test_apply_change_rejected(mocker: MockFixture) -> None:
+    """Test rejecting a change"""
+    mock_cursor, mock_conn = mocker.MagicMock(), mocker.MagicMock()
+    mock_conn.cursor.return_value = mock_cursor
+    mock_lmdb = mocker.patch("gn3.db.case_attributes.lmdb")
+    mock_env, mock_txn = mocker.MagicMock(), mocker.MagicMock()
+    mock_lmdb.open.return_value = mock_env
+    mock_env.begin.return_value.__enter__.return_value = mock_txn
+    tmpdir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
+    change_id, review_ids = 3, {1, 2, 3}
+    mock_txn.get.side_effect = [
+        pickle.dumps(review_ids),  # review_ids
+        None  # rejected_ids (initially empty)
+    ]
+
+    assert apply_change(mock_cursor, EditStatus.rejected,
+                        change_id, tmpdir) is True
+
+    # Verify SQL query call sequence
+    mock_cursor.execute.assert_called_once_with(
+        "UPDATE caseattributes_audit SET status = %s WHERE id = %s",
+        (str(EditStatus.rejected), change_id))
+    mock_cursor.executemany.assert_not_called()
+
+    # Verify LMDB operations
+    mock_env.begin.assert_called_once_with(write=True)
+    expected_txn_calls = [
+        mocker.call(b"review", pickle.dumps({1, 2})),
+        mocker.call(b"rejected", pickle.dumps({3}))
+    ]
+    mock_txn.put.assert_has_calls(expected_txn_calls, any_order=False)
 
 
 @pytest.mark.unit_test
-def test_approve_modifying_case_attribute(mocker: MockFixture) -> None:
-    """Test modifying a case-attribute"""
-    mock_conn = mocker.MagicMock()
-    with mock_conn.cursor() as cursor:
-        type(cursor).rowcount = 1
-        cursor.fetchone.return_value = (
-            """
-{
-  "id": "12",
-  "Modification": {
-    "description": {
-      "Current": "Test",
-      "Original": "A"
-    },
-    "name": {
-      "Current": "Height (A)",
-      "Original": "Height"
+def test_apply_change_non_existent_change_id(mocker: MockFixture) -> None:
+    """Test that there's a missing change_id from the returned LMDB rejected set."""
+    mock_env, mock_txn = mocker.MagicMock(), mocker.MagicMock()
+    mock_cursor, mock_conn = mocker.MagicMock(), mocker.MagicMock()
+    mock_lmdb = mocker.patch("gn3.db.case_attributes.lmdb")
+    mock_lmdb.open.return_value = mock_env
+    mock_conn.cursor.return_value = mock_cursor
+    mock_env.begin.return_value.__enter__.return_value = mock_txn
+    change_id, review_ids = 28, {1, 2, 3}
+    mock_txn.get.side_effect = [
+        pickle.dumps(review_ids),  # b"review" key
+        None,                      # b"approved" key
+    ]
+    tmpdir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
+    assert apply_change(mock_cursor, EditStatus.approved,
+                        change_id, tmpdir) is False
+
+
+@pytest.mark.unit_test
+def test_get_changes(mocker: MockFixture) -> None:
+    """Test that reviews are correctly fetched"""
+    mock_fetch_case_attrs_changes = mocker.patch(
+        "gn3.db.case_attributes.__fetch_case_attrs_changes__"
+    )
+    mock_fetch_case_attrs_changes.return_value = [
+        {
+            "editor": "user1",
+            "json_diff_data": {
+                "inbredset_id": 1,
+                "Modifications": {
+                    "Original": {
+                        "B6D2F1": {"Epoch": "10au"},
+                        "BXD100": {"Epoch": "3b"},
+                        "BXD101": {"SeqCvge": "29"},
+                        "BXD102": {"Epoch": "3b"},
+                        "BXD108": {"SeqCvge": ""}
+                    },
+                    "Current": {
+                        "B6D2F1": {"Epoch": "10"},
+                        "BXD100": {"Epoch": "3"},
+                        "BXD101": {"SeqCvge": "2"},
+                        "BXD102": {"Epoch": "3"},
+                        "BXD108": {"SeqCvge": "oo"}
+                    }
+                }
+            },
+            "time_stamp": "2025-07-01 12:00:00"
+        },
+        {
+            "editor": "user2",
+            "json_diff_data": {
+                "inbredset_id": 1,
+                "Modifications": {
+                    "Original": {"BXD200": {"Epoch": "5a"}},
+                    "Current": {"BXD200": {"Epoch": "5"}}
+                }
+            },
+            "time_stamp": "2025-07-01 12:01:00"
+        }
+    ]
+    mock_lmdb = mocker.patch("gn3.db.case_attributes.lmdb")
+    mock_env, mock_txn = mocker.MagicMock(), mocker.MagicMock()
+    mock_lmdb.open.return_value = mock_env
+    mock_env.begin.return_value.__enter__.return_value = mock_txn
+    review_ids, approved_ids, rejected_ids = {1, 4}, {2, 3}, {5, 6, 7, 10}
+    mock_txn.get.side_effect = (
+        pickle.dumps(review_ids),    # b"review" key
+        pickle.dumps(approved_ids),  # b"approved" key
+        pickle.dumps(rejected_ids)   # b"rejected" key
+    )
+    result = get_changes(cursor=mocker.MagicMock(),
+                         change_type=EditStatus.review,
+                         directory=Path("/tmp"))
+    expected = {
+        "change-type": "review",
+        "count": {
+            "reviews": 2,
+            "approvals": 2,
+            "rejections": 4
+        },
+        "data": {
+            1: {
+                "editor": "user1",
+                "json_diff_data": {
+                    "inbredset_id": 1,
+                    "Modifications": {
+                        "Original": {
+                            "B6D2F1": {"Epoch": "10au"},
+                            "BXD100": {"Epoch": "3b"},
+                            "BXD101": {"SeqCvge": "29"},
+                            "BXD102": {"Epoch": "3b"},
+                            "BXD108": {"SeqCvge": ""}
+                        },
+                        "Current": {
+                            "B6D2F1": {"Epoch": "10"},
+                            "BXD100": {"Epoch": "3"},
+                            "BXD101": {"SeqCvge": "2"},
+                            "BXD102": {"Epoch": "3"},
+                            "BXD108": {"SeqCvge": "oo"}
+                        }
+                    }
+                },
+                "time_stamp": "2025-07-01 12:00:00"
+            },
+            4: {
+                'editor': 'user2',
+                'json_diff_data': {
+                    'inbredset_id': 1,
+                    'Modifications': {
+                        'Original': {
+                            'BXD200': {'Epoch': '5a'}
+                        },
+                        'Current': {
+                            'BXD200': {'Epoch': '5'}
+                        }
+                    }
+                },
+                "time_stamp": "2025-07-01 12:01:00"
+            }
+        }
     }
-  }
-}""",
-        )
-        _ = approve_case_attribute(
-            mock_conn,
-            case_attr_audit_id=3,
-        )
-        calls = [
-            mocker.call(
-                "SELECT json_diff_data FROM caseattributes_audit "
-                "WHERE id = %s",
-                (3,),
-            ),
-            mocker.call(
-                "UPDATE CaseAttribute SET Description = %s WHERE Id = %s",
-                (
-                    "Test",
-                    "12",
-                ),
-            ),
-            mocker.call(
-                "UPDATE CaseAttribute SET Name = %s WHERE Id = %s",
-                (
-                    "Height (A)",
-                    "12",
-                ),
-            ),
-            mocker.call(
-                "UPDATE caseattributes_audit SET "
-                "status = 'approved' WHERE id = %s",
-                (3,),
-            ),
-        ]
-        cursor.execute.assert_has_calls(calls, any_order=False)
+    assert result == expected
diff --git a/tests/unit/db/test_gen_menu.py b/tests/unit/db/test_gen_menu.py
index e6b5711..f64b4d3 100644
--- a/tests/unit/db/test_gen_menu.py
+++ b/tests/unit/db/test_gen_menu.py
@@ -120,7 +120,7 @@ class TestGenMenu(unittest.TestCase):
         with db_mock.cursor() as conn:
             with conn.cursor() as cursor:
                 for item in ["x", ("result"), ["result"], [1]]:
-                    cursor.fetchone.return_value = (item)
+                    cursor.fetchone.return_value = item
                 self.assertTrue(phenotypes_exist(db_mock, "test"))
 
     @pytest.mark.unit_test
@@ -140,7 +140,7 @@ class TestGenMenu(unittest.TestCase):
         db_mock = mock.MagicMock()
         with db_mock.cursor() as cursor:
             for item in ["x", ("result"), ["result"], [1]]:
-                cursor.fetchone.return_value = (item)
+                cursor.fetchone.return_value = item
                 self.assertTrue(phenotypes_exist(db_mock, "test"))
 
     @pytest.mark.unit_test
diff --git a/tests/unit/test_db_utils.py b/tests/unit/test_db_utils.py
index beb7169..51f4296 100644
--- a/tests/unit/test_db_utils.py
+++ b/tests/unit/test_db_utils.py
@@ -1,25 +1,61 @@
 """module contains test for db_utils"""
-from unittest import mock
-
 import pytest
 
-from gn3.db_utils import parse_db_url, database_connection
+from gn3.db_utils import parse_db_url
+
 
 @pytest.mark.unit_test
-@mock.patch("gn3.db_utils.mdb")
-@mock.patch("gn3.db_utils.parse_db_url")
-def test_database_connection(mock_db_parser, mock_sql):
-    """test for creating database connection"""
-    mock_db_parser.return_value = ("localhost", "guest", "4321", "users", None)
+@pytest.mark.parametrize(
+    "sql_uri,expected",
+    (("mysql://theuser:passwd@thehost:3306/thedb",
+      {
+          "host": "thehost",
+          "port": 3306,
+          "user": "theuser",
+          "password": "passwd",
+          "database": "thedb"
+      }),
+     (("mysql://auser:passwd@somehost:3307/thedb?"
+       "unix_socket=/run/mysqld/mysqld.sock&connect_timeout=30"),
+      {
+          "host": "somehost",
+          "port": 3307,
+          "user": "auser",
+          "password": "passwd",
+          "database": "thedb",
+          "unix_socket": "/run/mysqld/mysqld.sock",
+          "connect_timeout": 30
+      }),
+     ("mysql://guest:4321@localhost/users",
+      {
+          "host": "localhost",
+          "port": 3306,
+          "user": "guest",
+          "password": "4321",
+          "database": "users"
+      }),
+     ("mysql://localhost/users",
+      {
+          "host": "localhost",
+          "port": 3306,
+          "user": None,
+          "password": None,
+          "database": "users"
+      })))
+def test_parse_db_url(sql_uri, expected):
+    """Test that valid URIs are passed into valid connection dicts"""
+    assert parse_db_url(sql_uri) == expected
 
-    with database_connection("mysql://guest:4321@localhost/users") as _conn:
-        mock_sql.connect.assert_called_with(
-            db="users", user="guest", passwd="4321", host="localhost",
-            port=3306)
 
 @pytest.mark.unit_test
-def test_parse_db_url():
-    """test for parsing db_uri env variable"""
-    results = parse_db_url("mysql://username:4321@localhost/test")
-    expected_results = ("localhost", "username", "4321", "test", None)
-    assert results == expected_results
+@pytest.mark.parametrize(
+    "sql_uri,invalidopt",
+    (("mysql://localhost/users?socket=/run/mysqld/mysqld.sock", "socket"),
+     ("mysql://localhost/users?connect_timeout=30&notavalidoption=value",
+      "notavalidoption")))
+def test_parse_db_url_with_invalid_options(sql_uri, invalidopt):
+    """Test that invalid options cause the function to raise an exception."""
+    with pytest.raises(AssertionError) as exc_info:
+        parse_db_url(sql_uri)
+
+    assert exc_info.value.args[0] == f"Invalid database connection option ({invalidopt}) provided."
diff --git a/tests/unit/test_llm.py b/tests/unit/test_llm.py
index 8fbaba6..3a79486 100644
--- a/tests/unit/test_llm.py
+++ b/tests/unit/test_llm.py
@@ -1,11 +1,22 @@
 """Test cases for procedures defined in llms """
 # pylint: disable=C0301
+# pylint: disable=W0613
+from datetime import datetime, timedelta
+from unittest.mock import patch
+from unittest.mock import MagicMock
+
 import pytest
 from gn3.llms.process import fetch_pubmed
 from gn3.llms.process import parse_context
 from gn3.llms.process import format_bibliography_info
+from gn3.llms.errors import LLMError
+from gn3.api.llm  import clean_query
+from gn3.api.llm  import is_verified_anonymous_user
+from gn3.api.llm  import is_valid_address
+from gn3.api.llm  import check_rate_limiter
 
 
+FAKE_NOW = datetime(2025, 1, 1, 12, 0, 0)
 @pytest.mark.unit_test
 def test_parse_context():
     """test for parsing doc id context"""
@@ -104,3 +115,130 @@ def test_fetching_pubmed_info(monkeypatch):
 
     assert (fetch_pubmed(data, "/pubmed.json",  "data/")
             == expected_results)
+
+
+@pytest.mark.unit_test
+def test_clean_query():
+    """Test function for cleaning up query"""
+    assert clean_query("!what is genetics.") == "what is genetics"
+    assert clean_query("hello test?") == "hello test"
+    assert clean_query("  hello test with space?") == "hello test with space"
+
+
+@pytest.mark.unit_test
+def test_is_verified_anonymous_user():
+    """Test function for verifying anonymous user metadata"""
+    assert is_verified_anonymous_user({}) is False
+    assert is_verified_anonymous_user({"Anonymous-Id" : "qws2121dwsdwdwe",
+                                        "Anonymous-Status" : "verified"}) is True
+
+@pytest.mark.unit_test
+def test_is_valid_address() :
+    """Test function checks if is a valid ip address is valid"""
+    assert  is_valid_address("invalid_ip") is False
+    assert is_valid_address("127.0.0.1") is True
+
+
+@patch("gn3.api.llm.datetime")
+@patch("gn3.api.llm.db.connection")
+@patch("gn3.api.llm.is_valid_address", return_value=True)
+@pytest.mark.unit_test
+def test_first_time_visitor(mock_is_valid, mock_db_conn, mock_datetime):
+    """Test rate limiting for first-time visitor"""
+    mock_datetime.utcnow.return_value = FAKE_NOW
+    mock_datetime.strptime = datetime.strptime  # keep real one
+    mock_datetime.strftime = datetime.strftime  # keep real one
+
+    # Set up DB mock
+    mock_conn = MagicMock()
+    mock_cursor = MagicMock()
+    mock_conn.__enter__.return_value = mock_conn
+    mock_conn.cursor.return_value = mock_cursor
+    mock_cursor.fetchone.return_value = None
+    mock_db_conn.return_value = mock_conn
+
+    result = check_rate_limiter("127.0.0.1", "test/llm.db", "Chromosome x")
+    assert result is True
+    mock_cursor.execute.assert_any_call("""
+                INSERT INTO Limiter(identifier, tokens, expiry_time)
+                VALUES (?, ?, ?)
+            """, ("127.0.0.1", 4, "2025-01-01 12:24:00"))
+
+
+@patch("gn3.api.llm.datetime")
+@patch("gn3.api.llm.db.connection")
+@patch("gn3.api.llm.is_valid_address", return_value=True)
+@pytest.mark.unit_test
+def test_visitor_at_limit(mock_is_valid, mock_db_conn, mock_datetime):
+    """Test rate limiting for Visitor at limit"""
+    mock_datetime.utcnow.return_value = FAKE_NOW
+    mock_datetime.strptime = datetime.strptime  # keep real one
+    mock_datetime.strftime = datetime.strftime
+
+    mock_conn = MagicMock()
+    mock_cursor = MagicMock()
+    mock_conn.__enter__.return_value = mock_conn
+    mock_conn.cursor.return_value = mock_cursor
+    fake_expiry = (FAKE_NOW + timedelta(minutes=10)).strftime("%Y-%m-%d %H:%M:%S")
+    mock_cursor.fetchone.return_value = (0, fake_expiry) #token returned are 0
+    mock_db_conn.return_value = mock_conn
+    with pytest.raises(LLMError) as exc_info:
+        check_rate_limiter("127.0.0.1", "test/llm.db", "Chromosome x")
+    # assert llm error with correct message is raised
+    assert exc_info.value.args == ('Rate limit exceeded. Please try again later.', 'Chromosome x')
+
+
+@patch("gn3.api.llm.datetime")
+@patch("gn3.api.llm.db.connection")
+@patch("gn3.api.llm.is_valid_address", return_value=True)
+@pytest.mark.unit_test
+def test_visitor_with_tokens(mock_is_valid, mock_db_conn, mock_datetime):
+    """Test rate limiting for user with valid tokens"""
+
+    mock_datetime.utcnow.return_value = FAKE_NOW
+    mock_datetime.strptime = datetime.strptime  # Use real versions
+    mock_datetime.strftime = datetime.strftime
+
+    mock_conn = MagicMock()
+    mock_cursor = MagicMock()
+    mock_conn.__enter__.return_value = mock_conn
+    mock_conn.cursor.return_value = mock_cursor
+
+    fake_expiry = (FAKE_NOW + timedelta(minutes=10)).strftime("%Y-%m-%d %H:%M:%S")
+    mock_cursor.fetchone.return_value = (3, fake_expiry)  # Simulate 3 tokens
+
+    mock_db_conn.return_value = mock_conn
+
+    results = check_rate_limiter("127.0.0.1", "test/llm.db", "Chromosome x")
+    assert results is True
+    mock_cursor.execute.assert_any_call("""
+                        UPDATE Limiter
+                        SET tokens = tokens - 1
+                        WHERE identifier = ? AND tokens > 0
+                    """, ("127.0.0.1",))
+
+@patch("gn3.api.llm.datetime")
+@patch("gn3.api.llm.db.connection")
+@patch("gn3.api.llm.is_valid_address", return_value=True)
+@pytest.mark.unit_test
+def test_visitor_token_expired(mock_is_valid, mock_db_conn, mock_datetime):
+    """Test rate limiting for expired tokens"""
+
+    mock_datetime.utcnow.return_value = FAKE_NOW
+    mock_datetime.strptime = datetime.strptime
+    mock_datetime.strftime = datetime.strftime
+    mock_conn = MagicMock()
+    mock_cursor = MagicMock()
+    mock_conn.__enter__.return_value = mock_conn
+    mock_conn.cursor.return_value = mock_cursor
+    fake_expiry = (FAKE_NOW - timedelta(minutes=10)).strftime("%Y-%m-%d %H:%M:%S")
+    mock_cursor.fetchone.return_value = (3, fake_expiry)  # Simulate 3 tokens
+    mock_db_conn.return_value = mock_conn
+
+    result = check_rate_limiter("127.0.0.1", "test/llm.db", "Chromosome x")
+    assert result is True
+    mock_cursor.execute.assert_any_call("""
+                    UPDATE Limiter
+                    SET tokens = ?, expiry_time = ?
+                    WHERE identifier = ?
+                """, (4, "2025-01-01 12:24:00", "127.0.0.1"))
diff --git a/tests/unit/test_rqtl2.py b/tests/unit/test_rqtl2.py
new file mode 100644
index 0000000..ddce91b
--- /dev/null
+++ b/tests/unit/test_rqtl2.py
@@ -0,0 +1,123 @@
+"""Module contains the unittest for rqtl2 functions  """
+# pylint: disable=C0301
+from unittest import mock
+import pytest
+from gn3.computations.rqtl2 import compose_rqtl2_cmd
+from gn3.computations.rqtl2 import generate_rqtl2_files
+from gn3.computations.rqtl2 import prepare_files
+from gn3.computations.rqtl2 import validate_required_keys
+
+
+@pytest.mark.unit_test
+@mock.patch("gn3.computations.rqtl2.write_to_csv")
+def test_generate_rqtl2_files(mock_write_to_csv):
+    """Test for generating rqtl2 files from set of inputs"""
+
+    mock_write_to_csv.side_effect = (
+        "/tmp/workspace/geno_file.csv",
+        "/tmp/workspace/pheno_file.csv"
+    )
+    data = {"crosstype": "riself",
+            "geno_data": [{"NAME": "Ge_code_1"}],
+            "pheno_data":  [{"NAME": "14343_at"}],
+            "alleles": ["L", "C"],
+            "geno_codes": {
+                "L": 1,
+                "C": 2
+            },
+            "na.strings": ["-", "NA"]
+            }
+
+    test_results = generate_rqtl2_files(data, "/tmp/workspace")
+    expected_results = {"geno_file": "/tmp/workspace/geno_file.csv",
+                        "pheno_file": "/tmp/workspace/pheno_file.csv",
+                        **data
+                        }
+    assert test_results == expected_results
+
+    # assert data is written to the csv
+    expected_calls = [mock.call(
+        "/tmp/workspace",
+        "geno_file.csv",
+        [{"NAME": "Ge_code_1"}]
+    ),
+        mock.call(
+        "/tmp/workspace",
+        "pheno_file.csv",
+        [{"NAME": "14343_at"}]
+    )]
+    mock_write_to_csv.assert_has_calls(expected_calls)
+
+
+@pytest.mark.unit_test
+def test_validate_required_keys():
+    """Test to validate required keys are in a dataset"""
+    required_keys = ["geno_data", "pheno_data", "geno_codes"]
+    assert ((False,
+            "Required key(s) missing: geno_data, pheno_data, geno_codes")
+            == validate_required_keys(required_keys, {})
+            )
+    assert ((True,
+            "")
+            == validate_required_keys(required_keys, {
+                "geno_data": [],
+                "pheno_data": [],
+                "geno_codes": {}
+            })
+            )
+
+
+@pytest.mark.unit_test
+def test_compose_rqtl2_cmd():
+    """Test for composing rqtl2 command"""
+    input_file = "/tmp/575732e-691e-49e5-8d82-30c564927c95/input_file.json"
+    output_file = "/tmp/575732e-691e-49e5-8d82-30c564927c95/output_file.json"
+    directory = "/tmp/575732e-691e-49e5-8d82-30c564927c95"
+    expected_results = f"Rscript /rqtl2_wrapper.R --input_file {input_file} --directory {directory} --output_file {output_file} --nperm 12 --method LMM --threshold 0.05 --cores 1"
+
+    # test for using default configs
+    assert compose_rqtl2_cmd(rqtl_path="/rqtl2_wrapper.R",
+                             input_file=input_file,
+                             output_file=output_file,
+                             workspace_dir=directory,
+                             data={
+                                 "nperm": 12,
+                                 "threshold": 0.05,
+                                 "method" : "LMM"
+                             },
+                             config={}) == expected_results
+
+    # test for default permutation, method  and threshold and  custom configs
+    expected_results = f"/bin/rscript /rqtl2_wrapper.R --input_file {input_file} --directory {directory} --output_file {output_file} --nperm 0 --method HK --threshold 1 --cores 12"
+    assert (compose_rqtl2_cmd(rqtl_path="/rqtl2_wrapper.R",
+                              input_file=input_file,
+                              output_file=output_file,
+                              workspace_dir=directory,
+                              data={},
+                              config={"MULTIPROCESSOR_PROCS": 12, "RSCRIPT": "/bin/rscript"})
+            == expected_results)
+
+
+@pytest.mark.unit_test
+@mock.patch("gn3.computations.rqtl2.os.makedirs")
+@mock.patch("gn3.computations.rqtl2.create_file")
+@mock.patch("gn3.computations.rqtl2.uuid")
+def test_preparing_rqtl_files(mock_uuid, mock_create_file, mock_mkdir):
+    """test to create required rqtl files"""
+    mock_create_file.return_value = None
+    mock_mkdir.return_value = None
+    mock_uuid.uuid4.return_value = "2fc75611-1524-418e-970f-67f94ea09846"
+    assert (
+        (
+            "/tmp/2fc75611-1524-418e-970f-67f94ea09846",
+            "/tmp/2fc75611-1524-418e-970f-67f94ea09846/rqtl2-input-2fc75611-1524-418e-970f-67f94ea09846.json",
+            "/tmp/2fc75611-1524-418e-970f-67f94ea09846/rqtl2-output-2fc75611-1524-418e-970f-67f94ea09846.json",
+            "/tmp/rqtl2-log-2fc75611-1524-418e-970f-67f94ea09846"
+        ) == prepare_files(tmpdir="/tmp/")
+    )
+    # assert method to create files is called
+    expected_calls = [mock.call("/tmp/2fc75611-1524-418e-970f-67f94ea09846/rqtl2-input-2fc75611-1524-418e-970f-67f94ea09846.json"),
+                      mock.call(
+                          "/tmp/2fc75611-1524-418e-970f-67f94ea09846/rqtl2-output-2fc75611-1524-418e-970f-67f94ea09846.json"),
+                      mock.call("/tmp/rqtl2-log-2fc75611-1524-418e-970f-67f94ea09846")]
+    mock_create_file.assert_has_calls(expected_calls)