diff --git a/tests/conftest.py b/tests/conftest.py
index 6d3c780b..c3fa7142 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,5 +1,6 @@
from pathlib import Path
import pytest
+import copy
import yaml
import tempfile
@@ -24,34 +25,75 @@
from fhir.resources.documentreference import DocumentReference, DocumentReferenceContent
-# TODO: Tidy up fixtures
-
-
@pytest.fixture
def cda_adapter():
+ """Provides a reusable instance of the CdaAdapter.
+
+ Use this fixture in tests that require parsing or formatting of CDA documents.
+
+ Example:
+ def test_parsing(cda_adapter):
+ request = CdaRequest(document="...")
+ document = cda_adapter.parse(request)
+ assert document is not None
+
+ Returns:
+ healthchain.io.adapters.CdaAdapter: An instance of the CDA adapter.
+ """
from healthchain.io import CdaAdapter
return CdaAdapter()
-# FHIR resource fixtures
+# ########################################
+# ######## FHIR Resource Fixtures ########
+# ########################################
@pytest.fixture
def empty_bundle():
- """Create an empty bundle for testing."""
+ """Provides an empty FHIR Bundle resource.
+
+ Use this fixture for tests that need to build a Bundle from scratch by adding
+ resources dynamically.
+
+ See Also:
+ `test_bundle`: A pre-populated bundle with mixed resources.
+
+ Returns:
+ fhir.resources.bundle.Bundle: An empty FHIR Bundle of type 'collection'.
+ """
return create_bundle()
+
@pytest.fixture
def test_condition():
- """Create a test condition."""
+ """Provides a minimal, generic FHIR Condition resource.
+
+ This fixture is useful for testing components that process a single Condition
+ resource without needing specific clinical details.
+
+ Returns:
+ fhir.resources.condition.Condition: A minimal FHIR Condition resource with a
+ subject, code, and display text.
+
+ See Also:
+ `test_condition_list`: For testing with multiple conditions.
+ """
return create_condition(subject="Patient/123", code="123", display="Test Condition")
@pytest.fixture
def test_condition_list():
- """Create a list of test conditions."""
+ """Provides a list containing two FHIR Condition resources.
+
+ Use this fixture for testing components that need to handle lists or collections
+ of Condition resources.
+
+ Returns:
+ list[fhir.resources.condition.Condition]: A list of two distinct Condition resources.
+ """
return [
create_condition(subject="Patient/123", code="123", display="Test Condition"),
create_condition(subject="Patient/123", code="456", display="Test Condition 2"),
@@ -60,7 +102,18 @@ def test_condition_list():
@pytest.fixture
def test_medication():
- """Create a test medication statement."""
+ """Provides a minimal, generic FHIR MedicationStatement resource.
+
+ This fixture is useful for testing components that process a single
+ MedicationStatement without needing specific dosage or timing details.
+
+ Returns:
+ fhir.resources.medicationstatement.MedicationStatement: A minimal FHIR
+ MedicationStatement resource with a subject, code, and display text.
+
+ See Also:
+ `test_medication_with_dosage`: For a more detailed medication resource.
+ """
return create_medication_statement(
subject="Patient/123", code="456", display="Test Medication"
)
@@ -68,7 +121,18 @@ def test_medication():
@pytest.fixture
def test_allergy():
- """Create a test allergy intolerance."""
+ """Provides a minimal, generic FHIR AllergyIntolerance resource.
+
+ This fixture is useful for testing components that process a single
+ AllergyIntolerance resource without needing reaction details.
+
+ Returns:
+ fhir.resources.allergyintolerance.AllergyIntolerance: A minimal FHIR
+ AllergyIntolerance resource with a patient reference, code, and display text.
+
+ See Also:
+ `test_allergy_with_reaction`: For an allergy with reaction details.
+ """
return create_allergy_intolerance(
patient="Patient/123", code="789", display="Test Allergy"
)
@@ -76,6 +140,16 @@ def test_allergy():
@pytest.fixture
def test_allergy_with_reaction(test_allergy):
+ """Provides an AllergyIntolerance resource with type and reaction details.
+
+ Extends the `test_allergy` fixture by adding `type` and `reaction` fields,
+ including manifestation and severity. Use this for testing logic that
+ processes detailed allergy information.
+
+ Returns:
+ fhir.resources.allergyintolerance.AllergyIntolerance: A detailed FHIR AllergyIntolerance resource.
+ """
+ test_allergy = copy.deepcopy(test_allergy)
test_allergy.type = create_single_codeable_concept(
code="ABC", display="Test Allergy", system="http://snomed.info/sct"
)
@@ -91,6 +165,16 @@ def test_allergy_with_reaction(test_allergy):
@pytest.fixture
def test_medication_with_dosage(test_medication):
+ """Provides a MedicationStatement with dosage and effective period.
+
+ Extends the `test_medication` fixture by adding `dosage` (including route and
+ timing) and `effectivePeriod`. Use this for testing logic that processes
+ medication administration details.
+
+ Returns:
+ fhir.resources.medicationstatement.MedicationStatement: A detailed FHIR MedicationStatement resource.
+ """
+ test_medication = copy.deepcopy(test_medication)
test_medication.dosage = [
{
"doseAndRate": [{"doseQuantity": {"value": 500, "unit": "mg"}}],
@@ -108,7 +192,15 @@ def test_medication_with_dosage(test_medication):
@pytest.fixture
def doc_ref_with_content():
- """Create a DocumentReference with single text content."""
+ """Provides a DocumentReference with a single, plain-text attachment.
+
+ The attachment data "Test document content" is base64 encoded. Use this
+ fixture to test functions that read or process content from a
+ DocumentReference.
+
+ Returns:
+ fhir.resources.documentreference.DocumentReference: A FHIR DocumentReference with one content attachment.
+ """
return create_document_reference(
data="Test document content",
content_type="text/plain",
@@ -118,7 +210,15 @@ def doc_ref_with_content():
@pytest.fixture
def doc_ref_with_multiple_content():
- """Create a DocumentReference with multiple text content."""
+ """Provides a DocumentReference with two separate plain-text attachments.
+
+ Contains two attachments with "First content" and "Second content". Use this
+ to test logic that handles multiple `content` entries in a single
+ DocumentReference.
+
+ Returns:
+ fhir.resources.documentreference.DocumentReference: A FHIR DocumentReference with two content attachments.
+ """
doc_ref = create_document_reference(
data="First content",
content_type="text/plain",
@@ -136,7 +236,14 @@ def doc_ref_with_multiple_content():
@pytest.fixture
def doc_ref_with_cda_xml():
- """Create a DocumentReference with CDA XML content."""
+ """Provides a DocumentReference with XML content.
+
+ The attachment has a `contentType` of "text/xml" and data of "".
+ Useful for simulating a DocumentReference that points to a CDA document.
+
+ Returns:
+ fhir.resources.documentreference.DocumentReference: A DocumentReference with an XML attachment.
+ """
return create_document_reference(
data="",
content_type="text/xml",
@@ -145,18 +252,36 @@ def doc_ref_with_cda_xml():
@pytest.fixture
def doc_ref_without_content():
- """Create a DocumentReference without content for error testing."""
+ """Provides an invalid DocumentReference with no attachment data.
+
+ The `attachment` is missing the required `data` or `url` field. This is
+ intended for testing error handling and validation logic.
+
+ Returns:
+ fhir.resources.documentreference.DocumentReference: An incomplete DocumentReference resource.
+ """
+ from fhir.resources.attachment import Attachment
return DocumentReference(
status="current",
- content=[
- {"attachment": {"contentType": "text/plain"}}
- ], # Missing required data
+ content=[DocumentReferenceContent(attachment=Attachment(contentType="text/plain"))], # Missing required data or url
)
@pytest.fixture
def test_bundle():
- """Create a test bundle."""
+ """Provides a FHIR Bundle containing one Condition and one MedicationStatement.
+
+ Use this fixture for testing components that process bundles with mixed
+ resource types.
+
+ Example:
+ def test_bundle_processor(test_bundle):
+ conditions = get_resources(test_bundle, "Condition")
+ assert len(conditions) == 1
+
+ Returns:
+ fhir.resources.bundle.Bundle: A FHIR Bundle with two resource entries.
+ """
bundle = create_bundle()
bundle.entry = [
{
@@ -175,7 +300,23 @@ def test_bundle():
@pytest.fixture
def test_document():
- """Create a test document with FHIR resources."""
+ """Provides a `Document` container with pre-populated FHIR lists.
+
+ This fixture creates a `healthchain.io.Document` instance and populates its
+ `fhir` attribute with a problem list, medication list, and allergy list,
+ each containing one resource.
+
+ Example:
+ def test_process_document(test_document):
+ # test_document.fhir.problem_list is already populated
+ assert len(test_document.fhir.problem_list) == 1
+ # Run a pipeline or function on the document
+ result = process_clinical_data(test_document)
+ assert result is not None
+
+ Returns:
+ healthchain.io.containers.Document: A pre-populated Document container for pipeline testing.
+ """
doc = Document(data="Test note")
doc.fhir.bundle = create_bundle()
@@ -200,11 +341,29 @@ def test_document():
@pytest.fixture
def test_empty_document():
+ """Provides an empty `Document` container with simple text data.
+
+ Returns:
+ healthchain.io.containers.Document: A Document container with `data` set and no other annotations.
+ """
return Document(data="This is a sample text for testing.")
@pytest.fixture
def valid_prefetch_data():
+ """Provides a `Prefetch` model object for CDS Hooks testing.
+
+ Contains a single prefetch key "document" with a DocumentReference resource.
+ Use this for testing services that consume CDS Hooks prefetch data.
+
+ Example:
+ def test_prefetch_handler(valid_prefetch_data):
+ request = CDSRequest(prefetch=valid_prefetch_data.prefetch)
+ # ... test logic
+
+ Returns:
+ healthchain.models.hooks.prefetch.Prefetch: A Pydantic model representing valid prefetch data.
+ """
return Prefetch(
prefetch={
"document": create_document_reference(
@@ -214,11 +373,21 @@ def valid_prefetch_data():
)
-# Test request and response fixtures
+# #################################################
+# ######## Request and Response Fixtures ########
+# #################################################
@pytest.fixture
def test_cds_request():
+ """Provides a sample `CDSRequest` object.
+
+ Represents a typical CDS Hooks request for the `patient-view` hook, including
+ context and a minimal Patient resource in the prefetch data.
+
+ Returns:
+ healthchain.models.requests.cdsrequest.CDSRequest: A Pydantic model representing a CDS Hooks request.
+ """
cds_dict = {
"hook": "patient-view",
"hookInstance": "29e93987-c345-4cb7-9a92-b5136289c2a4",
@@ -238,6 +407,11 @@ def test_cds_request():
@pytest.fixture
def test_cds_response_single_card():
+ """Provides a `CDSResponse` object with a single informational card.
+
+ Returns:
+ healthchain.models.responses.cdsresponse.CDSResponse: A response containing one `Card` in its `cards` list.
+ """
return CDSResponse(
cards=[
Card(
@@ -252,11 +426,23 @@ def test_cds_response_single_card():
@pytest.fixture
def test_cds_response_empty():
+ """Provides an empty `CDSResponse` object with no cards.
+
+ Returns:
+ healthchain.models.responses.cdsresponse.CDSResponse: A response with an empty `cards` list.
+ """
return CDSResponse(cards=[])
@pytest.fixture
def test_cds_response_multiple_cards():
+ """Provides a `CDSResponse` object with multiple cards.
+
+ Contains two cards with different indicators ('info' and 'warning').
+
+ Returns:
+ healthchain.models.responses.cdsresponse.CDSResponse: A response containing two `Card` objects.
+ """
return CDSResponse(
cards=[
Card(
@@ -273,7 +459,15 @@ def test_cds_response_multiple_cards():
@pytest.fixture
def test_cda_request():
- with open("./tests/data/test_cda.xml", "r") as file:
+ """Provides a `CdaRequest` object with CDA XML content from a file.
+
+ Reads the content from `./tests/data/test_cda.xml`.
+
+ Returns:
+ healthchain.models.requests.cdarequest.CdaRequest: A request object containing a CDA XML string.
+ """
+ cda_path = Path(__file__).parent / "data" / "test_cda.xml"
+ with open(cda_path, "r") as file:
test_cda = file.read()
return CdaRequest(document=test_cda)
@@ -281,6 +475,11 @@ def test_cda_request():
@pytest.fixture
def test_cda_response():
+ """Provides a sample `CdaResponse` object with a mock CDA document.
+
+ Returns:
+ healthchain.models.responses.cdaresponse.CdaResponse: A response object with a mock XML document string.
+ """
return CdaResponse(
document="Mock CDA Response Document",
error=None,
@@ -289,6 +488,13 @@ def test_cda_response():
@pytest.fixture
def test_cda_response_with_error():
+ """Provides a `CdaResponse` object representing an error condition.
+
+ The `document` is empty and the `error` field is populated.
+
+ Returns:
+ healthchain.models.responses.cdaresponse.CdaResponse: A response object indicating an error occurred.
+ """
return CdaResponse(
document="", error="An error occurred while processing the CDA document"
)
@@ -296,27 +502,38 @@ def test_cda_response_with_error():
@pytest.fixture
def test_soap_request():
- with open("./tests/data/test_soap_request.xml", "r") as file:
- test_soap = file.read()
-
- return CdaRequest(document=test_soap)
+ """Provides a `CdaRequest` with a sample SOAP XML request from a file.
+ Reads the content from `./tests/data/test_soap_request.xml`.
-@pytest.fixture
-def real_config_dir():
- """Use the actual config directory for testing"""
- project_root = Path(__file__).parent.parent
- config_dir = project_root / "configs"
-
- if not config_dir.exists():
- pytest.skip("Actual config directory not found. Skipping ConfigManager tests.")
+ Returns:
+ healthchain.models.requests.cdarequest.CdaRequest: A request object containing a SOAP XML string.
+ """
+ soap_path = Path(__file__).parent / "data" / "test_soap_request.xml"
+ with open(soap_path, "r") as file:
+ test_soap = file.read()
- return config_dir
+ return CdaRequest(document=test_soap)
@pytest.fixture
def config_fixtures():
- """Create temporary directory with config files for testing both ConfigManager and InteropConfigManager."""
+ """Creates a temporary directory with a complete set of config files.
+
+ This fixture simulates the entire `configs` directory structure, including
+ defaults, environments, module-specific configs (interop), and mappings.
+ It is suitable for testing both `ConfigManager` and `InteropConfigManager`.
+
+ Example:
+ def test_config_loading(config_fixtures):
+ # config_fixtures is a Path to a temporary directory
+ manager = ConfigManager(config_dir=config_fixtures)
+ manager.load()
+ assert manager.get_config_value("debug") is True
+
+ Yields:
+ pathlib.Path: The path to the temporary configuration directory.
+ """
with tempfile.TemporaryDirectory() as temp_dir:
config_dir = Path(temp_dir)
@@ -565,3 +782,22 @@ def config_fixtures():
yaml.dump(mapping_content, f)
yield config_dir
+
+
+@pytest.fixture
+def real_config_dir():
+ """Provides the path to the actual `configs` directory in the project.
+
+ This fixture allows tests to run against the real, checked-in configuration
+ files. It will skip tests if the directory is not found.
+
+ Returns:
+ pathlib.Path: The path to the project's `configs` directory.
+ """
+ project_root = Path(__file__).parent.parent
+ config_dir = project_root / "configs"
+
+ if not config_dir.exists():
+ pytest.skip("Actual config directory not found. Skipping ConfigManager tests.")
+
+ return config_dir