177 lines
6.2 KiB
Python
177 lines
6.2 KiB
Python
"""Basic tests for MedGraph Engine core components."""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import pytest
|
|
|
|
# Add parent dir to path
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
class TestParser:
|
|
"""Test PDF parser and chunking logic."""
|
|
|
|
def test_normalize_name(self):
|
|
from extract_entities import normalize_name
|
|
assert normalize_name(" Otitis Media Aguda ") == "otitis media aguda"
|
|
assert normalize_name("hipertensión...") == "hipertensión"
|
|
assert normalize_name(" múltiples espacios ") == "múltiples espacios"
|
|
assert normalize_name("") == ""
|
|
|
|
def test_strip_accents(self):
|
|
from dedup_entities import strip_accents
|
|
assert strip_accents("hipertensión") == "hipertension"
|
|
assert strip_accents("diagnóstico") == "diagnostico"
|
|
assert strip_accents("farmacología") == "farmacologia"
|
|
assert strip_accents("normal") == "normal"
|
|
|
|
def test_has_accents(self):
|
|
from dedup_entities import has_accents
|
|
assert has_accents("hipertensión") is True
|
|
assert has_accents("hipertension") is False
|
|
|
|
def test_pick_canonical(self):
|
|
from dedup_entities import pick_canonical
|
|
nodes = [
|
|
{"nombre": "hipertension", "freq": 10, "sinonimos": []},
|
|
{"nombre": "hipertensión", "freq": 5, "sinonimos": ["HTA"]},
|
|
]
|
|
canon = pick_canonical(nodes)
|
|
assert canon["nombre"] == "hipertensión" # prefers accented
|
|
|
|
def test_find_accent_groups(self):
|
|
from dedup_entities import find_accent_groups
|
|
entities = [
|
|
{"nombre": "hipertensión"},
|
|
{"nombre": "hipertension"},
|
|
{"nombre": "diabetes"},
|
|
]
|
|
groups = find_accent_groups(entities)
|
|
assert len(groups) == 1
|
|
assert "hipertension" in groups
|
|
assert len(groups["hipertension"]) == 2
|
|
|
|
|
|
class TestChunking:
|
|
"""Test chunking parameters and structure."""
|
|
|
|
def test_chunk_config_values(self):
|
|
from parser_v2 import TARGET_SIZE, MIN_SIZE, MAX_SIZE, OVERLAP_SIZE
|
|
assert TARGET_SIZE == 280
|
|
assert MIN_SIZE == 150
|
|
assert MAX_SIZE == 380
|
|
assert OVERLAP_SIZE == 60
|
|
assert MIN_SIZE < TARGET_SIZE < MAX_SIZE
|
|
|
|
def test_parent_config(self):
|
|
from parser_v2 import PARENT_WINDOW, MAX_PARENT_WORDS
|
|
assert PARENT_WINDOW == 3
|
|
assert MAX_PARENT_WORDS == 1200
|
|
|
|
|
|
class TestExtraction:
|
|
"""Test entity extraction validation logic."""
|
|
|
|
def test_validate_entity_types(self):
|
|
from extract_entities import ENTITY_TYPES
|
|
expected = {
|
|
"patologia", "estructura_anatomica", "procedimiento",
|
|
"farmaco", "grupo_farmacologico", "agente", "signo",
|
|
"sintoma", "metodo_dx", "hallazgo", "parametro",
|
|
}
|
|
assert ENTITY_TYPES == expected
|
|
|
|
def test_validate_relation_types(self):
|
|
from extract_entities import RELATION_TYPES
|
|
assert "CAUSADA_POR" in RELATION_TYPES
|
|
assert "SE_TRATA_CON" in RELATION_TYPES
|
|
assert "SE_DIAGNOSTICA_CON" in RELATION_TYPES
|
|
assert "ASOCIADA_A" in RELATION_TYPES
|
|
|
|
def test_type_to_label_mapping(self):
|
|
from extract_entities import TYPE_TO_LABEL
|
|
assert TYPE_TO_LABEL["patologia"] == "Patologia"
|
|
assert TYPE_TO_LABEL["farmaco"] == "Farmaco"
|
|
assert TYPE_TO_LABEL["estructura_anatomica"] == "EstructuraAnatomica"
|
|
|
|
def test_validate_extraction_filters_invalid(self):
|
|
from extract_entities import _validate_extraction
|
|
data = {
|
|
"entidades": [
|
|
{"nombre": "otitis media", "tipo": "patologia", "sinonimos": []},
|
|
{"nombre": "x", "tipo": "patologia", "sinonimos": []}, # too short
|
|
{"nombre": "valid", "tipo": "INVALID_TYPE", "sinonimos": []}, # bad type
|
|
{"nombre": "", "tipo": "patologia", "sinonimos": []}, # empty
|
|
],
|
|
"relaciones": []
|
|
}
|
|
result = _validate_extraction(data, {"id": "test", "libro_id": "test"})
|
|
assert len(result["entidades"]) == 1
|
|
assert result["entidades"][0]["nombre"] == "otitis media"
|
|
|
|
|
|
class TestCanonicalization:
|
|
"""Test entity canonicalization logic."""
|
|
|
|
def test_canonicalize_merges_duplicates(self):
|
|
from extract_entities import canonicalize_entities
|
|
extractions = [
|
|
{
|
|
"entidades": [
|
|
{"nombre": "otitis media", "tipo": "patologia", "sinonimos": ["OMA"]},
|
|
],
|
|
"relaciones": [],
|
|
"chunk_id": "c1",
|
|
},
|
|
{
|
|
"entidades": [
|
|
{"nombre": "otitis media", "tipo": "patologia", "sinonimos": []},
|
|
],
|
|
"relaciones": [],
|
|
"chunk_id": "c2",
|
|
},
|
|
]
|
|
entities, relations = canonicalize_entities(extractions)
|
|
# Should merge into 1 entity
|
|
otitis = [e for e in entities if e["nombre"] == "otitis media"]
|
|
assert len(otitis) == 1
|
|
assert otitis[0]["freq"] == 2
|
|
assert "OMA" in otitis[0]["sinonimos"]
|
|
|
|
|
|
class TestAPIConfig:
|
|
"""Test API configuration basics."""
|
|
|
|
def test_env_example_exists(self):
|
|
assert os.path.exists(
|
|
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".env.example")
|
|
)
|
|
|
|
def test_requirements_no_vertexai(self):
|
|
req_path = os.path.join(
|
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "api", "requirements.txt"
|
|
)
|
|
with open(req_path) as f:
|
|
content = f.read()
|
|
assert "vertexai" not in content.lower()
|
|
assert "aiplatform" not in content.lower()
|
|
|
|
def test_docker_compose_exists(self):
|
|
assert os.path.exists(
|
|
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "docker-compose.yml")
|
|
)
|
|
|
|
def test_docker_compose_no_real_credentials(self):
|
|
compose_path = os.path.join(
|
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "docker-compose.yml"
|
|
)
|
|
with open(compose_path) as f:
|
|
content = f.read()
|
|
assert "changeme" in content
|
|
assert "AIzaSy" not in content
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|