|
""" |
|
TDD Tests for FhirFlame MCP Server |
|
Write tests FIRST, then implement to make them pass |
|
""" |
|
|
|
import pytest |
|
import asyncio |
|
import json |
|
import time |
|
from unittest.mock import Mock, patch, AsyncMock |
|
from typing import Dict, Any |
|
|
|
|
|
try: |
|
from src.fhirflame_mcp_server import FhirFlameMCPServer |
|
from src.codellama_processor import CodeLlamaProcessor |
|
except ImportError: |
|
|
|
FhirFlameMCPServer = None |
|
CodeLlamaProcessor = None |
|
|
|
|
|
class TestFhirFlameMCPServerTDD: |
|
"""TDD tests for FhirFlame MCP Server - RED phase""" |
|
|
|
def setup_method(self): |
|
"""Setup for each test""" |
|
self.sample_medical_text = """ |
|
DISCHARGE SUMMARY |
|
|
|
Patient: John Doe |
|
DOB: 1980-01-01 |
|
MRN: 123456789 |
|
|
|
DIAGNOSIS: Essential Hypertension |
|
|
|
VITAL SIGNS: |
|
- Blood Pressure: 140/90 mmHg |
|
- Heart Rate: 72 bpm |
|
- Temperature: 98.6°F |
|
|
|
MEDICATIONS: |
|
- Lisinopril 10mg daily |
|
- Metoprolol 25mg twice daily |
|
""" |
|
|
|
self.expected_fhir_bundle = { |
|
"resourceType": "Bundle", |
|
"type": "document", |
|
"entry": [ |
|
{ |
|
"resource": { |
|
"resourceType": "Patient", |
|
"name": [{"given": ["John"], "family": "Doe"}], |
|
"birthDate": "1980-01-01" |
|
} |
|
} |
|
] |
|
} |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.asyncio |
|
async def test_mcp_server_initialization(self): |
|
"""Test: MCP server initializes correctly""" |
|
|
|
|
|
server = FhirFlameMCPServer() |
|
|
|
|
|
assert server is not None |
|
assert hasattr(server, 'tools') |
|
assert len(server.tools) == 2 |
|
assert 'process_medical_document' in server.tools |
|
assert 'validate_fhir_bundle' in server.tools |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.asyncio |
|
async def test_process_medical_document_tool_exists(self): |
|
"""Test: process_medical_document tool is properly registered""" |
|
|
|
server = FhirFlameMCPServer() |
|
|
|
|
|
tool = server.get_tool('process_medical_document') |
|
|
|
|
|
assert tool is not None |
|
assert tool['name'] == 'process_medical_document' |
|
assert 'description' in tool |
|
assert 'parameters' in tool |
|
assert tool['parameters']['document_content']['required'] is True |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.asyncio |
|
async def test_validate_fhir_bundle_tool_exists(self): |
|
"""Test: validate_fhir_bundle tool is properly registered""" |
|
|
|
server = FhirFlameMCPServer() |
|
|
|
|
|
tool = server.get_tool('validate_fhir_bundle') |
|
|
|
|
|
assert tool is not None |
|
assert tool['name'] == 'validate_fhir_bundle' |
|
assert 'description' in tool |
|
assert 'parameters' in tool |
|
assert tool['parameters']['fhir_bundle']['required'] is True |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.asyncio |
|
async def test_process_medical_document_success(self): |
|
"""Test: process_medical_document returns valid FHIR bundle""" |
|
|
|
server = FhirFlameMCPServer() |
|
document_content = "base64_encoded_medical_document" |
|
document_type = "discharge_summary" |
|
|
|
|
|
result = await server.call_tool('process_medical_document', { |
|
'document_content': document_content, |
|
'document_type': document_type |
|
}) |
|
|
|
|
|
assert result['success'] is True |
|
assert 'fhir_bundle' in result |
|
assert result['fhir_bundle']['resourceType'] == 'Bundle' |
|
assert len(result['fhir_bundle']['entry']) > 0 |
|
assert result['processing_metadata']['model_used'] == 'codellama:13b-instruct' |
|
assert result['processing_metadata']['gpu_used'] == 'RTX_4090' |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.asyncio |
|
async def test_process_medical_document_extracts_entities(self): |
|
"""Test: Medical entities are correctly extracted""" |
|
|
|
server = FhirFlameMCPServer() |
|
document_content = self.sample_medical_text |
|
|
|
|
|
result = await server.call_tool('process_medical_document', { |
|
'document_content': document_content, |
|
'document_type': 'discharge_summary' |
|
}) |
|
|
|
|
|
assert result['success'] is True |
|
assert result['extraction_results']['entities_found'] > 0 |
|
assert result['extraction_results']['quality_score'] > 0.6 |
|
|
|
|
|
fhir_bundle = result['fhir_bundle'] |
|
patient_found = any( |
|
entry['resource']['resourceType'] == 'Patient' |
|
for entry in fhir_bundle['entry'] |
|
) |
|
assert patient_found is True |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.asyncio |
|
async def test_validate_fhir_bundle_success(self): |
|
"""Test: FHIR validation with healthcare grade standards""" |
|
|
|
server = FhirFlameMCPServer() |
|
fhir_bundle = self.expected_fhir_bundle |
|
|
|
|
|
result = await server.call_tool('validate_fhir_bundle', { |
|
'fhir_bundle': fhir_bundle, |
|
'validation_level': 'healthcare_grade' |
|
}) |
|
|
|
|
|
assert result['success'] is True |
|
assert result['validation_results']['is_valid'] is True |
|
assert result['validation_results']['compliance_score'] > 0.9 |
|
assert result['compliance_summary']['fhir_r4_compliant'] is True |
|
assert result['compliance_summary']['hipaa_ready'] is True |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.asyncio |
|
async def test_mcp_error_handling(self): |
|
"""Test: MCP server handles errors gracefully""" |
|
|
|
server = FhirFlameMCPServer() |
|
|
|
|
|
result = await server.call_tool('process_medical_document', { |
|
'document_content': '', |
|
'document_type': 'discharge_summary' |
|
}) |
|
|
|
|
|
assert result['success'] is False |
|
assert 'error' in result |
|
assert 'Empty document' in result['error'] |
|
|
|
@pytest.mark.mcp |
|
@pytest.mark.integration |
|
@pytest.mark.asyncio |
|
async def test_complete_mcp_workflow(self): |
|
"""Test: Complete MCP workflow from document to validated FHIR""" |
|
|
|
server = FhirFlameMCPServer() |
|
test_document = self.sample_medical_text |
|
|
|
|
|
|
|
process_result = await server.call_tool('process_medical_document', { |
|
'document_content': test_document, |
|
'document_type': 'discharge_summary' |
|
}) |
|
assert process_result['success'] is True |
|
|
|
|
|
validate_result = await server.call_tool('validate_fhir_bundle', { |
|
'fhir_bundle': process_result['fhir_bundle'], |
|
'validation_level': 'healthcare_grade' |
|
}) |
|
assert validate_result['success'] is True |
|
|
|
|
|
assert validate_result['validation_results']['is_valid'] is True |
|
assert validate_result['compliance_summary']['hipaa_ready'] is True |
|
|
|
|
|
class TestCodeLlamaProcessorTDD: |
|
"""TDD tests for CodeLlama processor - RED phase""" |
|
|
|
def setup_method(self): |
|
"""Setup for each test""" |
|
self.sample_text = "Patient: John Doe, DOB: 1980-01-01, Diagnosis: Hypertension" |
|
|
|
@pytest.mark.codellama |
|
@pytest.mark.gpu |
|
def test_codellama_processor_initialization(self): |
|
"""Test: CodeLlama processor initializes correctly""" |
|
|
|
|
|
processor = CodeLlamaProcessor() |
|
|
|
|
|
assert processor is not None |
|
assert processor.model_name == 'codellama:13b-instruct' |
|
assert processor.gpu_available is True |
|
assert processor.vram_allocated == '12GB' |
|
|
|
@pytest.mark.codellama |
|
@pytest.mark.gpu |
|
@pytest.mark.asyncio |
|
async def test_codellama_medical_text_processing(self): |
|
"""Test: CodeLlama processes medical text correctly""" |
|
|
|
processor = CodeLlamaProcessor() |
|
medical_text = self.sample_text |
|
|
|
|
|
result = await processor.process_medical_text_codellama(medical_text) |
|
|
|
|
|
assert result['success'] is True |
|
assert result['model_used'] == 'codellama:13b-instruct' |
|
assert result['gpu_used'] == 'RTX_4090' |
|
assert result['vram_used'] == '12GB' |
|
assert 'extracted_data' in result |
|
assert result['processing_time'] < 5.0 |
|
|
|
@pytest.mark.codellama |
|
@pytest.mark.gpu |
|
@pytest.mark.asyncio |
|
async def test_codellama_json_output_format(self): |
|
"""Test: CodeLlama returns proper JSON format for FHIR""" |
|
|
|
processor = CodeLlamaProcessor() |
|
medical_text = self.sample_text |
|
|
|
|
|
result = await processor.process_medical_text_codellama(medical_text) |
|
|
|
|
|
assert result['success'] is True |
|
extracted_data = result['extracted_data'] |
|
|
|
|
|
try: |
|
parsed_data = json.loads(extracted_data) |
|
assert 'patient' in parsed_data |
|
assert 'conditions' in parsed_data |
|
assert 'confidence_score' in parsed_data |
|
except json.JSONDecodeError: |
|
pytest.fail("CodeLlama did not return valid JSON") |
|
|
|
@pytest.mark.codellama |
|
@pytest.mark.gpu |
|
def test_codellama_gpu_memory_efficiency(self): |
|
"""Test: CodeLlama uses GPU memory efficiently""" |
|
|
|
processor = CodeLlamaProcessor() |
|
|
|
|
|
memory_info = processor.get_memory_info() |
|
|
|
|
|
assert memory_info['total_vram'] == '24GB' |
|
assert memory_info['allocated_vram'] == '12GB' |
|
assert memory_info['available_vram'] == '12GB' |
|
assert memory_info['memory_efficient'] is True |
|
|
|
|
|
class TestPerformanceBenchmarksTDD: |
|
"""TDD performance tests for RTX 4090 optimization""" |
|
|
|
@pytest.mark.benchmark |
|
@pytest.mark.gpu |
|
@pytest.mark.slow |
|
def test_document_processing_speed_benchmark(self): |
|
"""Benchmark: Document processing speed on RTX 4090""" |
|
try: |
|
import pytest_benchmark |
|
except ImportError: |
|
pytest.skip("pytest-benchmark not available") |
|
|
|
|
|
processor = CodeLlamaProcessor() |
|
sample_doc = "Patient: Jane Smith, DOB: 1975-05-15, Chief Complaint: Chest pain" |
|
|
|
|
|
start_time = time.time() |
|
result = asyncio.run(processor.process_medical_text_codellama(sample_doc)) |
|
processing_time = time.time() - start_time |
|
|
|
|
|
assert result['success'] is True |
|
assert processing_time < 10.0 |
|
print(f"🕒 Processing completed in {processing_time:.2f} seconds") |
|
assert result['gpu_used'] == 'RTX_4090' |
|
|
|
@pytest.mark.benchmark |
|
@pytest.mark.gpu |
|
def test_concurrent_processing_capability(self): |
|
"""Test: RTX 4090 can handle concurrent medical document processing""" |
|
|
|
processor = CodeLlamaProcessor() |
|
documents = [ |
|
"Patient A: Hypertension diagnosis", |
|
"Patient B: Diabetes management", |
|
"Patient C: Pneumonia treatment" |
|
] |
|
|
|
|
|
async def process_concurrent(): |
|
tasks = [ |
|
processor.process_medical_text_codellama(doc) |
|
for doc in documents |
|
] |
|
return await asyncio.gather(*tasks) |
|
|
|
results = asyncio.run(process_concurrent()) |
|
|
|
|
|
assert len(results) == 3 |
|
for result in results: |
|
assert result['success'] is True |
|
assert result['gpu_used'] == 'RTX_4090' |
|
|
|
|
|
@pytest.mark.skip(reason="Will fail until implementation - TDD RED phase") |
|
class TestTDDRedPhaseRunner: |
|
"""This class ensures tests fail initially as expected in TDD""" |
|
|
|
def test_all_tests_should_fail_initially(self): |
|
"""Meta-test: Confirms we're in TDD RED phase""" |
|
|
|
|
|
pass |