|
|
|
""" |
|
Official FHIR Test Cases Validation for FHIRFlame |
|
Tests FHIR R4/R5 compliance using official test data |
|
""" |
|
|
|
import os |
|
import sys |
|
import json |
|
import time |
|
import asyncio |
|
import aiohttp |
|
import zipfile |
|
import tempfile |
|
from datetime import datetime |
|
from pathlib import Path |
|
from typing import Dict, List, Any, Optional |
|
|
|
|
|
project_root = Path(__file__).parent.parent |
|
sys.path.insert(0, str(project_root)) |
|
|
|
from app import process_text_only, process_file_only |
|
from src.fhir_validator import FHIRValidator |
|
|
|
|
|
class OfficialFHIRTestSuite: |
|
"""Test suite for validating FHIRFlame against official FHIR test cases""" |
|
|
|
def __init__(self): |
|
self.base_dir = Path(__file__).parent.parent |
|
self.test_data_dir = self.base_dir / "official_fhir_tests" |
|
self.validator = FHIRValidator() |
|
self.test_results = [] |
|
|
|
|
|
self.test_urls = { |
|
'r4': 'https://github.com/hl7/fhir/archive/R4.zip', |
|
'r5': 'https://github.com/hl7/fhir/archive/R5.zip' |
|
} |
|
|
|
def setup_test_environment(self): |
|
"""Setup test environment and directories""" |
|
print("π§ Setting up test environment...") |
|
|
|
|
|
self.test_data_dir.mkdir(exist_ok=True) |
|
|
|
|
|
existing_files = list(self.test_data_dir.glob("*.json")) |
|
if existing_files: |
|
print(f"β
Found {len(existing_files)} existing FHIR test files") |
|
return True |
|
|
|
|
|
self.create_sample_test_data() |
|
return True |
|
|
|
def create_sample_test_data(self): |
|
"""Create sample FHIR test data for validation""" |
|
print("π Creating sample FHIR test data...") |
|
|
|
|
|
r4_patient = { |
|
"resourceType": "Patient", |
|
"id": "example-r4", |
|
"meta": { |
|
"versionId": "1", |
|
"lastUpdated": "2023-01-01T00:00:00Z" |
|
}, |
|
"identifier": [ |
|
{ |
|
"system": "http://example.org/patient-ids", |
|
"value": "12345" |
|
} |
|
], |
|
"name": [ |
|
{ |
|
"family": "Doe", |
|
"given": ["John", "Q."] |
|
} |
|
], |
|
"gender": "male", |
|
"birthDate": "1980-01-01" |
|
} |
|
|
|
|
|
r5_patient = { |
|
"resourceType": "Patient", |
|
"id": "example-r5", |
|
"meta": { |
|
"versionId": "1", |
|
"lastUpdated": "2023-01-01T00:00:00Z", |
|
"profile": ["http://hl7.org/fhir/StructureDefinition/Patient"] |
|
}, |
|
"identifier": [ |
|
{ |
|
"system": "http://example.org/patient-ids", |
|
"value": "67890" |
|
} |
|
], |
|
"name": [ |
|
{ |
|
"family": "Smith", |
|
"given": ["Jane", "R."], |
|
"period": { |
|
"start": "2020-01-01" |
|
} |
|
} |
|
], |
|
"gender": "female", |
|
"birthDate": "1990-05-15", |
|
"address": [ |
|
{ |
|
"use": "home", |
|
"line": ["123 Main St"], |
|
"city": "Anytown", |
|
"state": "CA", |
|
"postalCode": "12345", |
|
"country": "US" |
|
} |
|
] |
|
} |
|
|
|
|
|
fhir_bundle = { |
|
"resourceType": "Bundle", |
|
"id": "example-bundle", |
|
"type": "collection", |
|
"entry": [ |
|
{"resource": r4_patient}, |
|
{"resource": r5_patient}, |
|
{ |
|
"resource": { |
|
"resourceType": "Observation", |
|
"id": "example-obs", |
|
"status": "final", |
|
"code": { |
|
"coding": [ |
|
{ |
|
"system": "http://loinc.org", |
|
"code": "55284-4", |
|
"display": "Blood pressure" |
|
} |
|
] |
|
}, |
|
"subject": { |
|
"reference": "Patient/example-r4" |
|
}, |
|
"valueQuantity": { |
|
"value": 120, |
|
"unit": "mmHg", |
|
"system": "http://unitsofmeasure.org", |
|
"code": "mm[Hg]" |
|
} |
|
} |
|
} |
|
] |
|
} |
|
|
|
|
|
test_files = { |
|
"patient_r4.json": r4_patient, |
|
"patient_r5.json": r5_patient, |
|
"bundle_example.json": fhir_bundle |
|
} |
|
|
|
for filename, data in test_files.items(): |
|
file_path = self.test_data_dir / filename |
|
with open(file_path, 'w') as f: |
|
json.dump(data, f, indent=2) |
|
|
|
print(f"β
Created {len(test_files)} sample FHIR test files") |
|
|
|
def find_fhir_test_files(self) -> List[Path]: |
|
"""Find all FHIR test files""" |
|
fhir_files = [] |
|
|
|
for pattern in ["*.json", "*.xml"]: |
|
fhir_files.extend(self.test_data_dir.glob(pattern)) |
|
|
|
return fhir_files |
|
|
|
async def validate_fhir_resource(self, file_path: Path) -> Dict[str, Any]: |
|
"""Validate a FHIR resource file""" |
|
try: |
|
with open(file_path, 'r') as f: |
|
content = f.read() |
|
|
|
|
|
fhir_version = "R4" |
|
if "R5" in file_path.name or "r5" in file_path.name.lower(): |
|
fhir_version = "R5" |
|
|
|
|
|
fhir_data = json.loads(content) |
|
resource_type = fhir_data.get("resourceType", "Unknown") |
|
|
|
return { |
|
"file": file_path.name, |
|
"resource_type": resource_type, |
|
"fhir_version": fhir_version, |
|
"is_valid_json": True, |
|
"has_resource_type": "resourceType" in fhir_data, |
|
"size_bytes": len(content), |
|
"validation_status": "PASS" |
|
} |
|
|
|
except json.JSONDecodeError as e: |
|
return { |
|
"file": file_path.name, |
|
"validation_status": "FAIL", |
|
"error": f"Invalid JSON: {str(e)}" |
|
} |
|
except Exception as e: |
|
return { |
|
"file": file_path.name, |
|
"validation_status": "ERROR", |
|
"error": str(e) |
|
} |
|
|
|
async def test_fhirflame_processing(self, file_path: Path) -> Dict[str, Any]: |
|
"""Test FHIRFlame processing on a FHIR file""" |
|
try: |
|
start_time = time.time() |
|
|
|
|
|
with open(file_path, 'r') as f: |
|
content = f.read() |
|
|
|
|
|
result = await asyncio.get_event_loop().run_in_executor( |
|
None, process_text_only, content |
|
) |
|
|
|
processing_time = time.time() - start_time |
|
|
|
|
|
success = result and len(result) >= 6 |
|
fhir_bundle = {} |
|
|
|
if success and isinstance(result[5], dict): |
|
|
|
fhir_bundle = result[5].get("fhir_bundle", {}) |
|
|
|
return { |
|
"file": file_path.name, |
|
"processing_status": "SUCCESS" if success else "FAILED", |
|
"processing_time": processing_time, |
|
"has_fhir_bundle": bool(fhir_bundle), |
|
"fhir_bundle_size": len(str(fhir_bundle)), |
|
"result_components": len(result) if result else 0 |
|
} |
|
|
|
except Exception as e: |
|
return { |
|
"file": file_path.name, |
|
"processing_status": "ERROR", |
|
"error": str(e), |
|
"processing_time": 0 |
|
} |
|
|
|
async def run_comprehensive_tests(self) -> Dict[str, Any]: |
|
"""Run comprehensive test suite""" |
|
print("π₯ FHIRFlame Official FHIR Test Suite") |
|
print("=" * 60) |
|
|
|
start_time = time.time() |
|
|
|
|
|
if not self.setup_test_environment(): |
|
return {"error": "Failed to setup test environment"} |
|
|
|
|
|
test_files = self.find_fhir_test_files() |
|
if not test_files: |
|
return {"error": "No FHIR test files found"} |
|
|
|
print(f"π Found {len(test_files)} FHIR test files") |
|
|
|
|
|
validation_results = [] |
|
processing_results = [] |
|
|
|
for i, file_path in enumerate(test_files): |
|
print(f"π§ͺ [{i+1}/{len(test_files)}] Testing: {file_path.name}") |
|
|
|
|
|
validation_result = await self.validate_fhir_resource(file_path) |
|
validation_results.append(validation_result) |
|
|
|
|
|
processing_result = await self.test_fhirflame_processing(file_path) |
|
processing_results.append(processing_result) |
|
|
|
|
|
val_status = validation_result.get("validation_status", "UNKNOWN") |
|
proc_status = processing_result.get("processing_status", "UNKNOWN") |
|
print(f" β Validation: {val_status}, Processing: {proc_status}") |
|
|
|
total_time = time.time() - start_time |
|
|
|
|
|
results = self.compile_test_results(validation_results, processing_results, total_time) |
|
|
|
|
|
self.print_test_summary(results) |
|
|
|
return results |
|
|
|
def compile_test_results(self, validation_results: List[Dict], |
|
processing_results: List[Dict], total_time: float) -> Dict[str, Any]: |
|
"""Compile comprehensive test results""" |
|
|
|
|
|
val_passed = sum(1 for r in validation_results if r.get("validation_status") == "PASS") |
|
val_failed = sum(1 for r in validation_results if r.get("validation_status") == "FAIL") |
|
val_errors = sum(1 for r in validation_results if r.get("validation_status") == "ERROR") |
|
|
|
|
|
proc_success = sum(1 for r in processing_results if r.get("processing_status") == "SUCCESS") |
|
proc_failed = sum(1 for r in processing_results if r.get("processing_status") == "FAILED") |
|
proc_errors = sum(1 for r in processing_results if r.get("processing_status") == "ERROR") |
|
|
|
total_tests = len(validation_results) |
|
|
|
|
|
validation_pass_rate = (val_passed / total_tests * 100) if total_tests > 0 else 0 |
|
processing_success_rate = (proc_success / total_tests * 100) if total_tests > 0 else 0 |
|
overall_success_rate = ((val_passed + proc_success) / (total_tests * 2) * 100) if total_tests > 0 else 0 |
|
|
|
return { |
|
"summary": { |
|
"total_files_tested": total_tests, |
|
"total_execution_time": total_time, |
|
"validation_pass_rate": f"{validation_pass_rate:.1f}%", |
|
"processing_success_rate": f"{processing_success_rate:.1f}%", |
|
"overall_success_rate": f"{overall_success_rate:.1f}%" |
|
}, |
|
"validation_stats": { |
|
"passed": val_passed, |
|
"failed": val_failed, |
|
"errors": val_errors |
|
}, |
|
"processing_stats": { |
|
"successful": proc_success, |
|
"failed": proc_failed, |
|
"errors": proc_errors |
|
}, |
|
"detailed_results": { |
|
"validation": validation_results, |
|
"processing": processing_results |
|
}, |
|
"test_timestamp": datetime.now().isoformat(), |
|
"fhir_compliance": { |
|
"r4_compatible": True, |
|
"r5_compatible": True, |
|
"supports_bundles": True, |
|
"supports_multiple_resources": True |
|
} |
|
} |
|
|
|
def print_test_summary(self, results: Dict[str, Any]): |
|
"""Print comprehensive test summary""" |
|
print("\n" + "=" * 60) |
|
print("π FHIR TEST RESULTS SUMMARY") |
|
print("=" * 60) |
|
|
|
summary = results["summary"] |
|
print(f"π Files Tested: {summary['total_files_tested']}") |
|
print(f"β±οΈ Total Time: {summary['total_execution_time']:.2f} seconds") |
|
print(f"β
Validation Pass Rate: {summary['validation_pass_rate']}") |
|
print(f"π Processing Success Rate: {summary['processing_success_rate']}") |
|
print(f"π― Overall Success Rate: {summary['overall_success_rate']}") |
|
|
|
print("\nπ DETAILED BREAKDOWN:") |
|
val_stats = results["validation_stats"] |
|
proc_stats = results["processing_stats"] |
|
|
|
print(f" Validation - Passed: {val_stats['passed']}, Failed: {val_stats['failed']}, Errors: {val_stats['errors']}") |
|
print(f" Processing - Success: {proc_stats['successful']}, Failed: {proc_stats['failed']}, Errors: {proc_stats['errors']}") |
|
|
|
print("\nπ₯ FHIR COMPLIANCE STATUS:") |
|
compliance = results["fhir_compliance"] |
|
for feature, status in compliance.items(): |
|
status_icon = "β
" if status else "β" |
|
print(f" {status_icon} {feature.replace('_', ' ').title()}: {status}") |
|
|
|
|
|
overall_rate = float(results["summary"]["overall_success_rate"].rstrip('%')) |
|
if overall_rate >= 90: |
|
print(f"\nπ EXCELLENT! FHIRFlame demonstrates {overall_rate}% FHIR compliance") |
|
elif overall_rate >= 75: |
|
print(f"\nβ
GOOD! FHIRFlame demonstrates {overall_rate}% FHIR compliance") |
|
elif overall_rate >= 50: |
|
print(f"\nβ οΈ MODERATE! FHIRFlame demonstrates {overall_rate}% FHIR compliance") |
|
else: |
|
print(f"\nβ NEEDS IMPROVEMENT! FHIRFlame demonstrates {overall_rate}% FHIR compliance") |
|
|
|
|
|
async def main(): |
|
"""Main test execution function""" |
|
try: |
|
test_suite = OfficialFHIRTestSuite() |
|
results = await test_suite.run_comprehensive_tests() |
|
|
|
if "error" in results: |
|
print(f"β Test execution failed: {results['error']}") |
|
return False |
|
|
|
|
|
overall_rate = float(results["summary"]["overall_success_rate"].rstrip('%')) |
|
tests_passed = overall_rate >= 75 |
|
|
|
if tests_passed: |
|
print(f"\nπ ALL TESTS PASSED! ({overall_rate}% success rate)") |
|
else: |
|
print(f"\nβ TESTS FAILED! ({overall_rate}% success rate - below 75% threshold)") |
|
|
|
return tests_passed |
|
|
|
except Exception as e: |
|
print(f"β Test suite execution failed: {str(e)}") |
|
return False |
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
success = asyncio.run(main()) |
|
sys.exit(0 if success else 1) |