2025-11-03 20:45:58 +00:00
import unittest
import json
2025-01-09 17:59:28 +00:00
import pandas as pd
2025-11-03 20:45:58 +00:00
import numpy as np
from src . utils import string_utils
2026-01-26 16:52:55 +00:00
from src . constants . delimiters import Delimiter
2025-07-07 21:42:49 +00:00
2025-11-03 20:45:58 +00:00
class TestStringUtils ( unittest . TestCase ) :
def test_extract_text_from_delimiters_pipe ( self ) :
s = " foo |bar| baz |qux| "
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . extract_text_from_delimiters ( s , Delimiter . PIPE ) , " qux "
)
self . assertEqual (
string_utils . extract_text_from_delimiters ( s , Delimiter . PIPE , 0 ) , " bar "
)
self . assertEqual (
string_utils . extract_text_from_delimiters ( " no delimiters " , Delimiter . PIPE ) ,
" N/A " ,
)
2025-11-03 20:45:58 +00:00
with self . assertRaises ( TypeError ) :
string_utils . extract_text_from_delimiters ( 123 , Delimiter . PIPE )
with self . assertRaises ( TypeError ) :
string_utils . extract_text_from_delimiters ( s , " notadelimiter " )
def test_extract_text_from_delimiters_backtick ( self ) :
s = " foo `bar` baz `qux` "
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . extract_text_from_delimiters ( s , Delimiter . BACKTICK ) , " qux "
)
2025-11-03 20:45:58 +00:00
def test_extract_text_from_delimiters_triple_backtick ( self ) :
s = " foo ```bar``` baz ```qux``` "
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . extract_text_from_delimiters ( s , Delimiter . TRIPLE_BACKTICK ) ,
" qux " ,
)
2025-11-03 20:45:58 +00:00
def test_page_key_sort ( self ) :
keys = [ " 10 " , " 2 " , " A " , " 1 " , " B " , " 1.5 " ]
sorted_keys = sorted ( keys , key = string_utils . page_key_sort )
self . assertEqual ( sorted_keys , [ " 1 " , " 1.5 " , " 2 " , " 10 " , " A " , " B " ] )
def test_json_parsing_search ( self ) :
s = ' { " FIELD1 " : " value1 " , " FIELD2 " : " value2 " } '
fields = [ " FIELD1 " , " FIELD2 " ]
result = string_utils . json_parsing_search ( s , fields )
self . assertEqual ( result , { " FIELD1 " : " value1 " , " FIELD2 " : " value2 " } )
def test_universal_json_load_valid ( self ) :
# Simple dict
s = ' { " a " : 1, " b " : 2} '
self . assertEqual ( string_utils . universal_json_load ( s ) , { " a " : 1 , " b " : 2 } )
# Simple list
2026-01-26 16:52:55 +00:00
s = " [1, 2, 3] "
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . universal_json_load ( s ) , [ 1 , 2 , 3 ] )
# Nested dict
s = ' { " a " : { " b " : [1, 2, { " c " : " d " }]}} '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " a " : { " b " : [ 1 , 2 , { " c " : " d " } ] } }
)
2025-11-03 20:45:58 +00:00
# List of dicts
s = ' [ { " x " : 1}, { " y " : 2}] '
self . assertEqual ( string_utils . universal_json_load ( s ) , [ { " x " : 1 } , { " y " : 2 } ] )
# Dict of lists
s = ' { " a " : [1, 2], " b " : [3, 4]} '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " a " : [ 1 , 2 ] , " b " : [ 3 , 4 ] }
)
2025-11-03 20:45:58 +00:00
# List of strings
s = ' [ " foo " , " bar " , " baz " ] '
self . assertEqual ( string_utils . universal_json_load ( s ) , [ " foo " , " bar " , " baz " ] )
# JSON with text before/after
s = ' start { " field " : " value " } end '
self . assertEqual ( string_utils . universal_json_load ( s ) , { " field " : " value " } )
2026-01-26 16:52:55 +00:00
s = " before [1, 2, 3] after "
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . universal_json_load ( s ) , [ 1 , 2 , 3 ] )
# Multiple objects, last wins
s = ' first { " a " : 1} middle { " b " : 2} last { " c " : 3} '
self . assertEqual ( string_utils . universal_json_load ( s ) , { " c " : 3 } )
# Multiple arrays, last wins
2026-01-26 16:52:55 +00:00
s = " first [1] middle [2] last [3] "
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . universal_json_load ( s ) , [ 3 ] )
# Nested JSON with text 1
s = ' junk { " outer " : { " inner " : [1, 2, { " deep " : true}]}} more junk '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) ,
{ " outer " : { " inner " : [ 1 , 2 , { " deep " : True } ] } } ,
)
2025-11-03 20:45:58 +00:00
# Nested JSON with text 2
s = ' junk [ { " dict1_key " : " dict1_val " }, { " dict2_key " : " dict2_val " }] more junk '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) ,
[ { " dict1_key " : " dict1_val " } , { " dict2_key " : " dict2_val " } ] ,
)
2025-11-03 20:45:58 +00:00
# List of dicts
s = ' junk```json[ { " a1 " : " value_a1 " , " b1 " : " value_b1 " }, { " a2 " : " value_a2 " , " b2 " : " value_b2 " }]``` '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) ,
[
{ " a1 " : " value_a1 " , " b1 " : " value_b1 " } ,
{ " a2 " : " value_a2 " , " b2 " : " value_b2 " } ,
] ,
)
2025-11-03 20:45:58 +00:00
# Boolean and null values
s = ' { " ok " : true, " val " : null} '
2026-01-26 16:52:55 +00:00
self . assertEqual ( string_utils . universal_json_load ( s ) , { " ok " : True , " val " : None } )
2025-11-03 20:45:58 +00:00
def test_universal_json_load_invalid ( self ) :
# No valid JSON
2026-01-26 16:52:55 +00:00
s = " no json here "
2025-11-03 20:45:58 +00:00
with self . assertRaises ( ValueError ) :
string_utils . universal_json_load ( s )
# Broken JSON
s = ' { broken: " json " } '
with self . assertRaises ( ValueError ) :
string_utils . universal_json_load ( s )
# Non-string input
with self . assertRaises ( TypeError ) :
string_utils . universal_json_load ( 123 )
# Unbalanced brackets
s = ' { " a " : [1, 2, 3} '
with self . assertRaises ( ValueError ) :
string_utils . universal_json_load ( s )
# Empty string
2026-01-26 16:52:55 +00:00
s = " "
2025-11-03 20:45:58 +00:00
with self . assertRaises ( ValueError ) :
string_utils . universal_json_load ( s )
def test_universal_json_load_edge_cases ( self ) :
# Empty object
2026-01-26 16:52:55 +00:00
s = " text {} more text "
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . universal_json_load ( s ) , { } )
# Empty array
2026-01-26 16:52:55 +00:00
s = " text [] more text "
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . universal_json_load ( s ) , [ ] )
# Large object
large = { f " k { i } " : i for i in range ( 100 ) }
2026-01-26 16:52:55 +00:00
s = f " before { json . dumps ( large ) } after "
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . universal_json_load ( s ) , large )
def test_contains_reimbursement ( self ) :
s = " 90 % r eimbursement of billed charges "
self . assertTrue ( string_utils . contains_reimbursement ( s , method = " keyword " ) )
s = " Payment of $500 per visit "
self . assertTrue ( string_utils . contains_reimbursement ( s , method = " regex " ) )
s = " No reimbursement here "
self . assertTrue ( string_utils . contains_reimbursement ( s , method = " keyword " ) )
with self . assertRaises ( ValueError ) :
string_utils . contains_reimbursement ( s , method = " invalid " )
def test_is_empty_string ( self ) :
self . assertTrue ( string_utils . is_empty ( " " ) )
self . assertTrue ( string_utils . is_empty ( " N/A " ) )
self . assertFalse ( string_utils . is_empty ( " valid " ) )
def test_is_empty_list ( self ) :
self . assertTrue ( string_utils . is_empty ( [ ] ) )
self . assertTrue ( string_utils . is_empty ( [ None , " " , " N/A " ] ) )
self . assertFalse ( string_utils . is_empty ( [ " valid " ] ) )
def test_is_empty_pandas_series ( self ) :
s = pd . Series ( [ " valid " , " " , None , " N/A " ] )
mask = string_utils . is_empty ( s , pd_mask = True )
self . assertTrue ( mask . iloc [ 1 ] )
self . assertTrue ( mask . iloc [ 2 ] )
self . assertTrue ( mask . iloc [ 3 ] )
self . assertFalse ( mask . iloc [ 0 ] )
2026-01-26 16:52:55 +00:00
self . assertFalse (
string_utils . is_empty ( pd . Series ( [ " valid " , None ] ) , pd_mask = False )
)
self . assertTrue (
string_utils . is_empty ( pd . Series ( [ None , " " , " N/A " ] ) , pd_mask = False )
)
2025-11-03 20:45:58 +00:00
def test_datetime_str ( self ) :
result = string_utils . datetime_str ( )
self . assertTrue ( result . startswith ( " [ " ) )
self . assertTrue ( result . endswith ( " ] " ) )
def test_extract_signature_page ( self ) :
2026-01-26 16:52:55 +00:00
text_dict = {
" 1 " : " Contract page 1 " ,
" 2 " : " This page has 2 signatures at the bottom " ,
" 3 " : " Final page " ,
}
2025-11-03 20:45:58 +00:00
result = string_utils . extract_signature_page ( text_dict , " test.pdf " )
self . assertIn ( " 2 " , result )
self . assertEqual ( result [ " 2 " ] , " This page has 2 signatures at the bottom " )
2026-01-26 16:52:55 +00:00
text_dict = {
" 1 " : " Contract page 1 " ,
" 2 " : " In Witness Whereof " ,
" 3 " : " Final page " ,
}
2025-11-03 20:45:58 +00:00
result = string_utils . extract_signature_page ( text_dict , " test.pdf " )
self . assertIn ( " 2 " , result )
2025-11-26 17:21:44 +00:00
self . assertEqual ( result [ " 2 " ] , " In Witness Whereof " )
2026-01-26 16:52:55 +00:00
text_dict = {
" 1 " : " Contract page 1 " ,
" 2 " : " Signature page follows this section " ,
" 3 " : " signature page " ,
}
2025-11-26 17:21:44 +00:00
result = string_utils . extract_signature_page ( text_dict , " test.pdf " )
2025-11-03 20:45:58 +00:00
self . assertIn ( " 3 " , result )
def test_extract_effective_date_pages ( self ) :
2026-01-26 16:52:55 +00:00
text_dict = {
" 1 " : " Contract effective date: January 1, 2024 " ,
" 2 " : " General terms and conditions " ,
" 3 " : " This page has 1 signature " ,
}
2025-11-03 20:45:58 +00:00
result = string_utils . extract_effective_date_pages ( text_dict , " test.pdf " )
self . assertIn ( " 1 " , result )
self . assertIn ( " 3 " , result )
self . assertEqual ( len ( result ) , 2 )
def test_normalize_state_to_abbreviation ( self ) :
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . normalize_state_to_abbreviation ( " California " ) , " CA "
)
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . normalize_state_to_abbreviation ( " ca " ) , " CA " )
self . assertEqual ( string_utils . normalize_state_to_abbreviation ( " N.Y. " ) , " NY " )
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . normalize_state_to_abbreviation ( " Unknown State " ) ,
" Unknown State " ,
)
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . normalize_state_to_abbreviation ( " N/A " ) , " N/A " )
def test_flatten_to_strings ( self ) :
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . flatten_to_strings ( [ " a " , [ " b " , " c " ] , " d " ] ) ,
[ " a " , " b " , " c " , " d " ] ,
)
2025-11-03 20:45:58 +00:00
self . assertEqual ( string_utils . flatten_to_strings ( " single " ) , [ " single " ] )
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . flatten_to_strings ( [ 1 , [ 2 , 3 ] , " text " ] ) ,
[ " 1 " , " 2 " , " 3 " , " text " ] ,
)
self . assertEqual (
string_utils . flatten_to_strings ( [ " text " , [ " nested " ] ] ) ,
[ " text " , " nested " ] ,
)
2026-01-05 18:34:18 +00:00
2025-11-03 20:45:58 +00:00
def test_universal_json_load_multiple_invalid ( self ) :
# Multiple invalid JSON objects
2026-01-26 16:52:55 +00:00
s = " { broken: true} some text { not: valid} more text "
2025-11-03 20:45:58 +00:00
with self . assertRaises ( ValueError ) :
string_utils . universal_json_load ( s )
def test_universal_json_load_one_invalid_multiple_valid ( self ) :
# One invalid, multiple valid (should take the last valid)
s = ' { broken: true} before { " a " : 1} middle { " b " : 2} end '
self . assertEqual ( string_utils . universal_json_load ( s ) , { " b " : 2 } )
def test_universal_json_load_first_nested ( self ) :
# Multiple, with the first one being nested
s = ' { " outer " : { " inner " : [1, 2]}} middle { " simple " : true} end '
self . assertEqual ( string_utils . universal_json_load ( s ) , { " simple " : True } )
def test_universal_json_load_last_nested ( self ) :
# Multiple, with the last one being nested
s = ' { " simple " : true} middle { " outer " : { " inner " : [1, 2]}} end '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " outer " : { " inner " : [ 1 , 2 ] } }
)
2025-11-03 20:45:58 +00:00
2026-01-05 18:34:18 +00:00
def test_universal_json_load_brackets_in_strings ( self ) :
# Brackets inside string values should be ignored during bracket matching
s = ' { " text " : " array [1] and {2} " } '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " text " : " array [1] and {2} " }
)
2026-01-05 18:34:18 +00:00
# Double brackets in strings (the original bug case)
s = ' { " field " : " Exhibit [[ to Attachment ] " } '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " field " : " Exhibit [[ to Attachment ] " }
)
2026-01-05 18:34:18 +00:00
# Unbalanced brackets in strings
s = ' { " text " : " missing close [ bracket " } '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " text " : " missing close [ bracket " }
)
2026-01-05 18:34:18 +00:00
# Curly braces in strings
s = ' { " text " : " object { key: value} here " } '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " text " : " object { key: value} here " }
)
2026-01-05 18:34:18 +00:00
# Escaped quotes should not break string detection
s = ' { " text " : " say \\ " hello \\ " " } '
self . assertEqual ( string_utils . universal_json_load ( s ) , { " text " : ' say " hello " ' } )
# Escaped quotes with brackets
s = ' { " text " : " say \\ " hello [world] \\ " " } '
2026-01-26 16:52:55 +00:00
self . assertEqual (
string_utils . universal_json_load ( s ) , { " text " : ' say " hello [world] " ' }
)
2026-01-05 18:34:18 +00:00
# Real-world LLM response case with markdown and brackets in JSON strings
raw_input = ' Here is the analysis: \n \n ```json \n [ { " SERVICE_TERM " : " Inpatient services " , " REIMB_TERM " : " 100 % o f rates in Exhibit [[ to Attachment ] " }, { " SERVICE_TERM " : " Outpatient services " , " REIMB_TERM " : " 63 % o f rates in Exhibit [[ to Attachment ] " }] \n ``` '
result = string_utils . universal_json_load ( raw_input )
self . assertEqual ( len ( result ) , 2 )
self . assertEqual ( result [ 0 ] [ " SERVICE_TERM " ] , " Inpatient services " )
self . assertIn ( " Exhibit [[ " , result [ 0 ] [ " REIMB_TERM " ] )
2026-01-26 16:52:55 +00:00
2025-11-03 20:45:58 +00:00
if __name__ == " __main__ " :
unittest . main ( )