Skip to main content
Glama
test_oncokb_client.py20 kB
"""Comprehensive unit tests for OncoKB client.""" import json import os from pathlib import Path from unittest.mock import patch import pytest from biomcp.http_client import RequestError from biomcp.variants.oncokb_client import ( ONCOKB_DEMO_URL, ONCOKB_PROD_URL, OncoKBClient, ) # Load mock responses from test data file def load_mock_responses() -> dict: """Load mock OncoKB responses from JSON file.""" test_data_dir = Path(__file__).parent.parent.parent / "data" mock_file = test_data_dir / "oncokb_mock_responses.json" with open(mock_file) as f: return json.load(f) @pytest.fixture def mock_responses(): """Fixture providing mock OncoKB responses.""" return load_mock_responses() class TestOncoKBClient: """Test suite for OncoKBClient functionality.""" def test_client_initialization_demo(self): """Test client initializes with demo URL when no token present.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() assert client.base_url == ONCOKB_DEMO_URL assert client.is_demo is True assert "Accept" in client.headers assert "Authorization" not in client.headers def test_client_initialization_prod(self): """Test client switches to production URL when token is set.""" with ( patch.dict(os.environ, {"ONCOKB_TOKEN": "test-token"}, clear=True), patch("biomcp.variants.oncokb_client.ONCOKB_TOKEN", "test-token"), ): client = OncoKBClient() assert client.base_url == ONCOKB_PROD_URL assert client.is_demo is False assert "Authorization" in client.headers assert client.headers["Authorization"] == "Bearer test-token" def test_token_detection_with_bearer_prefix(self): """Test that Bearer prefix is not duplicated if already present.""" with ( patch.dict( os.environ, {"ONCOKB_TOKEN": "Bearer existing-token"}, clear=True, ), patch( "biomcp.variants.oncokb_client.ONCOKB_TOKEN", "Bearer existing-token", ), ): client = OncoKBClient() assert client.headers["Authorization"] == "Bearer existing-token" assert not client.headers["Authorization"].startswith( "Bearer Bearer" ) def test_server_selection_demo_mode(self): """Test demo server selection when no token is configured.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() assert client.base_url == ONCOKB_DEMO_URL assert client.is_demo is True def test_server_selection_prod_mode(self): """Test production server selection when token is configured.""" token = "my-oncokb-token" # noqa: S105 - test token with ( patch.dict(os.environ, {"ONCOKB_TOKEN": token}, clear=True), patch("biomcp.variants.oncokb_client.ONCOKB_TOKEN", token), ): client = OncoKBClient() assert client.base_url == ONCOKB_PROD_URL assert client.is_demo is False @pytest.mark.asyncio async def test_get_curated_genes_success(self, mock_responses): """Test successful retrieval of curated genes list.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() mock_genes = mock_responses["allCuratedGenes"] with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (mock_genes, None) result, error = await client.get_curated_genes() # Verify result assert error is None assert result is not None assert isinstance(result, list) assert len(result) == 3 # Check BRAF entry braf = next( (g for g in result if g["hugoSymbol"] == "BRAF"), None ) assert braf is not None assert braf["entrezGeneId"] == 673 assert braf["geneType"] == "ONCOGENE" assert "BRAF" in braf["summary"] # Check TP53 entry tp53 = next( (g for g in result if g["hugoSymbol"] == "TP53"), None ) assert tp53 is not None assert tp53["geneType"] == "TSG" assert tp53["entrezGeneId"] == 7157 # Verify API was called correctly mock_request.assert_called_once() call_kwargs = mock_request.call_args[1] assert call_kwargs["domain"] == "oncokb" assert call_kwargs["endpoint_key"] == "oncokb_curated_genes" assert call_kwargs["cache_ttl"] == 86400 # 24 hours @pytest.mark.asyncio async def test_get_curated_genes_api_error(self): """Test handling of API errors in get_curated_genes.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() error_response = RequestError( code=500, message="Internal server error" ) with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (None, error_response) result, error = await client.get_curated_genes() assert result is None assert error is not None assert error.code == 500 assert "Internal server error" in error.message @pytest.mark.asyncio async def test_get_curated_genes_unexpected_format(self): """Test handling of unexpected response format.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() # Return dict instead of list with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = ({"error": "not a list"}, None) result, error = await client.get_curated_genes() assert result is None assert error is not None assert "Unexpected response format" in error.message @pytest.mark.asyncio async def test_get_curated_genes_exception_handling(self): """Test exception handling in get_curated_genes.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.side_effect = ValueError("Unexpected error") result, error = await client.get_curated_genes() assert result is None assert error is not None assert "Failed to fetch curated genes" in error.message @pytest.mark.asyncio async def test_get_gene_annotation_success(self, mock_responses): """Test successful retrieval of BRAF gene annotation.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() mock_annotation = mock_responses["genesByHugoSymbol"][0] with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (mock_annotation, None) result, error = await client.get_gene_annotation("BRAF") # Verify result assert error is None assert result is not None assert result["hugoSymbol"] == "BRAF" assert result["entrezGeneId"] == 673 assert result["geneType"] == "ONCOGENE" assert "geneAliases" in result assert "BRAF1" in result["geneAliases"] # Verify API was called correctly mock_request.assert_called_once() call_kwargs = mock_request.call_args[1] assert call_kwargs["domain"] == "oncokb" assert call_kwargs["endpoint_key"] == "oncokb_gene_annotation" assert call_kwargs["cache_ttl"] == 3600 # 1 hour @pytest.mark.asyncio async def test_get_gene_annotation_multiple_genes(self, mock_responses): """Test annotation retrieval for multiple different genes.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() # Test BRAF braf_annotation = mock_responses["genesByHugoSymbol"][0] with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (braf_annotation, None) result, error = await client.get_gene_annotation("BRAF") assert error is None assert result["hugoSymbol"] == "BRAF" # Test ROS1 ros1_annotation = mock_responses["genesByHugoSymbol"][1] with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (ros1_annotation, None) result, error = await client.get_gene_annotation("ROS1") assert error is None assert result["hugoSymbol"] == "ROS1" assert result["geneType"] == "ONCOGENE" # Test TP53 tp53_annotation = mock_responses["genesByHugoSymbol"][2] with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (tp53_annotation, None) result, error = await client.get_gene_annotation("TP53") assert error is None assert result["hugoSymbol"] == "TP53" assert result["geneType"] == "TSG" @pytest.mark.asyncio async def test_get_gene_annotation_api_error(self): """Test handling of API errors in get_gene_annotation.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() error_response = RequestError(code=404, message="Gene not found") with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (None, error_response) result, error = await client.get_gene_annotation("INVALID") assert result is None assert error is not None assert error.code == 404 @pytest.mark.asyncio async def test_get_gene_annotation_unexpected_format(self): """Test handling of unexpected response format in gene annotation.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() # Return list instead of dict with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (["not", "a", "dict"], None) result, error = await client.get_gene_annotation("BRAF") assert result is None assert error is not None assert "Unexpected response format" in error.message @pytest.mark.asyncio async def test_get_variant_annotation_success(self, mock_responses): """Test successful retrieval of BRAF V600E variant annotation.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() mock_annotation = mock_responses["variantAnnotation"][ "BRAF_V600E_melanoma" ] with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (mock_annotation, None) result, error = await client.get_variant_annotation( "BRAF", "V600E" ) # Verify result assert error is None assert result is not None # Check query details query = result["query"] assert query["hugoSymbol"] == "BRAF" assert query["alteration"] == "V600E" assert query["entrezGeneId"] == 673 # Check oncogenicity assert result["oncogenic"] == "Oncogenic" assert result["mutationEffect"]["knownEffect"] == ( "Gain-of-function" ) # Check evidence levels assert result["highestSensitiveLevel"] == "LEVEL_1" assert result["highestFdaLevel"] == "LEVEL_Fda2" assert result["hotspot"] is True # Check treatments treatments = result["treatments"] assert len(treatments) > 0 dabrafenib_treatment = treatments[0] assert dabrafenib_treatment["level"] == "LEVEL_1" assert len(dabrafenib_treatment["drugs"]) > 0 assert dabrafenib_treatment["drugs"][0]["drugName"] == ( "Dabrafenib" ) # Verify API was called correctly mock_request.assert_called_once() call_kwargs = mock_request.call_args[1] assert call_kwargs["domain"] == "oncokb" assert ( call_kwargs["endpoint_key"] == "oncokb_variant_annotation" ) assert call_kwargs["cache_ttl"] == 3600 # 1 hour @pytest.mark.asyncio async def test_get_variant_annotation_parameters(self): """Test that variant annotation sends correct parameters.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = ( {"query": {}, "oncogenic": "Oncogenic"}, None, ) await client.get_variant_annotation("BRAF", "V600E") # Verify parameters call_kwargs = mock_request.call_args[1] request_params = call_kwargs["request"] assert request_params["hugoSymbol"] == "BRAF" assert request_params["alteration"] == "V600E" assert "_headers" in request_params @pytest.mark.asyncio async def test_get_variant_annotation_api_error(self): """Test handling of API errors in get_variant_annotation.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() error_response = RequestError( code=404, message="Variant not found" ) with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = (None, error_response) result, error = await client.get_variant_annotation( "BRAF", "INVALID" ) assert result is None assert error is not None assert error.code == 404 @pytest.mark.asyncio async def test_get_variant_annotation_exception_handling(self): """Test exception handling in get_variant_annotation.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.side_effect = RuntimeError("Network error") result, error = await client.get_variant_annotation( "BRAF", "V600E" ) assert result is None assert error is not None assert "Failed to fetch variant annotation" in error.message def test_headers_json_formatting(self): """Test that headers are properly formatted as JSON.""" with ( patch.dict(os.environ, {"ONCOKB_TOKEN": "test-token"}, clear=True), patch("biomcp.variants.oncokb_client.ONCOKB_TOKEN", "test-token"), ): client = OncoKBClient() headers_json = client._headers_json() # Should be valid JSON parsed = json.loads(headers_json) assert "Accept" in parsed assert "Authorization" in parsed assert parsed["Authorization"] == "Bearer test-token" @pytest.mark.asyncio async def test_error_handling_graceful_degradation(self): """Test that all methods gracefully handle errors and return None.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() # Simulate complete API failure with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = ( None, RequestError(code=503, message="Service unavailable"), ) # All methods should return None without raising exceptions genes_result, genes_error = await client.get_curated_genes() assert genes_result is None assert genes_error is not None gene_result, gene_error = await client.get_gene_annotation( "BRAF" ) assert gene_result is None assert gene_error is not None ( variant_result, variant_error, ) = await client.get_variant_annotation("BRAF", "V600E") assert variant_result is None assert variant_error is not None @pytest.mark.asyncio async def test_caching_behavior(self): """Test that caching parameters are correctly set.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = ([], None) # Test curated genes - 24 hour cache await client.get_curated_genes() assert mock_request.call_args[1]["cache_ttl"] == 86400 # Test gene annotation - 1 hour cache mock_request.return_value = ({}, None) await client.get_gene_annotation("BRAF") assert mock_request.call_args[1]["cache_ttl"] == 3600 # Test variant annotation - 1 hour cache await client.get_variant_annotation("BRAF", "V600E") assert mock_request.call_args[1]["cache_ttl"] == 3600 @pytest.mark.asyncio async def test_retry_enabled_for_all_methods(self): """Test that retry is enabled for all API methods.""" with patch.dict(os.environ, {}, clear=True): client = OncoKBClient() with patch( "biomcp.variants.oncokb_client.request_api" ) as mock_request: mock_request.return_value = ([], None) await client.get_curated_genes() assert mock_request.call_args[1]["enable_retry"] is True mock_request.return_value = ({}, None) await client.get_gene_annotation("BRAF") assert mock_request.call_args[1]["enable_retry"] is True await client.get_variant_annotation("BRAF", "V600E") assert mock_request.call_args[1]["enable_retry"] is True

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/genomoncology/biomcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server