feat(#51): Task 12 completada - Tests automatizados para catálogo de parámetros
- Creados 4 archivos de test completos con cobertura total - test_analysis_parameter.py: Tests del modelo de parámetros - test_parameter_range.py: Tests de rangos de referencia - test_result_parameter_integration.py: Tests de integración - test_auto_result_generation.py: Tests de generación automática - Creado script simplificado test_parameters_simple.py para ejecución rápida - Corregido valor por defecto de age_max a 150 en parameter_range.py - Documentación completa en README.md
This commit is contained in:
parent
999896f89e
commit
aaa1204490
|
@ -44,7 +44,7 @@ class LimsParameterRange(models.Model):
|
|||
|
||||
age_max = fields.Integer(
|
||||
string='Edad Máxima',
|
||||
default=999,
|
||||
default=150,
|
||||
help='Edad máxima en años (inclusive)'
|
||||
)
|
||||
|
||||
|
@ -117,7 +117,7 @@ class LimsParameterRange(models.Model):
|
|||
parts.append(gender_name)
|
||||
|
||||
# Agregar rango de edad
|
||||
if record.age_min == 0 and record.age_max == 999:
|
||||
if record.age_min == 0 and record.age_max == 150:
|
||||
parts.append('Todas las edades')
|
||||
else:
|
||||
parts.append(f"{record.age_min}-{record.age_max} años")
|
||||
|
|
80
lims_management/tests/README.md
Normal file
80
lims_management/tests/README.md
Normal file
|
@ -0,0 +1,80 @@
|
|||
# Tests del Módulo LIMS
|
||||
|
||||
Este directorio contiene los tests automatizados para el módulo `lims_management`, específicamente para el sistema de catálogo de parámetros.
|
||||
|
||||
## Estructura de Tests
|
||||
|
||||
### 1. test_analysis_parameter.py
|
||||
Tests para el modelo `lims.analysis.parameter`:
|
||||
- Creación de parámetros con diferentes tipos de valores
|
||||
- Validaciones de campos requeridos
|
||||
- Prevención de códigos duplicados
|
||||
- Relaciones con rangos y análisis
|
||||
|
||||
### 2. test_parameter_range.py
|
||||
Tests para el modelo `lims.parameter.range`:
|
||||
- Creación de rangos de referencia
|
||||
- Validaciones de valores mínimos y máximos
|
||||
- Rangos específicos por género y edad
|
||||
- Búsqueda de rangos aplicables según características del paciente
|
||||
|
||||
### 3. test_result_parameter_integration.py
|
||||
Tests de integración entre resultados y parámetros:
|
||||
- Asignación de parámetros a resultados
|
||||
- Selección automática de rangos aplicables
|
||||
- Detección de valores fuera de rango y críticos
|
||||
- Formato de visualización de resultados
|
||||
|
||||
### 4. test_auto_result_generation.py
|
||||
Tests para la generación automática de resultados:
|
||||
- Creación automática al generar pruebas
|
||||
- Herencia de secuencia desde la configuración
|
||||
- Rendimiento en creación masiva
|
||||
|
||||
## Ejecución de Tests
|
||||
|
||||
### Usando Odoo Test Framework
|
||||
```bash
|
||||
# Desde el servidor Odoo
|
||||
python3 -m odoo.cli.server -d lims_demo --test-enable --test-tags lims_management
|
||||
```
|
||||
|
||||
### Usando el Script Simplificado
|
||||
```bash
|
||||
# Copiar script al contenedor
|
||||
docker cp test/test_parameters_simple.py lims_odoo:/tmp/
|
||||
|
||||
# Ejecutar tests
|
||||
docker-compose exec odoo python3 /tmp/test_parameters_simple.py
|
||||
```
|
||||
|
||||
## Cobertura de Tests
|
||||
|
||||
Los tests cubren:
|
||||
|
||||
1. **Validaciones del Modelo**
|
||||
- Campos requeridos según tipo de parámetro
|
||||
- Restricciones de unicidad
|
||||
- Validaciones de rangos
|
||||
|
||||
2. **Lógica de Negocio**
|
||||
- Generación automática de resultados
|
||||
- Búsqueda de rangos aplicables
|
||||
- Cálculo de estados (fuera de rango, crítico)
|
||||
|
||||
3. **Integración**
|
||||
- Flujo completo desde orden hasta resultados
|
||||
- Compatibilidad con el sistema existente
|
||||
|
||||
## Datos de Prueba
|
||||
|
||||
Los tests utilizan:
|
||||
- Parámetros de demostración del archivo `parameter_demo.xml`
|
||||
- Rangos de referencia de `parameter_range_demo.xml`
|
||||
- Análisis configurados en `analysis_parameter_config_demo.xml`
|
||||
|
||||
## Notas Importantes
|
||||
|
||||
- Los tests se ejecutan en transacciones que se revierten automáticamente
|
||||
- No afectan los datos de producción o demostración
|
||||
- Requieren que el módulo esté instalado con datos demo
|
5
lims_management/tests/__init__.py
Normal file
5
lims_management/tests/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import test_analysis_parameter
|
||||
from . import test_parameter_range
|
||||
from . import test_result_parameter_integration
|
||||
from . import test_auto_result_generation
|
175
lims_management/tests/test_analysis_parameter.py
Normal file
175
lims_management/tests/test_analysis_parameter.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests para el modelo lims.analysis.parameter
|
||||
"""
|
||||
from odoo.tests import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class TestAnalysisParameter(TransactionCase):
|
||||
"""Tests para el catálogo de parámetros de análisis"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.Parameter = self.env['lims.analysis.parameter']
|
||||
|
||||
def test_create_numeric_parameter(self):
|
||||
"""Test crear parámetro numérico con validaciones"""
|
||||
# Crear parámetro numérico válido
|
||||
param = self.Parameter.create({
|
||||
'code': 'TEST001',
|
||||
'name': 'Test Parameter',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'mg/dL',
|
||||
'description': 'Test numeric parameter'
|
||||
})
|
||||
|
||||
self.assertEqual(param.code, 'TEST001')
|
||||
self.assertEqual(param.value_type, 'numeric')
|
||||
self.assertEqual(param.unit, 'mg/dL')
|
||||
|
||||
def test_numeric_parameter_requires_unit(self):
|
||||
"""Test que parámetros numéricos requieren unidad"""
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
self.Parameter.create({
|
||||
'code': 'TEST002',
|
||||
'name': 'Test Parameter No Unit',
|
||||
'value_type': 'numeric',
|
||||
# Sin unit - debe fallar
|
||||
})
|
||||
self.assertIn('unidad de medida', str(e.exception))
|
||||
|
||||
def test_create_selection_parameter(self):
|
||||
"""Test crear parámetro de selección con opciones"""
|
||||
param = self.Parameter.create({
|
||||
'code': 'TEST003',
|
||||
'name': 'Test Selection',
|
||||
'value_type': 'selection',
|
||||
'selection_values': 'Positivo,Negativo,Indeterminado'
|
||||
})
|
||||
|
||||
self.assertEqual(param.value_type, 'selection')
|
||||
self.assertEqual(param.selection_values, 'Positivo,Negativo,Indeterminado')
|
||||
|
||||
def test_selection_parameter_requires_values(self):
|
||||
"""Test que parámetros de selección requieren valores"""
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
self.Parameter.create({
|
||||
'code': 'TEST004',
|
||||
'name': 'Test Selection No Values',
|
||||
'value_type': 'selection',
|
||||
# Sin selection_values - debe fallar
|
||||
})
|
||||
self.assertIn('valores de selección', str(e.exception))
|
||||
|
||||
def test_duplicate_code_not_allowed(self):
|
||||
"""Test que no se permiten códigos duplicados"""
|
||||
# Crear primer parámetro
|
||||
self.Parameter.create({
|
||||
'code': 'DUP001',
|
||||
'name': 'Original Parameter',
|
||||
'value_type': 'text'
|
||||
})
|
||||
|
||||
# Intentar crear duplicado
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
self.Parameter.create({
|
||||
'code': 'DUP001',
|
||||
'name': 'Duplicate Parameter',
|
||||
'value_type': 'text'
|
||||
})
|
||||
self.assertIn('ya existe', str(e.exception))
|
||||
|
||||
def test_boolean_parameter(self):
|
||||
"""Test crear parámetro booleano"""
|
||||
param = self.Parameter.create({
|
||||
'code': 'BOOL001',
|
||||
'name': 'Test Boolean',
|
||||
'value_type': 'boolean',
|
||||
'description': 'Boolean parameter'
|
||||
})
|
||||
|
||||
self.assertEqual(param.value_type, 'boolean')
|
||||
self.assertFalse(param.unit) # Boolean no debe tener unidad
|
||||
|
||||
def test_text_parameter(self):
|
||||
"""Test crear parámetro de texto"""
|
||||
param = self.Parameter.create({
|
||||
'code': 'TEXT001',
|
||||
'name': 'Test Text',
|
||||
'value_type': 'text',
|
||||
'description': 'Text parameter'
|
||||
})
|
||||
|
||||
self.assertEqual(param.value_type, 'text')
|
||||
self.assertFalse(param.unit) # Text no debe tener unidad
|
||||
self.assertFalse(param.selection_values) # Text no debe tener valores de selección
|
||||
|
||||
def test_parameter_name_display(self):
|
||||
"""Test nombre mostrado del parámetro"""
|
||||
# Con unidad
|
||||
param1 = self.Parameter.create({
|
||||
'code': 'DISP001',
|
||||
'name': 'Glucosa',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'mg/dL'
|
||||
})
|
||||
self.assertEqual(param1.display_name, 'Glucosa (mg/dL)')
|
||||
|
||||
# Sin unidad
|
||||
param2 = self.Parameter.create({
|
||||
'code': 'DISP002',
|
||||
'name': 'Cultivo',
|
||||
'value_type': 'text'
|
||||
})
|
||||
self.assertEqual(param2.display_name, 'Cultivo')
|
||||
|
||||
def test_parameter_ranges_relationship(self):
|
||||
"""Test relación con rangos de referencia"""
|
||||
param = self.Parameter.create({
|
||||
'code': 'RANGE001',
|
||||
'name': 'Test with Ranges',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'U/L'
|
||||
})
|
||||
|
||||
# Crear rango para este parámetro
|
||||
range1 = self.env['lims.parameter.range'].create({
|
||||
'parameter_id': param.id,
|
||||
'name': 'Adult Male',
|
||||
'gender': 'male',
|
||||
'age_min': 18,
|
||||
'age_max': 65,
|
||||
'normal_min': 10.0,
|
||||
'normal_max': 50.0
|
||||
})
|
||||
|
||||
self.assertEqual(len(param.range_ids), 1)
|
||||
self.assertEqual(param.range_ids[0], range1)
|
||||
|
||||
def test_parameter_analysis_relationship(self):
|
||||
"""Test relación con análisis a través de product.template.parameter"""
|
||||
param = self.Parameter.create({
|
||||
'code': 'ANAL001',
|
||||
'name': 'Test Analysis Link',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'mmol/L'
|
||||
})
|
||||
|
||||
# Crear producto análisis
|
||||
analysis = self.env['product.template'].create({
|
||||
'name': 'Test Analysis',
|
||||
'type': 'service',
|
||||
'is_analysis': True,
|
||||
'categ_id': self.env.ref('lims_management.product_category_clinical_analysis').id,
|
||||
})
|
||||
|
||||
# Crear configuración parámetro-análisis
|
||||
config = self.env['product.template.parameter'].create({
|
||||
'product_tmpl_id': analysis.id,
|
||||
'parameter_id': param.id,
|
||||
'sequence': 10
|
||||
})
|
||||
|
||||
self.assertEqual(len(param.analysis_config_ids), 1)
|
||||
self.assertEqual(param.analysis_config_ids[0], config)
|
283
lims_management/tests/test_auto_result_generation.py
Normal file
283
lims_management/tests/test_auto_result_generation.py
Normal file
|
@ -0,0 +1,283 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests para la generación automática de resultados basada en parámetros
|
||||
"""
|
||||
from odoo.tests import TransactionCase
|
||||
from datetime import date
|
||||
|
||||
|
||||
class TestAutoResultGeneration(TransactionCase):
|
||||
"""Tests para la generación automática de resultados al crear pruebas"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
# Modelos
|
||||
self.Test = self.env['lims.test']
|
||||
self.Sample = self.env['stock.lot']
|
||||
self.Order = self.env['sale.order']
|
||||
self.Parameter = self.env['lims.analysis.parameter']
|
||||
self.TemplateParam = self.env['product.template.parameter']
|
||||
self.Product = self.env['product.template']
|
||||
self.Partner = self.env['res.partner']
|
||||
|
||||
# Crear paciente
|
||||
self.patient = self.Partner.create({
|
||||
'name': 'Patient for Auto Generation',
|
||||
'is_patient': True,
|
||||
'gender': 'male',
|
||||
'birth_date': date(1985, 3, 15)
|
||||
})
|
||||
|
||||
# Crear doctor
|
||||
self.doctor = self.Partner.create({
|
||||
'name': 'Dr. Test',
|
||||
'is_doctor': True
|
||||
})
|
||||
|
||||
# Crear parámetros
|
||||
self.param1 = self.Parameter.create({
|
||||
'code': 'AUTO1',
|
||||
'name': 'Parameter Auto 1',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'mg/dL'
|
||||
})
|
||||
|
||||
self.param2 = self.Parameter.create({
|
||||
'code': 'AUTO2',
|
||||
'name': 'Parameter Auto 2',
|
||||
'value_type': 'selection',
|
||||
'selection_values': 'Normal,Anormal'
|
||||
})
|
||||
|
||||
self.param3 = self.Parameter.create({
|
||||
'code': 'AUTO3',
|
||||
'name': 'Parameter Auto 3',
|
||||
'value_type': 'text'
|
||||
})
|
||||
|
||||
# Crear análisis con parámetros configurados
|
||||
self.analysis_multi = self.Product.create({
|
||||
'name': 'Multi-Parameter Analysis',
|
||||
'type': 'service',
|
||||
'is_analysis': True,
|
||||
'categ_id': self.env.ref('lims_management.product_category_clinical_analysis').id,
|
||||
'sample_type_id': self.env.ref('lims_management.sample_type_blood').id,
|
||||
})
|
||||
|
||||
# Configurar parámetros en el análisis
|
||||
self.TemplateParam.create({
|
||||
'product_tmpl_id': self.analysis_multi.id,
|
||||
'parameter_id': self.param1.id,
|
||||
'sequence': 10
|
||||
})
|
||||
|
||||
self.TemplateParam.create({
|
||||
'product_tmpl_id': self.analysis_multi.id,
|
||||
'parameter_id': self.param2.id,
|
||||
'sequence': 20
|
||||
})
|
||||
|
||||
self.TemplateParam.create({
|
||||
'product_tmpl_id': self.analysis_multi.id,
|
||||
'parameter_id': self.param3.id,
|
||||
'sequence': 30
|
||||
})
|
||||
|
||||
# Crear análisis sin parámetros
|
||||
self.analysis_empty = self.Product.create({
|
||||
'name': 'Empty Analysis',
|
||||
'type': 'service',
|
||||
'is_analysis': True,
|
||||
'categ_id': self.env.ref('lims_management.product_category_clinical_analysis').id,
|
||||
})
|
||||
|
||||
def test_auto_generate_results_on_test_creation(self):
|
||||
"""Test generación automática de resultados al crear una prueba"""
|
||||
# Crear orden y muestra
|
||||
order = self.Order.create({
|
||||
'partner_id': self.patient.id,
|
||||
'doctor_id': self.doctor.id,
|
||||
'is_lab_request': True,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': self.analysis_multi.product_variant_id.id,
|
||||
'product_uom_qty': 1.0
|
||||
})]
|
||||
})
|
||||
order.action_confirm()
|
||||
|
||||
# Generar muestra
|
||||
order.action_generate_samples()
|
||||
sample = order.lab_sample_ids[0]
|
||||
|
||||
# La prueba debe haberse creado automáticamente con los resultados
|
||||
self.assertEqual(len(sample.test_ids), 1)
|
||||
test = sample.test_ids[0]
|
||||
|
||||
# Verificar que se generaron todos los resultados
|
||||
self.assertEqual(len(test.result_ids), 3)
|
||||
|
||||
# Verificar que cada resultado tiene el parámetro correcto
|
||||
param_ids = test.result_ids.mapped('parameter_id')
|
||||
self.assertIn(self.param1, param_ids)
|
||||
self.assertIn(self.param2, param_ids)
|
||||
self.assertIn(self.param3, param_ids)
|
||||
|
||||
# Verificar orden de secuencia
|
||||
results_sorted = test.result_ids.sorted('sequence')
|
||||
self.assertEqual(results_sorted[0].parameter_id, self.param1)
|
||||
self.assertEqual(results_sorted[1].parameter_id, self.param2)
|
||||
self.assertEqual(results_sorted[2].parameter_id, self.param3)
|
||||
|
||||
def test_no_results_for_analysis_without_parameters(self):
|
||||
"""Test que no se generan resultados para análisis sin parámetros"""
|
||||
# Crear orden con análisis sin parámetros
|
||||
order = self.Order.create({
|
||||
'partner_id': self.patient.id,
|
||||
'is_lab_request': True,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': self.analysis_empty.product_variant_id.id,
|
||||
'product_uom_qty': 1.0
|
||||
})]
|
||||
})
|
||||
order.action_confirm()
|
||||
order.action_generate_samples()
|
||||
|
||||
sample = order.lab_sample_ids[0]
|
||||
test = sample.test_ids[0]
|
||||
|
||||
# No debe haber resultados
|
||||
self.assertEqual(len(test.result_ids), 0)
|
||||
|
||||
def test_manual_test_creation_generates_results(self):
|
||||
"""Test generación de resultados al crear prueba manualmente"""
|
||||
# Crear muestra manual
|
||||
sample = self.Sample.create({
|
||||
'name': 'SAMPLE-MANUAL-001',
|
||||
'is_lab_sample': True,
|
||||
'patient_id': self.patient.id,
|
||||
'sample_state': 'collected'
|
||||
})
|
||||
|
||||
# Crear prueba manualmente
|
||||
test = self.Test.create({
|
||||
'sample_id': sample.id,
|
||||
'patient_id': self.patient.id,
|
||||
'product_id': self.analysis_multi.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
# Verificar generación automática
|
||||
self.assertEqual(len(test.result_ids), 3)
|
||||
|
||||
def test_results_inherit_correct_sequence(self):
|
||||
"""Test que los resultados heredan la secuencia correcta"""
|
||||
# Crear análisis con secuencias específicas
|
||||
analysis = self.Product.create({
|
||||
'name': 'Sequence Test Analysis',
|
||||
'type': 'service',
|
||||
'is_analysis': True,
|
||||
'categ_id': self.env.ref('lims_management.product_category_clinical_analysis').id,
|
||||
})
|
||||
|
||||
# Configurar con secuencias no consecutivas
|
||||
self.TemplateParam.create({
|
||||
'product_tmpl_id': analysis.id,
|
||||
'parameter_id': self.param1.id,
|
||||
'sequence': 100
|
||||
})
|
||||
|
||||
self.TemplateParam.create({
|
||||
'product_tmpl_id': analysis.id,
|
||||
'parameter_id': self.param2.id,
|
||||
'sequence': 50
|
||||
})
|
||||
|
||||
self.TemplateParam.create({
|
||||
'product_tmpl_id': analysis.id,
|
||||
'parameter_id': self.param3.id,
|
||||
'sequence': 75
|
||||
})
|
||||
|
||||
# Crear prueba
|
||||
test = self.Test.create({
|
||||
'patient_id': self.patient.id,
|
||||
'product_id': analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
# Verificar orden: param2 (50), param3 (75), param1 (100)
|
||||
results_sorted = test.result_ids.sorted('sequence')
|
||||
self.assertEqual(results_sorted[0].parameter_id, self.param2)
|
||||
self.assertEqual(results_sorted[0].sequence, 50)
|
||||
self.assertEqual(results_sorted[1].parameter_id, self.param3)
|
||||
self.assertEqual(results_sorted[1].sequence, 75)
|
||||
self.assertEqual(results_sorted[2].parameter_id, self.param1)
|
||||
self.assertEqual(results_sorted[2].sequence, 100)
|
||||
|
||||
def test_bulk_test_creation_performance(self):
|
||||
"""Test rendimiento de creación masiva de pruebas"""
|
||||
# Crear múltiples órdenes
|
||||
orders = []
|
||||
for i in range(5):
|
||||
order = self.Order.create({
|
||||
'partner_id': self.patient.id,
|
||||
'is_lab_request': True,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': self.analysis_multi.product_variant_id.id,
|
||||
'product_uom_qty': 1.0
|
||||
})]
|
||||
})
|
||||
order.action_confirm()
|
||||
orders.append(order)
|
||||
|
||||
# Generar muestras en lote
|
||||
for order in orders:
|
||||
order.action_generate_samples()
|
||||
|
||||
# Verificar que todas las pruebas tienen resultados
|
||||
total_tests = 0
|
||||
total_results = 0
|
||||
|
||||
for order in orders:
|
||||
for sample in order.lab_sample_ids:
|
||||
for test in sample.test_ids:
|
||||
total_tests += 1
|
||||
total_results += len(test.result_ids)
|
||||
|
||||
self.assertEqual(total_tests, 5)
|
||||
self.assertEqual(total_results, 15) # 5 tests * 3 parameters each
|
||||
|
||||
def test_result_generation_with_mixed_analyses(self):
|
||||
"""Test generación con análisis mixtos (con y sin parámetros)"""
|
||||
# Crear orden con múltiples análisis
|
||||
order = self.Order.create({
|
||||
'partner_id': self.patient.id,
|
||||
'is_lab_request': True,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'product_id': self.analysis_multi.product_variant_id.id,
|
||||
'product_uom_qty': 1.0
|
||||
}),
|
||||
(0, 0, {
|
||||
'product_id': self.analysis_empty.product_variant_id.id,
|
||||
'product_uom_qty': 1.0
|
||||
})
|
||||
]
|
||||
})
|
||||
order.action_confirm()
|
||||
order.action_generate_samples()
|
||||
|
||||
# Verificar resultados por prueba
|
||||
tests_with_results = 0
|
||||
tests_without_results = 0
|
||||
|
||||
for sample in order.lab_sample_ids:
|
||||
for test in sample.test_ids:
|
||||
if test.result_ids:
|
||||
tests_with_results += 1
|
||||
else:
|
||||
tests_without_results += 1
|
||||
|
||||
self.assertEqual(tests_with_results, 1) # Solo analysis_multi
|
||||
self.assertEqual(tests_without_results, 1) # Solo analysis_empty
|
249
lims_management/tests/test_parameter_range.py
Normal file
249
lims_management/tests/test_parameter_range.py
Normal file
|
@ -0,0 +1,249 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests para el modelo lims.parameter.range
|
||||
"""
|
||||
from odoo.tests import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class TestParameterRange(TransactionCase):
|
||||
"""Tests para rangos de referencia de parámetros"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.Range = self.env['lims.parameter.range']
|
||||
self.Parameter = self.env['lims.analysis.parameter']
|
||||
|
||||
# Crear parámetro de prueba
|
||||
self.test_param = self.Parameter.create({
|
||||
'code': 'HGB_TEST',
|
||||
'name': 'Hemoglobina Test',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'g/dL'
|
||||
})
|
||||
|
||||
def test_create_basic_range(self):
|
||||
"""Test crear rango básico"""
|
||||
range_obj = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Adulto General',
|
||||
'normal_min': 12.0,
|
||||
'normal_max': 16.0
|
||||
})
|
||||
|
||||
self.assertEqual(range_obj.parameter_id, self.test_param)
|
||||
self.assertEqual(range_obj.normal_min, 12.0)
|
||||
self.assertEqual(range_obj.normal_max, 16.0)
|
||||
self.assertFalse(range_obj.gender) # Sin género específico
|
||||
|
||||
def test_range_validation_min_max(self):
|
||||
"""Test validación que min < max"""
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Rango Inválido',
|
||||
'normal_min': 20.0,
|
||||
'normal_max': 10.0 # Max menor que min
|
||||
})
|
||||
self.assertIn('menor o igual', str(e.exception))
|
||||
|
||||
def test_range_validation_age(self):
|
||||
"""Test validación de rangos de edad"""
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Rango Edad Inválida',
|
||||
'age_min': 65,
|
||||
'age_max': 18, # Max menor que min
|
||||
'normal_min': 12.0,
|
||||
'normal_max': 16.0
|
||||
})
|
||||
self.assertIn('edad', str(e.exception))
|
||||
|
||||
def test_critical_values_validation(self):
|
||||
"""Test validación de valores críticos"""
|
||||
# Crítico min debe ser menor que normal min
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Crítico Inválido',
|
||||
'normal_min': 12.0,
|
||||
'normal_max': 16.0,
|
||||
'critical_min': 13.0 # Mayor que normal_min
|
||||
})
|
||||
self.assertIn('crítico mínimo', str(e.exception))
|
||||
|
||||
# Crítico max debe ser mayor que normal max
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Crítico Inválido 2',
|
||||
'normal_min': 12.0,
|
||||
'normal_max': 16.0,
|
||||
'critical_max': 15.0 # Menor que normal_max
|
||||
})
|
||||
self.assertIn('crítico máximo', str(e.exception))
|
||||
|
||||
def test_gender_specific_ranges(self):
|
||||
"""Test rangos específicos por género"""
|
||||
# Rango para hombres
|
||||
male_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Hombre Adulto',
|
||||
'gender': 'male',
|
||||
'age_min': 18,
|
||||
'age_max': 65,
|
||||
'normal_min': 14.0,
|
||||
'normal_max': 18.0
|
||||
})
|
||||
|
||||
# Rango para mujeres
|
||||
female_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Mujer Adulta',
|
||||
'gender': 'female',
|
||||
'age_min': 18,
|
||||
'age_max': 65,
|
||||
'normal_min': 12.0,
|
||||
'normal_max': 16.0
|
||||
})
|
||||
|
||||
self.assertEqual(male_range.gender, 'male')
|
||||
self.assertEqual(female_range.gender, 'female')
|
||||
|
||||
def test_pregnancy_specific_range(self):
|
||||
"""Test rangos para embarazadas"""
|
||||
pregnancy_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Embarazada',
|
||||
'gender': 'female',
|
||||
'pregnant': True,
|
||||
'age_min': 15,
|
||||
'age_max': 50,
|
||||
'normal_min': 11.0,
|
||||
'normal_max': 14.0
|
||||
})
|
||||
|
||||
self.assertTrue(pregnancy_range.pregnant)
|
||||
self.assertEqual(pregnancy_range.gender, 'female')
|
||||
|
||||
def test_find_applicable_range(self):
|
||||
"""Test encontrar rango aplicable según características del paciente"""
|
||||
# Crear varios rangos
|
||||
general_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'General',
|
||||
'normal_min': 12.0,
|
||||
'normal_max': 16.0
|
||||
})
|
||||
|
||||
male_adult_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Hombre Adulto',
|
||||
'gender': 'male',
|
||||
'age_min': 18,
|
||||
'age_max': 65,
|
||||
'normal_min': 14.0,
|
||||
'normal_max': 18.0
|
||||
})
|
||||
|
||||
child_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Niño',
|
||||
'age_max': 12,
|
||||
'normal_min': 11.0,
|
||||
'normal_max': 14.0
|
||||
})
|
||||
|
||||
pregnant_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Embarazada',
|
||||
'gender': 'female',
|
||||
'pregnant': True,
|
||||
'normal_min': 11.0,
|
||||
'normal_max': 14.0
|
||||
})
|
||||
|
||||
# Test para hombre adulto de 30 años
|
||||
applicable = self.Range._find_applicable_range(
|
||||
self.test_param.id,
|
||||
gender='male',
|
||||
age=30,
|
||||
is_pregnant=False
|
||||
)
|
||||
self.assertEqual(applicable, male_adult_range)
|
||||
|
||||
# Test para niño de 8 años
|
||||
applicable = self.Range._find_applicable_range(
|
||||
self.test_param.id,
|
||||
gender='male',
|
||||
age=8,
|
||||
is_pregnant=False
|
||||
)
|
||||
self.assertEqual(applicable, child_range)
|
||||
|
||||
# Test para mujer embarazada
|
||||
applicable = self.Range._find_applicable_range(
|
||||
self.test_param.id,
|
||||
gender='female',
|
||||
age=28,
|
||||
is_pregnant=True
|
||||
)
|
||||
self.assertEqual(applicable, pregnant_range)
|
||||
|
||||
# Test para caso sin rango específico (mujer no embarazada)
|
||||
applicable = self.Range._find_applicable_range(
|
||||
self.test_param.id,
|
||||
gender='female',
|
||||
age=35,
|
||||
is_pregnant=False
|
||||
)
|
||||
self.assertEqual(applicable, general_range) # Debe devolver el rango general
|
||||
|
||||
def test_range_overlap_allowed(self):
|
||||
"""Test que se permiten rangos superpuestos"""
|
||||
# Rango 1: 0-18 años
|
||||
range1 = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Pediátrico',
|
||||
'age_max': 18,
|
||||
'normal_min': 11.0,
|
||||
'normal_max': 15.0
|
||||
})
|
||||
|
||||
# Rango 2: 12-65 años (se superpone con rango 1)
|
||||
range2 = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Adolescente-Adulto',
|
||||
'age_min': 12,
|
||||
'age_max': 65,
|
||||
'normal_min': 12.0,
|
||||
'normal_max': 16.0
|
||||
})
|
||||
|
||||
# Ambos rangos deben existir sin error
|
||||
self.assertTrue(range1.exists())
|
||||
self.assertTrue(range2.exists())
|
||||
|
||||
def test_range_description_compute(self):
|
||||
"""Test generación automática de descripción"""
|
||||
# Rango con todas las características
|
||||
full_range = self.Range.create({
|
||||
'parameter_id': self.test_param.id,
|
||||
'name': 'Completo',
|
||||
'gender': 'female',
|
||||
'age_min': 18,
|
||||
'age_max': 45,
|
||||
'pregnant': True,
|
||||
'normal_min': 11.0,
|
||||
'normal_max': 14.0,
|
||||
'critical_min': 8.0,
|
||||
'critical_max': 20.0
|
||||
})
|
||||
|
||||
description = full_range.description
|
||||
self.assertIn('Mujer', description)
|
||||
self.assertIn('18-45 años', description)
|
||||
self.assertIn('Embarazada', description)
|
||||
self.assertIn('11.0 - 14.0', description)
|
||||
self.assertIn('Críticos', description)
|
291
lims_management/tests/test_result_parameter_integration.py
Normal file
291
lims_management/tests/test_result_parameter_integration.py
Normal file
|
@ -0,0 +1,291 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests para la integración entre resultados y el catálogo de parámetros
|
||||
"""
|
||||
from odoo.tests import TransactionCase
|
||||
from datetime import date
|
||||
|
||||
|
||||
class TestResultParameterIntegration(TransactionCase):
|
||||
"""Tests para la integración de resultados con parámetros y rangos"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
# Modelos
|
||||
self.Result = self.env['lims.result']
|
||||
self.Test = self.env['lims.test']
|
||||
self.Parameter = self.env['lims.analysis.parameter']
|
||||
self.Range = self.env['lims.parameter.range']
|
||||
self.Partner = self.env['res.partner']
|
||||
self.Product = self.env['product.template']
|
||||
|
||||
# Crear paciente de prueba
|
||||
self.patient_male = self.Partner.create({
|
||||
'name': 'Test Patient Male',
|
||||
'is_patient': True,
|
||||
'gender': 'male',
|
||||
'birth_date': date(1990, 1, 1) # 34 años aprox
|
||||
})
|
||||
|
||||
self.patient_female_pregnant = self.Partner.create({
|
||||
'name': 'Test Patient Pregnant',
|
||||
'is_patient': True,
|
||||
'gender': 'female',
|
||||
'birth_date': date(1995, 6, 15), # 29 años aprox
|
||||
'is_pregnant': True
|
||||
})
|
||||
|
||||
# Crear parámetro de prueba
|
||||
self.param_glucose = self.Parameter.create({
|
||||
'code': 'GLU_TEST',
|
||||
'name': 'Glucosa Test',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'mg/dL'
|
||||
})
|
||||
|
||||
# Crear rangos de referencia
|
||||
self.range_general = self.Range.create({
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'name': 'General',
|
||||
'normal_min': 70.0,
|
||||
'normal_max': 100.0,
|
||||
'critical_min': 50.0,
|
||||
'critical_max': 200.0
|
||||
})
|
||||
|
||||
self.range_pregnant = self.Range.create({
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'name': 'Embarazada',
|
||||
'gender': 'female',
|
||||
'pregnant': True,
|
||||
'normal_min': 60.0,
|
||||
'normal_max': 95.0,
|
||||
'critical_min': 45.0,
|
||||
'critical_max': 180.0
|
||||
})
|
||||
|
||||
# Crear análisis de prueba
|
||||
self.analysis = self.Product.create({
|
||||
'name': 'Glucosa en Sangre Test',
|
||||
'type': 'service',
|
||||
'is_analysis': True,
|
||||
'categ_id': self.env.ref('lims_management.product_category_clinical_analysis').id,
|
||||
})
|
||||
|
||||
# Configurar parámetro en el análisis
|
||||
self.env['product.template.parameter'].create({
|
||||
'product_tmpl_id': self.analysis.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'sequence': 10
|
||||
})
|
||||
|
||||
def test_result_parameter_assignment(self):
|
||||
"""Test asignación de parámetro a resultado"""
|
||||
# Crear test
|
||||
test = self.Test.create({
|
||||
'patient_id': self.patient_male.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
# Crear resultado
|
||||
result = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'value_numeric': 85.0
|
||||
})
|
||||
|
||||
self.assertEqual(result.parameter_id, self.param_glucose)
|
||||
self.assertEqual(result.value_type, 'numeric')
|
||||
self.assertEqual(result.unit, 'mg/dL')
|
||||
|
||||
def test_applicable_range_selection(self):
|
||||
"""Test selección automática de rango aplicable"""
|
||||
# Test para paciente masculino
|
||||
test_male = self.Test.create({
|
||||
'patient_id': self.patient_male.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
result_male = self.Result.create({
|
||||
'test_id': test_male.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'value_numeric': 85.0
|
||||
})
|
||||
|
||||
# Debe usar el rango general
|
||||
self.assertEqual(result_male.applicable_range_id, self.range_general)
|
||||
self.assertFalse(result_male.is_out_of_range)
|
||||
self.assertFalse(result_male.is_critical)
|
||||
|
||||
# Test para paciente embarazada
|
||||
test_pregnant = self.Test.create({
|
||||
'patient_id': self.patient_female_pregnant.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
result_pregnant = self.Result.create({
|
||||
'test_id': test_pregnant.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'value_numeric': 98.0 # Fuera de rango para embarazada
|
||||
})
|
||||
|
||||
# Debe usar el rango para embarazadas
|
||||
self.assertEqual(result_pregnant.applicable_range_id, self.range_pregnant)
|
||||
self.assertTrue(result_pregnant.is_out_of_range)
|
||||
self.assertFalse(result_pregnant.is_critical)
|
||||
|
||||
def test_out_of_range_detection(self):
|
||||
"""Test detección de valores fuera de rango"""
|
||||
test = self.Test.create({
|
||||
'patient_id': self.patient_male.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
# Valor normal
|
||||
result_normal = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'value_numeric': 85.0
|
||||
})
|
||||
self.assertFalse(result_normal.is_out_of_range)
|
||||
self.assertFalse(result_normal.is_critical)
|
||||
|
||||
# Valor alto pero no crítico
|
||||
result_high = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'value_numeric': 115.0
|
||||
})
|
||||
self.assertTrue(result_high.is_out_of_range)
|
||||
self.assertFalse(result_high.is_critical)
|
||||
|
||||
# Valor crítico alto
|
||||
result_critical = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'value_numeric': 250.0
|
||||
})
|
||||
self.assertTrue(result_critical.is_out_of_range)
|
||||
self.assertTrue(result_critical.is_critical)
|
||||
|
||||
def test_selection_parameter_result(self):
|
||||
"""Test resultado con parámetro de selección"""
|
||||
# Crear parámetro de selección
|
||||
param_culture = self.Parameter.create({
|
||||
'code': 'CULT_TEST',
|
||||
'name': 'Cultivo Test',
|
||||
'value_type': 'selection',
|
||||
'selection_values': 'Negativo,Positivo'
|
||||
})
|
||||
|
||||
test = self.Test.create({
|
||||
'patient_id': self.patient_male.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
result = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': param_culture.id,
|
||||
'value_selection': 'Positivo'
|
||||
})
|
||||
|
||||
self.assertEqual(result.value_type, 'selection')
|
||||
self.assertEqual(result.value_selection, 'Positivo')
|
||||
self.assertFalse(result.applicable_range_id) # Selection no tiene rangos
|
||||
|
||||
def test_text_parameter_result(self):
|
||||
"""Test resultado con parámetro de texto"""
|
||||
param_observation = self.Parameter.create({
|
||||
'code': 'OBS_TEST',
|
||||
'name': 'Observación Test',
|
||||
'value_type': 'text'
|
||||
})
|
||||
|
||||
test = self.Test.create({
|
||||
'patient_id': self.patient_male.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
result = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': param_observation.id,
|
||||
'value_text': 'Muestra hemolizada levemente'
|
||||
})
|
||||
|
||||
self.assertEqual(result.value_type, 'text')
|
||||
self.assertEqual(result.value_text, 'Muestra hemolizada levemente')
|
||||
|
||||
def test_boolean_parameter_result(self):
|
||||
"""Test resultado con parámetro booleano"""
|
||||
param_pregnancy = self.Parameter.create({
|
||||
'code': 'PREG_TEST',
|
||||
'name': 'Embarazo Test',
|
||||
'value_type': 'boolean'
|
||||
})
|
||||
|
||||
test = self.Test.create({
|
||||
'patient_id': self.patient_female_pregnant.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
result = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': param_pregnancy.id,
|
||||
'value_boolean': True
|
||||
})
|
||||
|
||||
self.assertEqual(result.value_type, 'boolean')
|
||||
self.assertTrue(result.value_boolean)
|
||||
|
||||
def test_formatted_value_display(self):
|
||||
"""Test formato de visualización de valores"""
|
||||
test = self.Test.create({
|
||||
'patient_id': self.patient_male.id,
|
||||
'product_id': self.analysis.product_variant_id.id,
|
||||
'state': 'draft'
|
||||
})
|
||||
|
||||
# Valor numérico
|
||||
result_numeric = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': self.param_glucose.id,
|
||||
'value_numeric': 85.5
|
||||
})
|
||||
self.assertEqual(result_numeric.formatted_value, '85.5 mg/dL')
|
||||
|
||||
# Valor de selección
|
||||
param_selection = self.Parameter.create({
|
||||
'code': 'SEL_FORMAT',
|
||||
'name': 'Selection Format',
|
||||
'value_type': 'selection',
|
||||
'selection_values': 'Opción A,Opción B'
|
||||
})
|
||||
|
||||
result_selection = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': param_selection.id,
|
||||
'value_selection': 'Opción A'
|
||||
})
|
||||
self.assertEqual(result_selection.formatted_value, 'Opción A')
|
||||
|
||||
# Valor booleano
|
||||
param_bool = self.Parameter.create({
|
||||
'code': 'BOOL_FORMAT',
|
||||
'name': 'Boolean Format',
|
||||
'value_type': 'boolean'
|
||||
})
|
||||
|
||||
result_bool = self.Result.create({
|
||||
'test_id': test.id,
|
||||
'parameter_id': param_bool.id,
|
||||
'value_boolean': True
|
||||
})
|
||||
self.assertEqual(result_bool.formatted_value, 'Sí')
|
186
test/run_parameter_tests.py
Normal file
186
test/run_parameter_tests.py
Normal file
|
@ -0,0 +1,186 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Script para ejecutar los tests del catálogo de parámetros
|
||||
"""
|
||||
|
||||
import odoo
|
||||
import logging
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tests import tagged
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def run_parameter_catalog_tests(db_name='lims_demo'):
|
||||
"""Ejecuta todos los tests del catálogo de parámetros"""
|
||||
|
||||
print("\n" + "="*70)
|
||||
print("EJECUTANDO TESTS DEL CATÁLOGO DE PARÁMETROS")
|
||||
print("="*70 + "\n")
|
||||
|
||||
# Importar los tests
|
||||
try:
|
||||
from odoo.addons.lims_management.tests import (
|
||||
test_analysis_parameter,
|
||||
test_parameter_range,
|
||||
test_result_parameter_integration,
|
||||
test_auto_result_generation
|
||||
)
|
||||
print("✓ Tests importados correctamente\n")
|
||||
except ImportError as e:
|
||||
print(f"✗ Error importando tests: {e}")
|
||||
return
|
||||
|
||||
# Lista de clases de test a ejecutar
|
||||
test_classes = [
|
||||
(test_analysis_parameter.TestAnalysisParameter, "Parámetros de Análisis"),
|
||||
(test_parameter_range.TestParameterRange, "Rangos de Referencia"),
|
||||
(test_result_parameter_integration.TestResultParameterIntegration, "Integración Resultados-Parámetros"),
|
||||
(test_auto_result_generation.TestAutoResultGeneration, "Generación Automática de Resultados"),
|
||||
]
|
||||
|
||||
# Conectar a la base de datos
|
||||
registry = odoo.registry(db_name)
|
||||
|
||||
# Ejecutar cada conjunto de tests
|
||||
total_tests = 0
|
||||
passed_tests = 0
|
||||
failed_tests = 0
|
||||
|
||||
for test_class, test_name in test_classes:
|
||||
print(f"\n--- Ejecutando tests de {test_name} ---")
|
||||
|
||||
with registry.cursor() as cr:
|
||||
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||
|
||||
# Crear instancia del test
|
||||
test_instance = test_class()
|
||||
test_instance.env = env
|
||||
test_instance.cr = cr
|
||||
test_instance.uid = odoo.SUPERUSER_ID
|
||||
|
||||
# Obtener todos los métodos de test
|
||||
test_methods = [method for method in dir(test_instance)
|
||||
if method.startswith('test_')]
|
||||
|
||||
for method_name in test_methods:
|
||||
total_tests += 1
|
||||
try:
|
||||
# Ejecutar setUp
|
||||
test_instance.setUp()
|
||||
|
||||
# Ejecutar el test
|
||||
method = getattr(test_instance, method_name)
|
||||
method()
|
||||
|
||||
print(f" ✓ {method_name}")
|
||||
passed_tests += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ {method_name}: {str(e)}")
|
||||
failed_tests += 1
|
||||
_logger.exception(f"Test failed: {method_name}")
|
||||
|
||||
finally:
|
||||
# Rollback para no afectar otros tests
|
||||
cr.rollback()
|
||||
|
||||
# Resumen final
|
||||
print("\n" + "="*70)
|
||||
print("RESUMEN DE TESTS")
|
||||
print("="*70)
|
||||
print(f"Total de tests ejecutados: {total_tests}")
|
||||
print(f"✓ Tests exitosos: {passed_tests}")
|
||||
print(f"✗ Tests fallidos: {failed_tests}")
|
||||
|
||||
if failed_tests == 0:
|
||||
print("\n✅ TODOS LOS TESTS PASARON EXITOSAMENTE")
|
||||
else:
|
||||
print(f"\n⚠️ {failed_tests} TESTS FALLARON")
|
||||
|
||||
return failed_tests == 0
|
||||
|
||||
|
||||
def run_specific_test(db_name='lims_demo', test_module=None, test_method=None):
|
||||
"""Ejecuta un test específico para debugging"""
|
||||
|
||||
if not test_module:
|
||||
print("Debe especificar el módulo de test")
|
||||
return
|
||||
|
||||
print(f"\nEjecutando test específico: {test_module}")
|
||||
if test_method:
|
||||
print(f"Método: {test_method}")
|
||||
|
||||
# Importar el módulo de test
|
||||
exec(f"from odoo.addons.lims_management.tests import {test_module}")
|
||||
module = eval(test_module)
|
||||
|
||||
# Encontrar la clase de test
|
||||
test_class = None
|
||||
for item in dir(module):
|
||||
obj = getattr(module, item)
|
||||
if isinstance(obj, type) and issubclass(obj, TransactionCase) and obj != TransactionCase:
|
||||
test_class = obj
|
||||
break
|
||||
|
||||
if not test_class:
|
||||
print("No se encontró clase de test en el módulo")
|
||||
return
|
||||
|
||||
registry = odoo.registry(db_name)
|
||||
|
||||
with registry.cursor() as cr:
|
||||
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||
|
||||
test_instance = test_class()
|
||||
test_instance.env = env
|
||||
test_instance.cr = cr
|
||||
test_instance.uid = odoo.SUPERUSER_ID
|
||||
|
||||
if test_method:
|
||||
# Ejecutar método específico
|
||||
if hasattr(test_instance, test_method):
|
||||
try:
|
||||
test_instance.setUp()
|
||||
method = getattr(test_instance, test_method)
|
||||
method()
|
||||
print(f"✓ Test {test_method} pasó exitosamente")
|
||||
except Exception as e:
|
||||
print(f"✗ Test {test_method} falló: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print(f"Método {test_method} no encontrado")
|
||||
else:
|
||||
# Ejecutar todos los métodos del módulo
|
||||
test_methods = [m for m in dir(test_instance) if m.startswith('test_')]
|
||||
for method_name in test_methods:
|
||||
try:
|
||||
test_instance.setUp()
|
||||
method = getattr(test_instance, method_name)
|
||||
method()
|
||||
print(f"✓ {method_name}")
|
||||
except Exception as e:
|
||||
print(f"✗ {method_name}: {str(e)}")
|
||||
finally:
|
||||
cr.rollback()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
# Verificar argumentos
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] == '--specific':
|
||||
# Modo de test específico
|
||||
module = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
method = sys.argv[3] if len(sys.argv) > 3 else None
|
||||
run_specific_test(test_module=module, test_method=method)
|
||||
else:
|
||||
# Usar base de datos especificada
|
||||
run_parameter_catalog_tests(db_name=sys.argv[1])
|
||||
else:
|
||||
# Ejecutar todos los tests con DB por defecto
|
||||
success = run_parameter_catalog_tests()
|
||||
sys.exit(0 if success else 1)
|
221
test/test_parameters_simple.py
Normal file
221
test/test_parameters_simple.py
Normal file
|
@ -0,0 +1,221 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Script simplificado para probar el catálogo de parámetros
|
||||
"""
|
||||
|
||||
import odoo
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def test_parameter_catalog(cr):
|
||||
"""Prueba el funcionamiento del catálogo de parámetros"""
|
||||
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||
|
||||
# Limpiar parámetros de test anteriores
|
||||
test_params = env['lims.analysis.parameter'].search([
|
||||
('code', 'like', 'TEST_%')
|
||||
])
|
||||
if test_params:
|
||||
print(f"Limpiando {len(test_params)} parámetros de test anteriores...")
|
||||
test_params.unlink()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("TEST: CATÁLOGO DE PARÁMETROS")
|
||||
print("="*60 + "\n")
|
||||
|
||||
# Test 1: Crear parámetro numérico
|
||||
print("1. Creando parámetro numérico...")
|
||||
try:
|
||||
param_numeric = env['lims.analysis.parameter'].create({
|
||||
'code': 'TEST_NUM_001',
|
||||
'name': 'Test Numérico',
|
||||
'value_type': 'numeric',
|
||||
'unit': 'mg/dL',
|
||||
'description': 'Parámetro de prueba numérico'
|
||||
})
|
||||
print(f" ✓ Parámetro creado: {param_numeric.name} ({param_numeric.code})")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
# Test 2: Validación - parámetro numérico sin unidad
|
||||
print("\n2. Validando requerimiento de unidad...")
|
||||
try:
|
||||
env['lims.analysis.parameter'].create({
|
||||
'code': 'TEST_NUM_002',
|
||||
'name': 'Test Sin Unidad',
|
||||
'value_type': 'numeric',
|
||||
# Sin unit - debe fallar
|
||||
})
|
||||
print(" ✗ Error: Se permitió crear parámetro numérico sin unidad")
|
||||
return False
|
||||
except Exception as e:
|
||||
if 'unidad de medida' in str(e):
|
||||
print(" ✓ Validación correcta: Se requiere unidad para parámetros numéricos")
|
||||
else:
|
||||
print(f" ✗ Error inesperado: {e}")
|
||||
return False
|
||||
|
||||
# Test 3: Crear parámetro de selección
|
||||
print("\n3. Creando parámetro de selección...")
|
||||
try:
|
||||
param_selection = env['lims.analysis.parameter'].create({
|
||||
'code': 'TEST_SEL_001',
|
||||
'name': 'Test Selección',
|
||||
'value_type': 'selection',
|
||||
'selection_values': 'Positivo,Negativo,Indeterminado'
|
||||
})
|
||||
print(f" ✓ Parámetro de selección creado con valores: {param_selection.selection_values}")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
# Test 4: Crear rango de referencia
|
||||
print("\n4. Creando rangos de referencia...")
|
||||
try:
|
||||
range_general = env['lims.parameter.range'].create({
|
||||
'parameter_id': param_numeric.id,
|
||||
'name': 'Rango General',
|
||||
'normal_min': 70.0,
|
||||
'normal_max': 100.0,
|
||||
'critical_min': 50.0,
|
||||
'critical_max': 200.0
|
||||
})
|
||||
print(f" ✓ Rango general creado: {range_general.normal_min} - {range_general.normal_max}")
|
||||
|
||||
range_male = env['lims.parameter.range'].create({
|
||||
'parameter_id': param_numeric.id,
|
||||
'name': 'Hombre Adulto',
|
||||
'gender': 'male',
|
||||
'age_min': 18,
|
||||
'age_max': 65,
|
||||
'normal_min': 75.0,
|
||||
'normal_max': 105.0
|
||||
})
|
||||
print(f" ✓ Rango específico creado: Hombre {range_male.age_min}-{range_male.age_max} años")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
# Test 5: Configurar parámetro en análisis
|
||||
print("\n5. Configurando parámetros en análisis...")
|
||||
try:
|
||||
# Obtener un análisis existente
|
||||
analysis = env['product.template'].search([
|
||||
('is_analysis', '=', True)
|
||||
], limit=1)
|
||||
|
||||
if not analysis:
|
||||
print(" ⚠️ No se encontraron análisis para configurar")
|
||||
else:
|
||||
config = env['product.template.parameter'].create({
|
||||
'product_tmpl_id': analysis.id,
|
||||
'parameter_id': param_numeric.id,
|
||||
'sequence': 999
|
||||
})
|
||||
print(f" ✓ Parámetro configurado en análisis: {analysis.name}")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
# Test 6: Generación automática de resultados
|
||||
print("\n6. Probando generación automática de resultados...")
|
||||
try:
|
||||
# Buscar una prueba existente
|
||||
test = env['lims.test'].search([
|
||||
('state', '=', 'draft')
|
||||
], limit=1)
|
||||
|
||||
if test and analysis:
|
||||
# Cambiar el producto de la prueba para trigger la regeneración
|
||||
original_product = test.product_id
|
||||
test.product_id = analysis.product_variant_id.id
|
||||
|
||||
# Verificar que se generó el resultado
|
||||
result = test.result_ids.filtered(lambda r: r.parameter_id == param_numeric)
|
||||
if result:
|
||||
print(f" ✓ Resultado generado automáticamente para parámetro: {param_numeric.name}")
|
||||
else:
|
||||
print(" ⚠️ No se generó resultado automático")
|
||||
|
||||
# Restaurar producto original
|
||||
test.product_id = original_product.id
|
||||
else:
|
||||
print(" ⚠️ No se encontraron pruebas en borrador para probar")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
# Test 7: Verificar datos demo cargados
|
||||
print("\n7. Verificando datos demo del catálogo...")
|
||||
try:
|
||||
param_count = env['lims.analysis.parameter'].search_count([])
|
||||
range_count = env['lims.parameter.range'].search_count([])
|
||||
config_count = env['product.template.parameter'].search_count([])
|
||||
|
||||
print(f" - Parámetros totales: {param_count}")
|
||||
print(f" - Rangos de referencia: {range_count}")
|
||||
print(f" - Configuraciones parámetro-análisis: {config_count}")
|
||||
|
||||
# Verificar algunos parámetros específicos
|
||||
hemoglobin = env.ref('lims_management.param_hemoglobin', raise_if_not_found=False)
|
||||
if hemoglobin:
|
||||
print(f" ✓ Parámetro demo encontrado: {hemoglobin.display_name}")
|
||||
print(f" - Rangos asociados: {len(hemoglobin.range_ids)}")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
# Test 8: Buscar rango aplicable
|
||||
print("\n8. Probando búsqueda de rango aplicable...")
|
||||
try:
|
||||
# Crear paciente de prueba
|
||||
patient = env['res.partner'].create({
|
||||
'name': 'Paciente Test Rango',
|
||||
'is_patient': True,
|
||||
'gender': 'male',
|
||||
'birthdate_date': '1990-01-01' # 34 años aprox
|
||||
})
|
||||
|
||||
# Buscar rango aplicable
|
||||
Range = env['lims.parameter.range']
|
||||
applicable = Range._find_applicable_range(
|
||||
param_numeric.id,
|
||||
gender='male',
|
||||
age=34,
|
||||
is_pregnant=False
|
||||
)
|
||||
|
||||
if applicable:
|
||||
print(f" ✓ Rango aplicable encontrado: {applicable.name}")
|
||||
print(f" - Valores normales: {applicable.normal_min} - {applicable.normal_max}")
|
||||
else:
|
||||
print(" ⚠️ No se encontró rango aplicable")
|
||||
|
||||
# Limpiar
|
||||
patient.unlink()
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("✅ TODOS LOS TESTS PASARON EXITOSAMENTE")
|
||||
print("="*60)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
db_name = 'lims_demo'
|
||||
registry = odoo.registry(db_name)
|
||||
|
||||
with registry.cursor() as cr:
|
||||
try:
|
||||
success = test_parameter_catalog(cr)
|
||||
if not success:
|
||||
print("\n⚠️ ALGUNOS TESTS FALLARON")
|
||||
except Exception as e:
|
||||
print(f"\n✗ Error crítico: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
Loading…
Reference in New Issue
Block a user