diff options
Diffstat (limited to 'tests/unit/db')
| -rw-r--r-- | tests/unit/db/rdf/data.py | 199 | ||||
| -rw-r--r-- | tests/unit/db/rdf/test_wiki.py | 50 | ||||
| -rw-r--r-- | tests/unit/db/test_case_attributes.py | 471 | ||||
| -rw-r--r-- | tests/unit/db/test_gen_menu.py | 4 |
4 files changed, 547 insertions, 177 deletions
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 |
