From c42dccabdde8c1ac05e9b0acde1a1e6d3382ccce Mon Sep 17 00:00:00 2001 From: vicpaton Date: Thu, 12 Sep 2024 18:46:23 +0200 Subject: [PATCH] added tests --- tests/test_eval_graph.py | 101 +++++++++++++++++++++++++++++++++++ tests/test_methods_causal.py | 14 ++++- tests/test_methods_graph.py | 11 +++- tests/test_omics.py | 44 ++++++++++++--- 4 files changed, 160 insertions(+), 10 deletions(-) diff --git a/tests/test_eval_graph.py b/tests/test_eval_graph.py index afb2fab..6f148b4 100644 --- a/tests/test_eval_graph.py +++ b/tests/test_eval_graph.py @@ -60,6 +60,107 @@ def test_get_recovered_offtargets(network): assert result['perc_offtargets'][0] == 2 / 3 * 100 + +@patch('networkcommons.data.omics.panacea_experiments') +@patch('networkcommons.data.omics.panacea_gold_standard') +@patch('networkcommons.data.network.get_omnipath') +@patch('networkcommons.utils.network_from_df') +@patch('networkcommons.data.omics.panacea_tables') +@patch('networkcommons.methods.run_shortest_paths') +@patch('networkcommons.methods.run_sign_consistency') +@patch('networkcommons.methods.run_all_paths') +@patch('networkcommons.methods.add_pagerank_scores') +@patch('networkcommons.methods.run_corneto_carnival') +@patch('networkcommons.eval._metrics.get_metric_from_networks') +@patch('networkcommons.eval._metrics._log') +def test_get_offtarget_panacea_evaluation( + mock_log, + mock_get_metric_from_networks, + mock_run_corneto_carnival, + mock_add_pagerank_scores, + mock_run_all_paths, + mock_run_sign_consistency, + mock_run_shortest_paths, + mock_panacea_tables, + mock_network_from_df, + mock_get_omnipath, + mock_panacea_gold_standard, + mock_panacea_experiments +): + # Mocks + mock_panacea_experiments.return_value = pd.DataFrame({ + 'group': ['CellA_DrugA', 'CellA_DrugB'], + 'tf_scores': [True, True] + }) + + mock_panacea_gold_standard.return_value = pd.DataFrame({ + 'cmpd': ['DrugA', 'DrugA', 'DrugA'], + 'target': ['TargetA', 'TargetB', 'TargetC'], + 'rank': [1, 2, 3] + }) + + mock_network_df = pd.DataFrame({ + 'source': ['TargetA', 'Node1', 'TargetC', 'TargetD'], + 'target': ['Node1', 'TargetC', 'TargetC', 'Node2'], + 'interaction': [1, 1, 1, 1] + }) + mock_network_df = mock_network_df.astype({'source': str, 'target': str}) + mock_graph = nx.from_pandas_edgelist(mock_network_df, source='source', target='target', create_using=nx.DiGraph) + mock_network_from_df.return_value = mock_graph + + mock_panacea_tables.side_effect = lambda **kwargs: pd.DataFrame({ + 'items': ['Node1', 'Node2'], + 'act': [0.5, -0.3] + }) + + mock_run_shortest_paths.return_value = (MagicMock(), []) + mock_run_sign_consistency.return_value = (MagicMock(), []) + mock_run_all_paths.return_value = (MagicMock(), []) + mock_add_pagerank_scores.return_value = MagicMock() + mock_run_corneto_carnival.return_value = MagicMock() + + mock_get_metric_from_networks.return_value = pd.DataFrame({ + 'perc_offtargets': [10, 25], + 'network': ['shortest_path', 'all_paths'] + }) + + # Test + _metrics.get_offtarget_panacea_evaluation(cell='CellY') # No info + mock_log.assert_called_with("EVAL: no cell-drug combinations found with TF activity scores. Exiting...") + + result_df = _metrics.get_offtarget_panacea_evaluation(cell='CellA') # Only cell + + # Assertins + assert isinstance(result_df, pd.DataFrame) + assert 'perc_offtargets' in result_df.columns + assert len(result_df) > 0 + + result_df = _metrics.get_offtarget_panacea_evaluation(cell='CellA', drug='DrugA') # both + + assert isinstance(result_df, pd.DataFrame) + assert 'perc_offtargets' in result_df.columns + assert len(result_df) > 0 + + result_df = _metrics.get_offtarget_panacea_evaluation() # None + + assert isinstance(result_df, pd.DataFrame) + assert 'perc_offtargets' in result_df.columns + assert len(result_df) > 0 + + mock_panacea_experiments.assert_called() + mock_panacea_gold_standard.assert_called() + mock_get_omnipath.assert_called() + mock_network_from_df.assert_called() + mock_panacea_tables.assert_called() + mock_run_shortest_paths.assert_called() + mock_run_sign_consistency.assert_called() + mock_run_all_paths.assert_called() + mock_add_pagerank_scores.assert_called() + mock_run_corneto_carnival.assert_called() + mock_get_metric_from_networks.assert_called() + mock_log.assert_called_with("EVAL: finished offtarget recovery evaluation using PANACEA TF activity scores.") + + def test_all_nodes_in_ec50_dict(): network = nx.Graph([(1, 2), (2, 3)]) ec50_dict = {1: 5.0, 2: 10.0, 3: 15.0} diff --git a/tests/test_methods_causal.py b/tests/test_methods_causal.py index 8992575..3822269 100644 --- a/tests/test_methods_causal.py +++ b/tests/test_methods_causal.py @@ -4,6 +4,7 @@ from unittest.mock import patch, MagicMock import io import sys +import pytest @patch('networkcommons.methods._causal._log') # Mock the _log function def test_run_corneto_carnival(mock_log): @@ -92,4 +93,15 @@ def test_run_corneto_carnival(mock_log): assert any("CVXPY" in line for line in lines_in_output) assert any("Solver terminated with message: Optimization terminated successfully. (HiGHS Status 7: Optimal)" in line for line in lines_in_output) # Check that stdout has printed lines when verbose=True - assert len(captured_verbose_output) > 0 \ No newline at end of file + assert len(captured_verbose_output) > 0 + + # TODO: why does corneto raise a TypeError? Thought it was the empty network, but it's not + # with pytest.raises(TypeError): + # _causal.run_corneto_carnival( + # nx.DiGraph(), # Empty network + # source_dict, + # target_dict, + # betaWeight=0.1, + # solver='scipy', + # verbose=True + # ) diff --git a/tests/test_methods_graph.py b/tests/test_methods_graph.py index 6405cec..a033d9f 100644 --- a/tests/test_methods_graph.py +++ b/tests/test_methods_graph.py @@ -273,10 +273,19 @@ def test_run_all_paths_exceptions(): assert list(subnetwork.edges) == [] -def test_add_pagerank_scores(net2): +@patch('networkcommons.methods._graph._log') +def test_add_pagerank_scores(mock_log, net2): network, source_dict, target_dict = net2 + network_with_pagerank = _graph.add_pagerank_scores( + nx.DiGraph(), # empty + source_dict, + target_dict, + personalize_for='source', + ) + mock_log.assert_any_call('PPR: WARNING: Empty network, no scores added.') + network_with_pagerank = _graph.add_pagerank_scores( network, source_dict, diff --git a/tests/test_omics.py b/tests/test_omics.py index 8ffced9..3e9eefd 100644 --- a/tests/test_omics.py +++ b/tests/test_omics.py @@ -334,25 +334,52 @@ def test_decryptm_experiment_no_dataset(mock_decryptm_datasets): # FILE: omics/_panacea.py -@patch('networkcommons.data.omics._panacea._common._baseurl', return_value='http://example.com') -@patch('pandas.read_pickle') +@patch('urllib.request.urlopen') +@patch('pandas.read_csv') @patch('os.path.exists', return_value=False) -@patch('os.makedirs') @patch('pandas.DataFrame.to_pickle') -@patch('urllib.request.urlopen') -def test_panacea_experiments(mock_urlopen, mock_to_pickle, mock_makedirs, mock_path_exists, mock_read_pickle, mock_baseurl): - # Mock the HTTP response for the metadata file +def test_panacea_experiments(mock_to_pickle, mock_path_exists, mock_read_csv, mock_urlopen): + # Mock the metadata file (panacea__metadata.tsv) + mock_metadata = pd.DataFrame({ + 'group': ['ASPC_AEE788', 'ASPC_AFATINIB', 'DU145_CRIZOTINIB', 'ASPC_CEDIRANIB'], + 'sample_ID': ['ID1', 'ID2', 'ID3', 'ID4'] + }) + mock_read_csv.return_value = mock_metadata + + # Mock the HTML content returned by the remote server, + # with some lines containing the "__TF_scores.tsv" pattern + mock_html_content = """ + + + ASPC_AEE788__TF_scores.tsv
+ ASPC_AFATINIB__TF_scores.tsv
+ DU145_CRIZOTINIB__TF_scores.tsv
+ + + """ + + # Mock the HTTP response for the TF scores directory mock_response = MagicMock() - mock_response.read.return_value = b"group\tsample_ID\nA_B\tID1\nC_D\tID2" - mock_response.__enter__.return_value = mock_response + mock_response.read.return_value = mock_html_content.encode('utf-8') mock_urlopen.return_value = mock_response + # Call the function under test result_df = omics.panacea_experiments(update=True) + # Check that the result is a DataFrame assert isinstance(result_df, pd.DataFrame) + + # Check that the expected columns are in the DataFrame + assert 'group' in result_df.columns assert 'cell' in result_df.columns assert 'drug' in result_df.columns + assert 'tf_scores' in result_df.columns + + # Check that the 'tf_scores' column has the correct True/False values + expected_tf_scores = [True, True, True, False] # CEDIRANIB should not have TF scores + assert result_df['tf_scores'].tolist() == expected_tf_scores + # Verify that the function attempts to save the result as a pickle file mock_to_pickle.assert_called_once() @@ -583,6 +610,7 @@ def test_panacea_gold_standard_update(mock_urllib, mock_pandas_topickle, mock_ma mock_log.assert_any_call('DATA: found in cache, loading...') + # FILE: omics/_scperturb.py import pytest from unittest.mock import patch, MagicMock