Files
doczyai-pipelines/src/tests/test_code_funcs.py
T
2026-02-03 01:38:49 -05:00

438 lines
18 KiB
Python

import json
import re
import unittest
from unittest.mock import MagicMock, patch
import pandas as pd
import src.codes.code_funcs as code_funcs
from src.constants.constants import Constants
from src.constants.delimiters import Delimiter
class TestCodeFuncs(unittest.TestCase):
def setUp(self):
"""Set up test fixtures before each test method."""
# Create a mock Constants object
self.constants = MagicMock(spec=Constants)
# Configure mock constants
self.constants.SYNONYM_MAP = {
"DME": "DURABLE MEDICAL EQUIPMENT",
"SNF": "SKILLED NURSING FACILITY",
}
self.constants.REMOVAL_LIST = ["INPATIENT", "OUTPATIENT", "SERVICES"]
self.constants.STOP_WORD_LIST = ["THE", "AND", "OF"]
self.constants.DO_NOT_RUN = ["BY REPORT", "STOP-LOSS"]
# Setup mappings
self.constants.CPT_LEVEL1_MAPPING = {"12345": "Test Procedure"}
self.constants.HCPCS_LEVEL1_MAPPING = {"J0001": "Test Drug"}
self.constants.REV_LEVEL1_MAPPING = {"123": "Test Revenue"}
self.constants.CPT_LEVEL2_MAPPING = {"67890": "Advanced Procedure"}
self.constants.HCPCS_LEVEL2_MAPPING = {"J0002": "Advanced Drug"}
self.constants.REV_MAPPING = {"456": "Advanced Revenue"}
self.constants.BILL_TYPE_MAPPING = {"11X": "Inpatient Hospital"}
self.constants.BILL_TYPE_REVERSE_MAPPING = {"Inpatient Hospital": "11X"}
# Mock the embedding model and function
mock_embedding = MagicMock()
mock_embedding.index.search.return_value = ([0.9], [[0]])
mock_embedding.choices = ["Test Description"]
self.constants.get_embedding.return_value = mock_embedding
self.constants.EMBEDDING_MODEL = MagicMock()
mock_encoded = MagicMock()
mock_encoded.astype.return_value = "mock_vector"
self.constants.EMBEDDING_MODEL.encode.return_value = mock_encoded
def test_clean_service(self):
"""Test the clean_service function with various inputs."""
# Test empty input
self.assertEqual(code_funcs.clean_service("", self.constants), "")
self.assertEqual(code_funcs.clean_service(None, self.constants), "")
# Test synonym mapping
self.assertEqual(
code_funcs.clean_service("DME", self.constants), "DURABLE MEDICAL EQUIPMENT"
)
# Test removal list
self.assertEqual(
code_funcs.clean_service("INPATIENT SERVICES", self.constants), ""
)
# Test stop words only
self.assertEqual(code_funcs.clean_service("THE AND OF", self.constants), "")
# Test unlisted service
self.assertEqual(
code_funcs.clean_service("UNLISTED PROCEDURE", self.constants), "UNLISTED"
)
# Test normal case
self.assertEqual(
code_funcs.clean_service("CT SCAN SNF", self.constants),
"CT SCAN SKILLED NURSING FACILITY",
)
@patch("src.utils.llm_utils.invoke_claude")
@patch("src.utils.string_utils.universal_json_load")
def test_code_explicit(self, mock_json_load, mock_invoke_claude):
"""Test the code_explicit function for extracting explicit codes."""
# Setup mocks
mock_invoke_claude.return_value = '{"PROCEDURE_CD": "12345"}'
mock_json_load.return_value = {"PROCEDURE_CD": "12345"}
# Test successful extraction
result = code_funcs.code_explicit(
"TEST SERVICE", "TEST_METHODOLOGY", "test.pdf"
)
self.assertEqual(result, {"PROCEDURE_CD": "12345"})
mock_invoke_claude.assert_called_once()
@patch("src.utils.llm_utils.invoke_claude")
@patch("src.utils.string_utils.universal_json_load")
def test_code_category(self, mock_json_load, mock_invoke_claude):
"""Test the code_category function for detecting code categories."""
# Setup mocks
mock_invoke_claude.return_value = '["Drug A", "Drug B"]'
mock_json_load.return_value = ["Drug A", "Drug B"]
# Setup test data
proc_category = ["Category: J"]
hcpcs_mapping = {"J0001": "Drug A", "J0002": "Drug B", "K0001": "Other Drug"}
# Test category matching
result = code_funcs.code_category(
"J CODES", proc_category, hcpcs_mapping, "test.pdf"
)
# Verify the results - code_category returns lists (JSON conversion happens in extract_codes_from_service)
self.assertEqual(result["PROCEDURE_CD"], ["J0001", "J0002"])
self.assertEqual(result["PROCEDURE_CD_DESC"], ["Drug A", "Drug B"])
self.assertIn("J0001", result["PROCEDURE_CD"])
self.assertIn("J0002", result["PROCEDURE_CD"])
self.assertIn("Drug A", result["PROCEDURE_CD_DESC"])
self.assertIn("Drug B", result["PROCEDURE_CD_DESC"])
@patch("src.utils.llm_utils.invoke_claude")
@patch("src.utils.string_utils.extract_text_from_delimiters")
def test_code_implicit_special(self, mock_extract, mock_invoke_claude):
"""Test the code_implicit_special function for special case handling."""
# Setup mocks
mock_invoke_claude.return_value = "mock_response"
mock_extract.return_value = "Drugs"
# Test drug category - returns a list
result = code_funcs.code_implicit_special("DRUG SERVICE", "test.pdf")
self.assertEqual(result["PROCEDURE_CD"], ["J0000-J9999"])
self.assertEqual(result["PROCEDURE_CD_DESC"], "Drugs")
# Test no match
mock_extract.return_value = ""
result = code_funcs.code_implicit_special("OTHER SERVICE", "test.pdf")
self.assertEqual(result, {})
def test_get_embedding_levels(self):
"""Test the get_embedding_levels function for determining embedding levels."""
# Test level 1 with both codes enabled
implicit_run_dict = {"PROCEDURE_CD": True, "REVENUE_CD": True}
embedding_levels, cpt_mapping, hcpcs_mapping, rev_mapping = (
code_funcs.get_embedding_levels(1, implicit_run_dict, self.constants)
)
self.assertIn("cpt_level1", embedding_levels)
self.assertIn("hcpcs_level1", embedding_levels)
self.assertIn("rev_level1", embedding_levels)
self.assertEqual(cpt_mapping, self.constants.CPT_LEVEL1_MAPPING)
# Test level 2 with only procedure code enabled
implicit_run_dict = {"PROCEDURE_CD": True, "REVENUE_CD": False}
embedding_levels, cpt_mapping, hcpcs_mapping, rev_mapping = (
code_funcs.get_embedding_levels(2, implicit_run_dict, self.constants)
)
self.assertIn("cpt_level2", embedding_levels)
self.assertIn("hcpcs_level2", embedding_levels)
self.assertNotIn("rev", embedding_levels)
self.assertEqual(cpt_mapping, self.constants.CPT_LEVEL2_MAPPING)
def test_get_match_list(self):
"""Test the get_match_list function for retrieving matches from embeddings."""
# Configure mock for proper array-like return values
mock_embedding = MagicMock()
# Search returns a tuple of (scores, indices) where each is a 2D array
mock_embedding.index.search.return_value = (
# Scores array - 2D array with shape [1, top_k]
[[0.9]],
# Indices array - 2D array with shape [1, top_k]
[[0]],
)
mock_embedding.choices = ["Test Description"]
# Replace the mock embedding in constants for this test
self.constants.get_embedding.return_value = mock_embedding
# Test with constants mock configured in setUp
match_list, highest_similarity = code_funcs.get_match_list(
"TEST SERVICE", ["cpt_level1"], self.constants, 1
)
# Verify results
self.assertEqual(match_list, ["Test Description"])
self.assertEqual(highest_similarity, 0.9)
# Verify the appropriate method calls
self.constants.EMBEDDING_MODEL.encode.assert_called_once()
self.constants.get_embedding.assert_called_once_with("cpt_level1")
@patch("src.utils.llm_utils.invoke_claude")
@patch("src.utils.string_utils.universal_json_load")
@patch("src.codes.code_funcs.get_match_list")
def test_code_implicit_rag(
self, mock_get_match_list, mock_json_load, mock_invoke_claude
):
"""Test the code_implicit_rag function for RAG-based code extraction."""
# Mock get_match_list to return properly formatted results
mock_get_match_list.return_value = (["Test Procedure"], 0.9)
# Setup mocks for Claude responses
mock_invoke_claude.return_value = '["Test Procedure"]'
mock_json_load.return_value = ["Test Procedure"]
# Test with successful match
implicit_run_dict = {"PROCEDURE_CD": True, "REVENUE_CD": True}
# Patch the get_embedding_levels function to return predictable values
with patch(
"src.codes.code_funcs.get_embedding_levels"
) as mock_get_embedding_levels:
# Configure mock to return appropriate values for embedding levels
mock_get_embedding_levels.return_value = (
["cpt_level1"],
{"12345": "Test Procedure"},
{},
{},
)
# Call the function
result = code_funcs.code_implicit_rag(
"TEST SERVICE", implicit_run_dict, "test.pdf", self.constants
)
# Verify results - code_implicit_rag returns lists
self.assertEqual(result["PROCEDURE_CD"], ["12345"])
self.assertEqual(result["PROCEDURE_CD_DESC"], ["Test Procedure"])
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Level 1")
# Test exception handling
mock_json_load.side_effect = Exception("Test error")
result = code_funcs.code_implicit_rag(
"TEST SERVICE", implicit_run_dict, "test.pdf", self.constants
)
self.assertIn("CODE_METHODOLOGY", result)
@patch("src.utils.llm_utils.invoke_claude")
@patch("src.utils.string_utils.extract_text_from_delimiters")
def test_code_last_check(self, mock_extract, mock_invoke_claude):
"""Test the code_last_check function for categorizing non-matched services."""
# Setup mocks
mock_invoke_claude.return_value = "mock_response"
# Test specific case
mock_extract.return_value = "Specific"
result = code_funcs.code_last_check("SPECIAL SERVICE", "test.pdf")
self.assertEqual(result, "Specific")
# Test generic case
mock_extract.return_value = "Generic"
result = code_funcs.code_last_check("GENERIC SERVICE", "test.pdf")
self.assertEqual(result, "Generic")
@patch("src.utils.llm_utils.invoke_claude")
@patch("src.utils.string_utils.universal_json_load")
def test_fill_bill_type(self, mock_json_load, mock_invoke_claude):
"""Test the fill_bill_type function for populating bill type information."""
# Setup mocks
mock_invoke_claude.return_value = '["Inpatient Hospital"]'
mock_json_load.return_value = ["Inpatient Hospital"]
# Test with valid bill type
answer_dict = {}
result = code_funcs.fill_bill_type(
"INPATIENT SERVICE",
answer_dict,
self.constants.BILL_TYPE_MAPPING,
self.constants.BILL_TYPE_REVERSE_MAPPING,
)
self.assertEqual(result["BILL_TYPE_CD"], "11X")
self.assertEqual(result["BILL_TYPE_CD_DESC"], "Inpatient Hospital")
# Test with empty response
mock_json_load.return_value = []
answer_dict = {"SERVICE_TERM": "Test"}
result = code_funcs.fill_bill_type(
"OTHER SERVICE",
answer_dict,
self.constants.BILL_TYPE_MAPPING,
self.constants.BILL_TYPE_REVERSE_MAPPING,
)
self.assertEqual(result, {"SERVICE_TERM": "Test"})
def test_get_implicit_runs(self):
"""Test the get_implicit_runs function for determining which code runs to execute."""
# Test hospital claim type
result = code_funcs.get_implicit_runs({"AARETE_DERIVED_CLAIM_TYPE_CD": "H"})
self.assertTrue(result["REVENUE_CD"])
# Test medical claim type with bill type
result = code_funcs.get_implicit_runs(
{"AARETE_DERIVED_CLAIM_TYPE_CD": "M", "BILL_TYPE_CD_DESC": "Outpatient"}
)
self.assertTrue(result["REVENUE_CD"])
self.assertTrue(result["PROCEDURE_CD"])
# Test inpatient bill type
result = code_funcs.get_implicit_runs(
{
"AARETE_DERIVED_CLAIM_TYPE_CD": "M",
"BILL_TYPE_CD_DESC": "Inpatient Hospital",
}
)
self.assertTrue(result["REVENUE_CD"])
self.assertFalse(result["PROCEDURE_CD"])
@patch("src.codes.code_funcs.clean_service")
@patch("src.codes.code_funcs.fill_bill_type")
@patch("src.codes.code_funcs.get_implicit_runs")
@patch("src.codes.code_funcs.code_explicit")
@patch("src.codes.code_funcs.code_category")
@patch("src.codes.code_funcs.code_implicit_special")
@patch("src.codes.code_funcs.code_implicit_rag")
@patch("src.codes.code_funcs.code_last_check")
def test_extract_codes_from_service(
self,
mock_last_check,
mock_implicit_rag,
mock_implicit_special,
mock_category,
mock_explicit,
mock_implicit_runs,
mock_fill_bill_type,
mock_clean_service,
):
"""Test the extract_codes_from_service function end-to-end."""
# Setup mocks
mock_clean_service.return_value = "CLEAN SERVICE"
mock_fill_bill_type.return_value = {
"SERVICE_TERM": "TEST SERVICE",
"BILL_TYPE_CD_DESC": "Outpatient",
}
mock_implicit_runs.return_value = {"PROCEDURE_CD": True, "REVENUE_CD": True}
# Test explicit code match - extract_codes_from_service normalizes to JSON list format
mock_explicit.return_value = {"PROCEDURE_CD": "12345"}
result = code_funcs.extract_codes_from_service(
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
)
self.assertEqual(result["PROCEDURE_CD"], '["12345"]')
self.assertEqual(result["CODE_METHODOLOGY"], "Explicit")
# Test unlisted service
mock_clean_service.return_value = "UNLISTED"
result = code_funcs.extract_codes_from_service(
{"SERVICE_TERM": "UNLISTED SERVICE"}, self.constants
)
self.assertEqual(result["PROCEDURE_CD_DESC"], "UNLISTED")
# Test category match
mock_clean_service.return_value = "CLEAN SERVICE"
mock_explicit.return_value = {"PROCEDURE_CD": ["Category: J"]}
mock_category.return_value = {
"PROCEDURE_CD": ["J0001"],
"PROCEDURE_CD_DESC": ["Drug A"],
}
result = code_funcs.extract_codes_from_service(
{"SERVICE_TERM": "J CODES"}, self.constants
)
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Letter Category")
# Test special case match
mock_explicit.return_value = {}
mock_implicit_special.return_value = {
"PROCEDURE_CD": "J0000-J9999",
"PROCEDURE_CD_DESC": "Drugs",
}
result = code_funcs.extract_codes_from_service(
{"SERVICE_TERM": "DRUG SERVICE"}, self.constants
)
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Special Case")
# Test RAG match
mock_implicit_special.return_value = {}
mock_implicit_rag.return_value = {
"PROCEDURE_CD": "12345",
"CODE_METHODOLOGY": "Implicit - Level 1",
}
result = code_funcs.extract_codes_from_service(
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
)
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Level 1")
# Test no match - specific
mock_implicit_rag.return_value = {}
mock_last_check.return_value = "Specific"
result = code_funcs.extract_codes_from_service(
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
)
self.assertEqual(result["CODE_METHODOLOGY"], "Specific - No Match")
# Test no match - generic
mock_last_check.return_value = "Generic"
result = code_funcs.extract_codes_from_service(
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
)
self.assertEqual(result["CODE_METHODOLOGY"], "Generic - No Match")
@patch("src.codes.code_funcs.fill_claim_type")
@patch("src.codes.code_funcs.extract_codes_from_service")
def test_code_breakout(self, mock_extract_codes, mock_fill_claim_type):
"""Test the code_breakout function for processing DataFrame records."""
# Create test data
test_df = pd.DataFrame(
[{"SERVICE_TERM": "SERVICE A"}, {"SERVICE_TERM": "SERVICE B"}]
)
# Setup mocks
mock_fill_claim_type.return_value = [
{"SERVICE_TERM": "SERVICE A"},
{"SERVICE_TERM": "SERVICE B"},
]
mock_extract_codes.side_effect = [
{"SERVICE_TERM": "SERVICE A", "PROCEDURE_CD": "12345"},
{"SERVICE_TERM": "SERVICE B", "PROCEDURE_CD": "67890"},
]
# Test function
result_df = code_funcs.code_breakout(test_df, self.constants)
# Verify results - code_breakout normalizes to JSON list format
self.assertIsInstance(result_df, pd.DataFrame)
self.assertEqual(len(result_df), 2)
self.assertEqual(result_df.iloc[0]["PROCEDURE_CD"], '["12345"]')
self.assertEqual(result_df.iloc[1]["PROCEDURE_CD"], '["67890"]')
# Verify mock calls
mock_fill_claim_type.assert_called_once()
self.assertEqual(mock_extract_codes.call_count, 2)
if __name__ == "__main__":
unittest.main()