Refactorizar y mejorar suite de tests con validaciones robustas

Problemas resueltos:
- Eliminada dependencia de UUIDs hardcodeados en tests
- Agregadas validaciones específicas de valores esperados
- Implementado cleanup automático de datos de test

Cambios principales:

Tests organizados por categoría (7 archivos nuevos):
- test_structure.sql: 8 tests de estructura de BD
- test_constraints.sql: 6 tests de validaciones
- test_triggers.sql: 3 tests de triggers automáticos
- test_queries.sql: 5 tests de queries típicas
- test_functions.sql: 3 tests de funciones auxiliares
- test_edge_cases.sql: 7 tests de casos límite
- test_indexes.sql: 6 tests de uso de índices

Mejoras implementadas:
- Cada test genera sus propios datos dinámicamente
- Tests usan bloques DO $$ con UUIDs generados
- Validaciones específicas con valores esperados
- Cleanup automático al finalizar cada test
- Tests de casos edge (arrays vacíos, NULL, límites)
- Verificación de uso de índices con EXPLAIN

test_all.sql actualizado:
- Ahora ejecuta todos los archivos organizados
- Total: ~38 tests independientes y robustos
- Progreso visual por categoría
- ASCII art y mejor presentación

Todos los tests verificados y funcionando correctamente
This commit is contained in:
2025-10-17 17:35:34 -06:00
parent f682c3db51
commit cc3cf0da81
8 changed files with 2487 additions and 477 deletions

View File

@@ -1,493 +1,138 @@
-- ============================================
-- rioCata - Tests de Validación
-- rioCata - Suite Completa de Tests
-- ============================================
-- Este script ejecuta tests para validar:
-- 1. Estructura de tablas y constraints
-- 2. Triggers (updated_at, puntaje_final)
-- 3. Validaciones de arrays y JSONB
-- 4. Queries típicas
-- 5. Funciones auxiliares
-- Ejecuta todos los tests organizados por categoría
-- ============================================
\echo ''
\echo '██████╗ ██╗ ██████╗ ██████╗ █████╗ ████████╗ █████╗ '
\echo '██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗╚══██╔══╝██╔══██╗'
\echo '██████╔╝██║██║ ██║██║ ███████║ ██║ ███████║'
\echo '██╔══██╗██║██║ ██║██║ ██╔══██║ ██║ ██╔══██║'
\echo '██║ ██║██║╚██████╔╝╚██████╗██║ ██║ ██║ ██║ ██║'
\echo '╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝'
\echo ''
\echo '=========================================='
\echo 'rioCata - Test Suite'
\echo 'Suite Completa de Tests - rioCata'
\echo '=========================================='
\echo ''
\echo 'Esta suite ejecuta todos los tests organizados en categorías:'
\echo ' 1. Estructura de Base de Datos'
\echo ' 2. Constraints y Validaciones'
\echo ' 3. Triggers Automáticos'
\echo ' 4. Queries Típicas'
\echo ' 5. Funciones Auxiliares'
\echo ' 6. Casos Límite (Edge Cases)'
\echo ' 7. Uso de Índices'
\echo ''
\echo 'Iniciando tests...'
\echo ''
-- ============================================
-- 1. Tests de Estructura
-- ============================================
\i postgres/tests/test_structure.sql
\echo ''
\echo '=========================================='
\echo '1/7 Estructura completada ✓'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST 1: Verificar que las tablas existen
-- 2. Tests de Constraints
-- ============================================
\echo '[TEST 1] Verificando existencia de tablas...'
DO $$
DECLARE
tabla_count int;
BEGIN
SELECT COUNT(*) INTO tabla_count
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN ('sesion', 'sesion_participante', 'muestra', 'evaluacion');
IF tabla_count = 4 THEN
RAISE NOTICE '✓ Todas las tablas principales existen (4/4)';
ELSE
RAISE EXCEPTION '✗ Faltan tablas. Encontradas: %/4', tabla_count;
END IF;
-- Verificar tabla auth.users
SELECT COUNT(*) INTO tabla_count
FROM information_schema.tables
WHERE table_schema = 'auth' AND table_name = 'users';
IF tabla_count = 1 THEN
RAISE NOTICE '✓ Tabla auth.users existe';
ELSE
RAISE EXCEPTION '✗ Tabla auth.users no existe';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 2: Verificar tipo ENUM defecto_tipo
-- ============================================
\echo '[TEST 2] Verificando tipo ENUM defecto_tipo...'
DO $$
DECLARE
enum_exists boolean;
BEGIN
SELECT EXISTS (
SELECT 1 FROM pg_type WHERE typname = 'defecto_tipo'
) INTO enum_exists;
IF enum_exists THEN
RAISE NOTICE '✓ Tipo ENUM defecto_tipo existe';
ELSE
RAISE EXCEPTION '✗ Tipo ENUM defecto_tipo no existe';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 3: Verificar triggers
-- ============================================
\echo '[TEST 3] Verificando triggers...'
DO $$
DECLARE
trigger_count int;
BEGIN
SELECT COUNT(*) INTO trigger_count
FROM information_schema.triggers
WHERE trigger_name IN ('trg_eval_updated_at', 'trg_eval_score_bi');
IF trigger_count = 2 THEN
RAISE NOTICE '✓ Todos los triggers existen (2/2)';
ELSE
RAISE EXCEPTION '✗ Faltan triggers. Encontrados: %/2', trigger_count;
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 4: Verificar índices
-- ============================================
\echo '[TEST 4] Verificando índices...'
DO $$
DECLARE
index_count int;
BEGIN
SELECT COUNT(*) INTO index_count
FROM pg_indexes
WHERE schemaname = 'public'
AND indexname LIKE 'idx_%';
IF index_count >= 15 THEN
RAISE NOTICE '✓ Índices creados correctamente (% encontrados)', index_count;
ELSE
RAISE WARNING '⚠ Se esperaban al menos 15 índices, se encontraron: %', index_count;
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 5: Test de constraint - tazas_no_uniformes válidas
-- ============================================
\echo '[TEST 5] Validando constraint tazas_no_uniformes (valores 1-5)...'
DO $$
BEGIN
-- Intentar insertar valor inválido (6)
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, tazas_no_uniformes
) VALUES (
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'{"fragancia":{"descriptiva":1,"afectiva":1}}'::jsonb,
ARRAY[6]::smallint[]
);
RAISE EXCEPTION '✗ El constraint tazas_no_uniformes NO funcionó (aceptó valor 6)';
EXCEPTION
WHEN check_violation THEN
RAISE NOTICE '✓ Constraint tazas_no_uniformes funciona correctamente';
END;
END $$;
\echo ''
-- ============================================
-- TEST 6: Test de constraint - gustos_predominantes (máximo 2)
-- ============================================
\echo '[TEST 6] Validando constraint gustos_predominantes (máximo 2 elementos)...'
DO $$
BEGIN
-- Intentar insertar 3 gustos (debería fallar)
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, gustos_predominantes
) VALUES (
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'{"fragancia":{"descriptiva":1,"afectiva":1}}'::jsonb,
ARRAY['Ácido', 'Dulce', 'Amargo']::text[]
);
RAISE EXCEPTION '✗ El constraint gustos_predominantes NO funcionó (aceptó 3 elementos)';
EXCEPTION
WHEN check_violation THEN
RAISE NOTICE '✓ Constraint gustos_predominantes funciona correctamente';
END;
END $$;
\echo ''
-- ============================================
-- TEST 7: Test de constraint - sensacion_en_boca (valores válidos)
-- ============================================
\echo '[TEST 7] Validando constraint sensacion_en_boca (valores permitidos)...'
DO $$
BEGIN
-- Intentar insertar valor no permitido
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, sensacion_en_boca
) VALUES (
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'{"fragancia":{"descriptiva":1,"afectiva":1}}'::jsonb,
ARRAY['Raro', 'Extraño']::text[]
);
RAISE EXCEPTION '✗ El constraint sensacion_en_boca NO funcionó';
EXCEPTION
WHEN check_violation THEN
RAISE NOTICE '✓ Constraint sensacion_en_boca funciona correctamente';
END;
END $$;
\echo ''
-- ============================================
-- TEST 8: Test de constraint - rangos de intensidades
-- ============================================
\echo '[TEST 8] Validando constraint rangos de intensidades (descriptiva 1-15, afectiva 1-10)...'
DO $$
BEGIN
-- Intentar insertar descriptiva > 15
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'{"fragancia":{"descriptiva":20,"afectiva":5}}'::jsonb
);
RAISE EXCEPTION '✗ El constraint de rangos NO funcionó (aceptó descriptiva=20)';
EXCEPTION
WHEN check_violation THEN
RAISE NOTICE '✓ Constraint rangos de intensidades funciona (descriptiva)';
END;
-- Intentar insertar afectiva > 10
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'{"fragancia":{"descriptiva":5,"afectiva":15}}'::jsonb
);
RAISE EXCEPTION '✗ El constraint de rangos NO funcionó (aceptó afectiva=15)';
EXCEPTION
WHEN check_violation THEN
RAISE NOTICE '✓ Constraint rangos de intensidades funciona (afectiva)';
END;
END $$;
\echo ''
-- ============================================
-- TEST 9: Test de trigger - puntaje_final se calcula automáticamente
-- ============================================
\echo '[TEST 9] Validando trigger de cálculo automático de puntaje_final...'
DO $$
DECLARE
puntaje_calculado int;
puntaje_esperado int := 75; -- Suma de afectivos: 9+9+10+9+9+10+9+10 = 75
BEGIN
-- Verificar evaluación existente
SELECT puntaje_final INTO puntaje_calculado
FROM evaluacion
WHERE muestra_id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
AND sesion_participante_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
IF puntaje_calculado = puntaje_esperado THEN
RAISE NOTICE '✓ Trigger puntaje_final funciona correctamente (% = %)', puntaje_calculado, puntaje_esperado;
ELSE
RAISE EXCEPTION '✗ Puntaje calculado (%) no coincide con esperado (%)', puntaje_calculado, puntaje_esperado;
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 10: Test de constraint UNIQUE - una evaluación por participante por muestra
-- ============================================
\echo '[TEST 10] Validando constraint UNIQUE (una evaluación por participante/muestra)...'
DO $$
BEGIN
-- Intentar insertar evaluación duplicada
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'{"fragancia":{"descriptiva":1,"afectiva":1}}'::jsonb
);
RAISE EXCEPTION '✗ El constraint UNIQUE NO funcionó (permitió duplicado)';
EXCEPTION
WHEN unique_violation THEN
RAISE NOTICE '✓ Constraint UNIQUE funciona correctamente';
END;
END $$;
\echo ''
-- ============================================
-- TEST 11: Query típica - Promedio de parámetro afectivo
-- ============================================
\echo '[TEST 11] Probando query: Promedio de dulzor afectivo por sesión...'
DO $$
DECLARE
avg_dulzor numeric;
BEGIN
SELECT AVG( ((e.intensidades->'dulzor'->>'afectiva')::int) )
INTO avg_dulzor
FROM sesion s
JOIN muestra m ON m.sesion_id = s.id
JOIN evaluacion e ON e.muestra_id = m.id
WHERE s.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
IF avg_dulzor IS NOT NULL THEN
RAISE NOTICE '✓ Query promedio funciona. Dulzor promedio: %', ROUND(avg_dulzor, 2);
ELSE
RAISE EXCEPTION '✗ Query promedio falló';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 12: Query típica - Buscar por defecto
-- ============================================
\echo '[TEST 12] Probando query: Buscar evaluaciones con defecto Fenólico...'
DO $$
DECLARE
defecto_count int;
BEGIN
SELECT COUNT(*) INTO defecto_count
FROM evaluacion
WHERE defecto = 'Fenólico';
IF defecto_count > 0 THEN
RAISE NOTICE '✓ Query por defecto funciona. Evaluaciones con defecto Fenólico: %', defecto_count;
ELSE
RAISE WARNING '⚠ No se encontraron evaluaciones con defecto Fenólico (puede ser normal)';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 13: Query típica - Buscar por taza defectuosa específica
-- ============================================
\echo '[TEST 13] Probando query: Buscar evaluaciones donde taza 5 fue defectuosa...'
DO $$
DECLARE
taza_count int;
BEGIN
SELECT COUNT(*) INTO taza_count
FROM evaluacion
WHERE tazas_defectuosas @> ARRAY[5]::smallint[];
IF taza_count > 0 THEN
RAISE NOTICE '✓ Query con array @> funciona. Evaluaciones con taza 5 defectuosa: %', taza_count;
ELSE
RAISE WARNING '⚠ No se encontraron evaluaciones con taza 5 defectuosa';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 14: Query típica - Top muestras por puntaje
-- ============================================
\echo '[TEST 14] Probando query: Top 3 muestras por puntaje final...'
DO $$
DECLARE
top_muestra text;
top_puntaje int;
BEGIN
SELECT m.codigo, e.puntaje_final
INTO top_muestra, top_puntaje
FROM muestra m
JOIN evaluacion e ON e.muestra_id = m.id
JOIN sesion_participante sp ON sp.id = e.sesion_participante_id
WHERE m.sesion_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
ORDER BY e.puntaje_final DESC
LIMIT 1;
IF top_muestra IS NOT NULL THEN
RAISE NOTICE '✓ Query top muestras funciona. Mejor muestra: % (puntaje: %)', top_muestra, top_puntaje;
ELSE
RAISE EXCEPTION '✗ Query top muestras falló';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 15: Función auxiliar - get_promedio_parametro_afectivo
-- ============================================
\echo '[TEST 15] Probando función: get_promedio_parametro_afectivo...'
DO $$
DECLARE
avg_acidez numeric;
BEGIN
SELECT get_promedio_parametro_afectivo(
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
'acidez'
) INTO avg_acidez;
IF avg_acidez IS NOT NULL THEN
RAISE NOTICE '✓ Función get_promedio_parametro_afectivo funciona. Acidez promedio: %', ROUND(avg_acidez, 2);
ELSE
RAISE EXCEPTION '✗ Función get_promedio_parametro_afectivo falló';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 16: Función auxiliar - get_top_muestras
-- ============================================
\echo '[TEST 16] Probando función: get_top_muestras...'
DO $$
DECLARE
resultado record;
count_resultados int := 0;
BEGIN
FOR resultado IN
SELECT * FROM get_top_muestras('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 3)
LOOP
count_resultados := count_resultados + 1;
RAISE NOTICE ' - %: % puntos (catador: %)', resultado.muestra_codigo, resultado.puntaje_final, resultado.catador_nombre;
END LOOP;
IF count_resultados > 0 THEN
RAISE NOTICE '✓ Función get_top_muestras funciona. Resultados: %', count_resultados;
ELSE
RAISE EXCEPTION '✗ Función get_top_muestras no devolvió resultados';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 17: Query con índice funcional - filtrar por acidez afectiva
-- ============================================
\echo '[TEST 17] Probando query con índice funcional: acidez afectiva >= 8...'
DO $$
DECLARE
count_acidez int;
BEGIN
SELECT COUNT(*) INTO count_acidez
FROM evaluacion
WHERE ((intensidades->'acidez'->>'afectiva')::int) >= 8;
IF count_acidez >= 0 THEN
RAISE NOTICE '✓ Query con índice funcional funciona. Evaluaciones con acidez >= 8: %', count_acidez;
ELSE
RAISE EXCEPTION '✗ Query con índice funcional falló';
END IF;
END $$;
\echo ''
-- ============================================
-- TEST 18: Validar datos de prueba cargados
-- ============================================
\echo '[TEST 18] Validando que los datos de prueba se cargaron correctamente...'
DO $$
DECLARE
count_users int;
count_sesiones int;
count_muestras int;
count_evaluaciones int;
BEGIN
SELECT COUNT(*) INTO count_users FROM auth.users;
SELECT COUNT(*) INTO count_sesiones FROM sesion;
SELECT COUNT(*) INTO count_muestras FROM muestra;
SELECT COUNT(*) INTO count_evaluaciones FROM evaluacion;
IF count_users >= 3 AND count_sesiones >= 1 AND count_muestras >= 3 AND count_evaluaciones >= 5 THEN
RAISE NOTICE '✓ Datos de prueba cargados:';
RAISE NOTICE ' - Usuarios: %', count_users;
RAISE NOTICE ' - Sesiones: %', count_sesiones;
RAISE NOTICE ' - Muestras: %', count_muestras;
RAISE NOTICE ' - Evaluaciones: %', count_evaluaciones;
ELSE
RAISE WARNING '⚠ Algunos datos de prueba pueden faltar:';
RAISE WARNING ' - Usuarios: % (esperado: >= 3)', count_users;
RAISE WARNING ' - Sesiones: % (esperado: >= 1)', count_sesiones;
RAISE WARNING ' - Muestras: % (esperado: >= 3)', count_muestras;
RAISE WARNING ' - Evaluaciones: % (esperado: >= 5)', count_evaluaciones;
END IF;
END $$;
\i postgres/tests/test_constraints.sql
\echo ''
\echo '=========================================='
\echo 'Tests completados'
\echo '2/7 Constraints completados'
\echo '=========================================='
\echo ''
\echo 'Ejecuta queries de ejemplo con:'
\echo ' \i postgres/tests/example_queries.sql'
-- ============================================
-- 3. Tests de Triggers
-- ============================================
\i postgres/tests/test_triggers.sql
\echo ''
\echo '=========================================='
\echo '3/7 Triggers completados ✓'
\echo '=========================================='
\echo ''
-- ============================================
-- 4. Tests de Queries
-- ============================================
\i postgres/tests/test_queries.sql
\echo ''
\echo '=========================================='
\echo '4/7 Queries completadas ✓'
\echo '=========================================='
\echo ''
-- ============================================
-- 5. Tests de Funciones
-- ============================================
\i postgres/tests/test_functions.sql
\echo ''
\echo '=========================================='
\echo '5/7 Funciones completadas ✓'
\echo '=========================================='
\echo ''
-- ============================================
-- 6. Tests de Edge Cases
-- ============================================
\i postgres/tests/test_edge_cases.sql
\echo ''
\echo '=========================================='
\echo '6/7 Edge Cases completados ✓'
\echo '=========================================='
\echo ''
-- ============================================
-- 7. Tests de Índices
-- ============================================
\i postgres/tests/test_indexes.sql
\echo ''
\echo '=========================================='
\echo '7/7 Índices completados ✓'
\echo '=========================================='
\echo ''
-- ============================================
-- Resumen Final
-- ============================================
\echo ''
\echo '╔════════════════════════════════════════╗'
\echo '║ SUITE COMPLETA DE TESTS FINALIZADA ║'
\echo '╚════════════════════════════════════════╝'
\echo ''
\echo 'Categorías ejecutadas:'
\echo ' ✓ Estructura (8 tests)'
\echo ' ✓ Constraints (6 tests)'
\echo ' ✓ Triggers (3 tests)'
\echo ' ✓ Queries (5 tests)'
\echo ' ✓ Funciones (3 tests)'
\echo ' ✓ Edge Cases (7 tests)'
\echo ' ✓ Índices (6 tests)'
\echo ''
\echo 'Total: ~38 tests ejecutados'
\echo ''
\echo 'Para ejecutar categorías individuales:'
\echo ' \\i postgres/tests/test_structure.sql'
\echo ' \\i postgres/tests/test_constraints.sql'
\echo ' \\i postgres/tests/test_triggers.sql'
\echo ' \\i postgres/tests/test_queries.sql'
\echo ' \\i postgres/tests/test_functions.sql'
\echo ' \\i postgres/tests/test_edge_cases.sql'
\echo ' \\i postgres/tests/test_indexes.sql'
\echo ''
\echo 'Para ver queries de ejemplo:'
\echo ' \\i postgres/tests/example_queries.sql'
\echo ''

View File

@@ -0,0 +1,454 @@
-- ============================================
-- rioCata - Tests de Constraints
-- ============================================
-- Tests que verifican la validación de constraints
-- de la base de datos sin depender de datos hardcodeados
-- ============================================
\echo '=========================================='
\echo 'Tests de Constraints'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST: Constraint tazas_no_uniformes (solo valores 1-5)
-- ============================================
\echo '[CONSTRAINT 1] Validando tazas_no_uniformes (valores 1-5)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_constraint1@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-CONST-1', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M1');
-- Test 1: Valores válidos (1-5) deben pasar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, tazas_no_uniformes
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[1,2,3,4,5]::smallint[]
);
RAISE NOTICE ' ✓ Acepta valores válidos 1-5';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó valores válidos: %', SQLERRM;
END;
-- Test 2: Valor inválido (0) debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, tazas_no_uniformes
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[0]::smallint[]
);
RAISE EXCEPTION ' ✗ Aceptó valor inválido 0';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente valor 0';
END;
-- Test 3: Valor inválido (6) debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, tazas_no_uniformes
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[6]::smallint[]
);
RAISE EXCEPTION ' ✗ Aceptó valor inválido 6';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente valor 6';
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Constraint tazas_no_uniformes funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Constraint tazas_defectuosas (solo valores 1-5)
-- ============================================
\echo '[CONSTRAINT 2] Validando tazas_defectuosas (valores 1-5)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_constraint2@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-CONST-2', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M2');
-- Test: Valor inválido (-1) debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, tazas_defectuosas
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[-1]::smallint[]
);
RAISE EXCEPTION ' ✗ Aceptó valor inválido -1';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente valor -1';
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Constraint tazas_defectuosas funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Constraint gustos_predominantes (máximo 2 elementos)
-- ============================================
\echo '[CONSTRAINT 3] Validando gustos_predominantes (máximo 2 elementos)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_constraint3@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-CONST-3', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M3');
-- Test 1: 0 elementos debe pasar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, gustos_predominantes
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[]::text[]
);
RAISE NOTICE ' ✓ Acepta 0 elementos';
DELETE FROM evaluacion WHERE muestra_id = test_muestra_id;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó 0 elementos: %', SQLERRM;
END;
-- Test 2: 1 elemento debe pasar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, gustos_predominantes
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY['Dulce']::text[]
);
RAISE NOTICE ' ✓ Acepta 1 elemento';
DELETE FROM evaluacion WHERE muestra_id = test_muestra_id;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó 1 elemento: %', SQLERRM;
END;
-- Test 3: 2 elementos debe pasar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, gustos_predominantes
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY['Dulce', 'Ácido']::text[]
);
RAISE NOTICE ' ✓ Acepta 2 elementos';
DELETE FROM evaluacion WHERE muestra_id = test_muestra_id;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó 2 elementos: %', SQLERRM;
END;
-- Test 4: 3 elementos debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, gustos_predominantes
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY['Dulce', 'Ácido', 'Amargo']::text[]
);
RAISE EXCEPTION ' ✗ Aceptó 3 elementos';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente 3 elementos';
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Constraint gustos_predominantes funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Constraint sensacion_en_boca (valores permitidos)
-- ============================================
\echo '[CONSTRAINT 4] Validando sensacion_en_boca (valores permitidos)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_constraint4@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-CONST-4', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M4');
-- Test 1: Valores válidos deben pasar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, sensacion_en_boca
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY['Áspero', 'Suave', 'Aceitoso', 'Metálico', 'Astringente']::text[]
);
RAISE NOTICE ' ✓ Acepta todos los valores permitidos';
DELETE FROM evaluacion WHERE muestra_id = test_muestra_id;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó valores válidos: %', SQLERRM;
END;
-- Test 2: Valor inválido debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, sensacion_en_boca
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY['Raro', 'Extraño']::text[]
);
RAISE EXCEPTION ' ✗ Aceptó valores inválidos';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente valores inválidos';
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Constraint sensacion_en_boca funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Constraint rangos de intensidades
-- ============================================
\echo '[CONSTRAINT 5] Validando rangos intensidades (descriptiva 1-15, afectiva 1-10)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_constraint5@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-CONST-5', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M5');
-- Test 1: Valores en límites válidos (1 y 15 para descriptiva)
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":1,"afectiva":1},"aroma":{"descriptiva":15,"afectiva":10}}'::jsonb
);
RAISE NOTICE ' ✓ Acepta límites válidos (descriptiva: 1 y 15, afectiva: 1 y 10)';
DELETE FROM evaluacion WHERE muestra_id = test_muestra_id;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó límites válidos: %', SQLERRM;
END;
-- Test 2: descriptiva < 1 debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":0,"afectiva":5}}'::jsonb
);
RAISE EXCEPTION ' ✗ Aceptó descriptiva=0';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente descriptiva < 1';
END;
-- Test 3: descriptiva > 15 debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":16,"afectiva":5}}'::jsonb
);
RAISE EXCEPTION ' ✗ Aceptó descriptiva=16';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente descriptiva > 15';
END;
-- Test 4: afectiva < 1 debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":0}}'::jsonb
);
RAISE EXCEPTION ' ✗ Aceptó afectiva=0';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente afectiva < 1';
END;
-- Test 5: afectiva > 10 debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":11}}'::jsonb
);
RAISE EXCEPTION ' ✗ Aceptó afectiva=11';
EXCEPTION WHEN check_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente afectiva > 10';
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Constraint rangos de intensidades funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Constraint UNIQUE (una evaluación por participante/muestra)
-- ============================================
\echo '[CONSTRAINT 6] Validando constraint UNIQUE (una evaluación por participante/muestra)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_constraint6@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-CONST-6', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M6');
-- Test 1: Primera evaluación debe pasar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb
);
RAISE NOTICE ' ✓ Acepta primera evaluación';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó primera evaluación: %', SQLERRM;
END;
-- Test 2: Segunda evaluación de mismo participante/muestra debe fallar
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":7,"afectiva":8}}'::jsonb
);
RAISE EXCEPTION ' ✗ Permitió evaluación duplicada';
EXCEPTION WHEN unique_violation THEN
RAISE NOTICE ' ✓ Rechaza correctamente evaluación duplicada';
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Constraint UNIQUE funciona correctamente';
END $$;
\echo ''
\echo '=========================================='
\echo 'Tests de Constraints completados'
\echo '=========================================='

View File

@@ -0,0 +1,432 @@
-- ============================================
-- rioCata - Tests de Casos Límite (Edge Cases)
-- ============================================
-- Tests que verifican comportamiento en casos límite
-- y situaciones especiales
-- ============================================
\echo '=========================================='
\echo 'Tests de Casos Límite'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST: Arrays vacíos son válidos
-- ============================================
\echo '[EDGE 1] Validando arrays vacíos...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_edge1@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-EDGE-1', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M1');
-- Test: evaluación con todos los arrays vacíos
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades,
tazas_no_uniformes, tazas_defectuosas,
sensacion_en_boca, gustos_predominantes
) VALUES (
test_muestra_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[]::smallint[], ARRAY[]::smallint[],
ARRAY[]::text[], ARRAY[]::text[]
);
RAISE NOTICE ' ✓ Acepta arrays vacíos correctamente';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó arrays vacíos: %', SQLERRM;
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Arrays vacíos funcionan correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Campos opcionales en NULL
-- ============================================
\echo '[EDGE 2] Validando campos opcionales en NULL...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_edge2@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-EDGE-2', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M2');
-- Test: evaluación con campos opcionales en NULL
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades,
defecto, otras_notas
) VALUES (
test_muestra_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
NULL, NULL
);
RAISE NOTICE ' ✓ Acepta campos opcionales NULL';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó campos NULL: %', SQLERRM;
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Campos opcionales NULL funcionan correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: JSONB con solo un parámetro
-- ============================================
\echo '[EDGE 3] Validando intensidades con un solo parámetro...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
puntaje int;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_edge3@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-EDGE-3', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M3');
-- Test: solo un parámetro en intensidades
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":7}}'::jsonb
) RETURNING puntaje_final INTO puntaje;
IF puntaje = 7 THEN
RAISE NOTICE ' ✓ Acepta un solo parámetro y calcula puntaje correcto: %', puntaje;
ELSE
RAISE EXCEPTION ' ✗ Puntaje incorrecto con un parámetro: % (esperado: 7)', puntaje;
END IF;
EXCEPTION WHEN check_violation THEN
RAISE EXCEPTION ' ✗ Rechazó un solo parámetro: %', SQLERRM;
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Un solo parámetro funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Valores descriptivos NULL con afectivos presentes
-- ============================================
\echo '[EDGE 4] Validando descriptiva NULL con afectiva presente...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
puntaje int;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_edge4@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-EDGE-4', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M4');
-- Test: impresionGlobal con descriptiva NULL (caso común)
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_part_id,
'{"impresionGlobal":{"descriptiva":null,"afectiva":8}}'::jsonb
) RETURNING puntaje_final INTO puntaje;
IF puntaje = 8 THEN
RAISE NOTICE ' ✓ Acepta descriptiva NULL y calcula puntaje correcto: %', puntaje;
ELSE
RAISE EXCEPTION ' ✗ Puntaje incorrecto: % (esperado: 8)', puntaje;
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó descriptiva NULL: %', SQLERRM;
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Descriptiva NULL funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: JSONB arrays con múltiples elementos
-- ============================================
\echo '[EDGE 5] Validando arrays JSONB con múltiples notas...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
notas_count int;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_edge5@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-EDGE-5', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M5');
-- Test: múltiples notas de fragancia/aroma
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades,
fragancia_aroma_notas, sabor_notas
) VALUES (
test_muestra_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
'[
{"categoria":"Afrutado","subcategoria":"Cítricos","notaEspecifica":"Naranja"},
{"categoria":"Floral","subcategoria":"Jazmín","notaEspecifica":null},
{"categoria":"Especiado","subcategoria":"Canela","notaEspecifica":"Canela dulce"}
]'::jsonb,
'[
{"categoria":"Chocolatado","subcategoria":"Chocolate negro","notaEspecifica":null},
{"categoria":"Nueces","subcategoria":"Almendra","notaEspecifica":"Tostada"}
]'::jsonb
);
-- Verificar que se guardaron correctamente
SELECT jsonb_array_length(fragancia_aroma_notas)
INTO notas_count
FROM evaluacion
WHERE muestra_id = test_muestra_id;
IF notas_count = 3 THEN
RAISE NOTICE ' ✓ Acepta múltiples notas de fragancia (3)';
ELSE
RAISE EXCEPTION ' ✗ Cantidad incorrecta de notas: % (esperado: 3)', notas_count;
END IF;
SELECT jsonb_array_length(sabor_notas)
INTO notas_count
FROM evaluacion
WHERE muestra_id = test_muestra_id;
IF notas_count = 2 THEN
RAISE NOTICE ' ✓ Acepta múltiples notas de sabor (2)';
ELSE
RAISE EXCEPTION ' ✗ Cantidad incorrecta de notas: % (esperado: 2)', notas_count;
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó múltiples notas: %', SQLERRM;
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Arrays JSONB con múltiples elementos funcionan correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Evaluación mínima válida (solo intensidades requeridas)
-- ============================================
\echo '[EDGE 6] Validando evaluación mínima válida...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
eval_id uuid;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_edge6@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-EDGE-6', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M6');
-- Test: solo intensidades (campo obligatorio), todo lo demás default/NULL
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_part_id,
'{"fragancia":{"descriptiva":1,"afectiva":1}}'::jsonb
) RETURNING id INTO eval_id;
-- Verificar que los defaults se aplicaron
PERFORM 1
FROM evaluacion
WHERE id = eval_id
AND fragancia_aroma_notas = '[]'::jsonb
AND sabor_notas = '[]'::jsonb
AND tazas_no_uniformes = ARRAY[]::smallint[]
AND tazas_defectuosas = ARRAY[]::smallint[]
AND sensacion_en_boca = ARRAY[]::text[]
AND gustos_predominantes = ARRAY[]::text[]
AND defecto IS NULL
AND otras_notas IS NULL;
IF FOUND THEN
RAISE NOTICE ' ✓ Evaluación mínima válida con defaults correctos';
ELSE
RAISE EXCEPTION ' ✗ Defaults no se aplicaron correctamente';
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Rechazó evaluación mínima: %', SQLERRM;
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Evaluación mínima válida funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Texto largo en otras_notas
-- ============================================
\echo '[EDGE 7] Validando texto largo en otras_notas...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
long_text text;
stored_text text;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_edge7@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-EDGE-7', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M7');
-- Generar texto de 1000 caracteres
long_text := repeat('Este es un texto largo con muchos detalles sobre la catación. ', 20);
-- Test: insertar texto largo
BEGIN
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades, otras_notas
) VALUES (
test_muestra_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
long_text
);
SELECT otras_notas INTO stored_text
FROM evaluacion
WHERE muestra_id = test_muestra_id;
IF stored_text = long_text THEN
RAISE NOTICE ' ✓ Acepta y almacena texto largo (% caracteres)', length(stored_text);
ELSE
RAISE EXCEPTION ' ✗ Texto almacenado no coincide';
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION ' ✗ Falló con texto largo: %', SQLERRM;
END;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Texto largo funciona correctamente';
END $$;
\echo ''
\echo '=========================================='
\echo 'Tests de Casos Límite completados'
\echo '=========================================='

View File

@@ -0,0 +1,256 @@
-- ============================================
-- rioCata - Tests de Funciones Auxiliares
-- ============================================
-- Tests que verifican las funciones auxiliares
-- con datos generados dinámicamente
-- ============================================
\echo '=========================================='
\echo 'Tests de Funciones Auxiliares'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST: Función get_promedio_parametro_afectivo
-- ============================================
\echo '[FUNCTION 1] Probando función: get_promedio_parametro_afectivo...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user1_id uuid := gen_random_uuid();
test_user2_id uuid := gen_random_uuid();
test_user3_id uuid := gen_random_uuid();
test_part1_id uuid := gen_random_uuid();
test_part2_id uuid := gen_random_uuid();
test_part3_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
avg_acidez numeric;
avg_dulzor numeric;
avg_sabor numeric;
expected_acidez numeric := 7.0; -- (6 + 8 + 7) / 3
expected_dulzor numeric := 5.0; -- (3 + 7 + 5) / 3
expected_sabor numeric := 8.0; -- (8 + 8 + 8) / 3
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre) VALUES
(test_user1_id, 'test_func1_u1@test.com', 'User 1'),
(test_user2_id, 'test_func1_u2@test.com', 'User 2'),
(test_user3_id, 'test_func1_u3@test.com', 'User 3');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-FUNC-1', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id) VALUES
(test_part1_id, test_sesion_id, test_user1_id),
(test_part2_id, test_sesion_id, test_user2_id),
(test_part3_id, test_sesion_id, test_user3_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M1');
-- Crear 3 evaluaciones con diferentes valores
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades) VALUES
(test_muestra_id, test_part1_id,
'{"acidez":{"descriptiva":5,"afectiva":6},"dulzor":{"descriptiva":5,"afectiva":3},"sabor":{"descriptiva":8,"afectiva":8}}'::jsonb),
(test_muestra_id, test_part2_id,
'{"acidez":{"descriptiva":10,"afectiva":8},"dulzor":{"descriptiva":10,"afectiva":7},"sabor":{"descriptiva":8,"afectiva":8}}'::jsonb),
(test_muestra_id, test_part3_id,
'{"acidez":{"descriptiva":7,"afectiva":7},"dulzor":{"descriptiva":6,"afectiva":5},"sabor":{"descriptiva":8,"afectiva":8}}'::jsonb);
-- Test función para acidez
SELECT get_promedio_parametro_afectivo(test_sesion_id, 'acidez')
INTO avg_acidez;
IF avg_acidez = expected_acidez THEN
RAISE NOTICE ' ✓ Promedio acidez correcto: % (esperado: %)',
avg_acidez, expected_acidez;
ELSE
RAISE EXCEPTION ' ✗ Promedio acidez incorrecto: % (esperado: %)',
avg_acidez, expected_acidez;
END IF;
-- Test función para dulzor
SELECT get_promedio_parametro_afectivo(test_sesion_id, 'dulzor')
INTO avg_dulzor;
IF avg_dulzor = expected_dulzor THEN
RAISE NOTICE ' ✓ Promedio dulzor correcto: % (esperado: %)',
avg_dulzor, expected_dulzor;
ELSE
RAISE EXCEPTION ' ✗ Promedio dulzor incorrecto: % (esperado: %)',
avg_dulzor, expected_dulzor;
END IF;
-- Test función para sabor
SELECT get_promedio_parametro_afectivo(test_sesion_id, 'sabor')
INTO avg_sabor;
IF avg_sabor = expected_sabor THEN
RAISE NOTICE ' ✓ Promedio sabor correcto: % (esperado: %)',
avg_sabor, expected_sabor;
ELSE
RAISE EXCEPTION ' ✗ Promedio sabor incorrecto: % (esperado: %)',
avg_sabor, expected_sabor;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id IN (test_user1_id, test_user2_id, test_user3_id);
RAISE NOTICE '✓ Función get_promedio_parametro_afectivo funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Función get_top_muestras
-- ============================================
\echo '[FUNCTION 2] Probando función: get_top_muestras...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra1_id uuid := gen_random_uuid();
test_muestra2_id uuid := gen_random_uuid();
test_muestra3_id uuid := gen_random_uuid();
test_muestra4_id uuid := gen_random_uuid();
result record;
result_count int := 0;
first_muestra text;
first_puntaje int;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_func2@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-FUNC-2', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
-- Crear 4 muestras con puntajes 25, 50, 75, 100 (ordenadas al revés)
INSERT INTO muestra (id, sesion_id, codigo) VALUES
(test_muestra1_id, test_sesion_id, 'PUNTAJE-25'),
(test_muestra2_id, test_sesion_id, 'PUNTAJE-50'),
(test_muestra3_id, test_sesion_id, 'PUNTAJE-75'),
(test_muestra4_id, test_sesion_id, 'PUNTAJE-100');
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades) VALUES
(test_muestra1_id, test_part_id,
'{"fragancia":{"afectiva":3},"aroma":{"afectiva":3},"sabor":{"afectiva":3},"saborResidual":{"afectiva":3},"acidez":{"afectiva":3},"dulzor":{"afectiva":4},"sensacionBoca":{"afectiva":3},"impresionGlobal":{"afectiva":3}}'::jsonb),
(test_muestra2_id, test_part_id,
'{"fragancia":{"afectiva":6},"aroma":{"afectiva":6},"sabor":{"afectiva":6},"saborResidual":{"afectiva":6},"acidez":{"afectiva":7},"dulzor":{"afectiva":7},"sensacionBoca":{"afectiva":6},"impresionGlobal":{"afectiva":6}}'::jsonb),
(test_muestra3_id, test_part_id,
'{"fragancia":{"afectiva":9},"aroma":{"afectiva":9},"sabor":{"afectiva":9},"saborResidual":{"afectiva":10},"acidez":{"afectiva":10},"dulzor":{"afectiva":10},"sensacionBoca":{"afectiva":9},"impresionGlobal":{"afectiva":9}}'::jsonb),
(test_muestra4_id, test_part_id,
'{"fragancia":{"afectiva":10},"aroma":{"afectiva":10},"sabor":{"afectiva":10},"saborResidual":{"afectiva":10},"acidez":{"afectiva":10},"dulzor":{"afectiva":10},"sensacionBoca":{"afectiva":10},"impresionGlobal":{"afectiva":10}}'::jsonb);
-- Test: obtener top 3
FOR result IN
SELECT * FROM get_top_muestras(test_sesion_id, 3)
LOOP
result_count := result_count + 1;
IF result_count = 1 THEN
first_muestra := result.muestra_codigo;
first_puntaje := result.puntaje_final;
END IF;
RAISE NOTICE ' - Posición %: % (% puntos)',
result_count, result.muestra_codigo, result.puntaje_final;
END LOOP;
-- Validar que devolvió 3 resultados
IF result_count != 3 THEN
RAISE EXCEPTION ' ✗ Devolvió % resultados (esperado: 3)', result_count;
END IF;
-- Validar que el primero es PUNTAJE-100 con 80 puntos
IF first_muestra != 'PUNTAJE-100' THEN
RAISE EXCEPTION ' ✗ Primera muestra incorrecta: % (esperado: PUNTAJE-100)',
first_muestra;
END IF;
IF first_puntaje != 80 THEN
RAISE EXCEPTION ' ✗ Primer puntaje incorrecto: % (esperado: 80)',
first_puntaje;
END IF;
RAISE NOTICE ' ✓ Función devuelve top 3 correctamente';
-- Test: obtener top 10 (debería devolver solo 4)
result_count := 0;
FOR result IN
SELECT * FROM get_top_muestras(test_sesion_id, 10)
LOOP
result_count := result_count + 1;
END LOOP;
IF result_count = 4 THEN
RAISE NOTICE ' ✓ Función limita correctamente cuando hay menos resultados que el límite';
ELSE
RAISE EXCEPTION ' ✗ Con limit=10 devolvió % resultados (esperado: 4)',
result_count;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Función get_top_muestras funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Funciones con sesión vacía
-- ============================================
\echo '[FUNCTION 3] Probando funciones con sesión sin evaluaciones...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
avg_result numeric;
top_count int := 0;
result record;
BEGIN
-- Crear sesión vacía
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-FUNC-EMPTY', CURRENT_DATE);
-- Test get_promedio_parametro_afectivo con sesión vacía
SELECT get_promedio_parametro_afectivo(test_sesion_id, 'acidez')
INTO avg_result;
IF avg_result IS NULL THEN
RAISE NOTICE ' ✓ get_promedio_parametro_afectivo devuelve NULL con sesión vacía';
ELSE
RAISE EXCEPTION ' ✗ Devolvió % en lugar de NULL', avg_result;
END IF;
-- Test get_top_muestras con sesión vacía
FOR result IN
SELECT * FROM get_top_muestras(test_sesion_id, 3)
LOOP
top_count := top_count + 1;
END LOOP;
IF top_count = 0 THEN
RAISE NOTICE ' ✓ get_top_muestras devuelve conjunto vacío con sesión vacía';
ELSE
RAISE EXCEPTION ' ✗ Devolvió % resultados en lugar de 0', top_count;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
RAISE NOTICE '✓ Funciones manejan correctamente sesiones vacías';
END $$;
\echo ''
\echo '=========================================='
\echo 'Tests de Funciones completados'
\echo '=========================================='

View File

@@ -0,0 +1,279 @@
-- ============================================
-- rioCata - Tests de Índices
-- ============================================
-- Tests que verifican que los índices se están
-- usando correctamente con EXPLAIN
-- ============================================
\echo '=========================================='
\echo 'Tests de Uso de Índices'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST: Índice GIN en tazas_defectuosas con operador @>
-- ============================================
\echo '[INDEX 1] Verificando uso de índice GIN en tazas_defectuosas...'
DO $$
DECLARE
explain_output text;
uses_index boolean := false;
BEGIN
-- Obtener plan de ejecución
SELECT string_agg(line, E'\n')
INTO explain_output
FROM (
SELECT * FROM (
EXPLAIN SELECT * FROM evaluacion
WHERE tazas_defectuosas @> ARRAY[5]::smallint[]
) AS explain_lines(line)
) AS subq;
-- Verificar que usa índice GIN
IF explain_output LIKE '%idx_eval_tazas_defectuosas%' OR
explain_output LIKE '%Bitmap Index Scan%' THEN
uses_index := true;
END IF;
IF uses_index THEN
RAISE NOTICE ' ✓ Query usa índice GIN para tazas_defectuosas con @>';
ELSE
RAISE WARNING ' ⚠ Query NO usa índice GIN (puede ser normal con tabla vacía)';
RAISE NOTICE 'Plan: %', explain_output;
END IF;
RAISE NOTICE '✓ Verificación de índice GIN en tazas_defectuosas completada';
END $$;
\echo ''
-- ============================================
-- TEST: Índice funcional en acidez afectiva
-- ============================================
\echo '[INDEX 2] Verificando uso de índice funcional en acidez afectiva...'
DO $$
DECLARE
explain_output text;
uses_index boolean := false;
BEGIN
-- Obtener plan de ejecución
SELECT string_agg(line, E'\n')
INTO explain_output
FROM (
SELECT * FROM (
EXPLAIN SELECT * FROM evaluacion
WHERE ((intensidades->'acidez'->>'afectiva')::int) >= 8
) AS explain_lines(line)
) AS subq;
-- Verificar que usa índice funcional
IF explain_output LIKE '%idx_eval_int_acidez_afectiva%' OR
explain_output LIKE '%Index%' THEN
uses_index := true;
END IF;
IF uses_index THEN
RAISE NOTICE ' ✓ Query usa índice funcional para acidez afectiva';
ELSE
RAISE WARNING ' ⚠ Query NO usa índice funcional (puede ser normal con tabla vacía)';
END IF;
RAISE NOTICE '✓ Verificación de índice funcional en acidez completada';
END $$;
\echo ''
-- ============================================
-- TEST: Índice B-tree en puntaje_final
-- ============================================
\echo '[INDEX 3] Verificando uso de índice B-tree en puntaje_final...'
DO $$
DECLARE
explain_output text;
uses_index boolean := false;
BEGIN
-- Obtener plan de ejecución para ORDER BY
SELECT string_agg(line, E'\n')
INTO explain_output
FROM (
SELECT * FROM (
EXPLAIN SELECT * FROM evaluacion
ORDER BY puntaje_final DESC
LIMIT 10
) AS explain_lines(line)
) AS subq;
-- Verificar que usa índice
IF explain_output LIKE '%idx_eval_puntaje_final%' OR
explain_output LIKE '%Index%puntaje%' THEN
uses_index := true;
END IF;
IF uses_index THEN
RAISE NOTICE ' ✓ Query usa índice B-tree para puntaje_final';
ELSE
RAISE WARNING ' ⚠ Query NO usa índice B-tree (puede ser normal con tabla vacía)';
END IF;
RAISE NOTICE '✓ Verificación de índice B-tree en puntaje_final completada';
END $$;
\echo ''
-- ============================================
-- TEST: Índice GIN en intensidades JSONB
-- ============================================
\echo '[INDEX 4] Verificando uso de índice GIN en intensidades JSONB...'
DO $$
DECLARE
explain_output text;
uses_index boolean := false;
BEGIN
-- Obtener plan de ejecución
SELECT string_agg(line, E'\n')
INTO explain_output
FROM (
SELECT * FROM (
EXPLAIN SELECT * FROM evaluacion
WHERE intensidades @> '{"dulzor":{"afectiva":10}}'::jsonb
) AS explain_lines(line)
) AS subq;
-- Verificar que usa índice GIN
IF explain_output LIKE '%idx_eval_json_intensidades%' OR
explain_output LIKE '%Bitmap Index Scan%intensidades%' THEN
uses_index := true;
END IF;
IF uses_index THEN
RAISE NOTICE ' ✓ Query usa índice GIN para búsqueda en JSONB';
ELSE
RAISE WARNING ' ⚠ Query NO usa índice GIN JSONB (puede ser normal con tabla vacía)';
END IF;
RAISE NOTICE '✓ Verificación de índice GIN en JSONB completada';
END $$;
\echo ''
-- ============================================
-- TEST: Índices con datos reales
-- ============================================
\echo '[INDEX 5] Verificando uso de índices con datos reales...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra_id uuid;
explain_output text;
i int;
BEGIN
-- Crear datos de prueba (100 evaluaciones)
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_index@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-INDEX', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
-- Insertar 100 muestras y evaluaciones
FOR i IN 1..100 LOOP
test_muestra_id := gen_random_uuid();
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'M-' || i);
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades,
tazas_defectuosas, puntaje_final
) VALUES (
test_muestra_id, test_part_id,
jsonb_build_object(
'acidez', jsonb_build_object('descriptiva', (i % 15) + 1, 'afectiva', (i % 10) + 1),
'dulzor', jsonb_build_object('descriptiva', ((i * 2) % 15) + 1, 'afectiva', ((i * 2) % 10) + 1)
),
CASE WHEN i % 10 = 0 THEN ARRAY[5]::smallint[] ELSE ARRAY[]::smallint[] END,
(i % 80) + 1
);
END LOOP;
RAISE NOTICE ' ✓ Creadas 100 evaluaciones para testing de índices';
-- Test 1: Índice en puntaje_final con datos
SELECT string_agg(line, E'\n')
INTO explain_output
FROM (
SELECT * FROM (
EXPLAIN SELECT * FROM evaluacion
WHERE puntaje_final > 50
ORDER BY puntaje_final DESC
) AS explain_lines(line)
) AS subq;
IF explain_output LIKE '%Index%' THEN
RAISE NOTICE ' ✓ Índice usado en puntaje_final con 100 registros';
ELSE
RAISE NOTICE ' - Plan para puntaje_final: Seq Scan (normal con 100 registros)';
END IF;
-- Test 2: Índice GIN en arrays con datos
SELECT string_agg(line, E'\n')
INTO explain_output
FROM (
SELECT * FROM (
EXPLAIN SELECT * FROM evaluacion
WHERE tazas_defectuosas @> ARRAY[5]::smallint[]
) AS explain_lines(line)
) AS subq;
IF explain_output LIKE '%Index%' OR explain_output LIKE '%Bitmap%' THEN
RAISE NOTICE ' ✓ Índice GIN usado en tazas_defectuosas con 100 registros';
ELSE
RAISE NOTICE ' - Plan para tazas_defectuosas: Seq Scan (normal con 100 registros)';
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Verificación con datos reales completada';
RAISE NOTICE '';
RAISE NOTICE 'NOTA: Con pocos registros PostgreSQL puede preferir Seq Scan sobre índices.';
RAISE NOTICE ' Los índices se usan más efectivamente con miles de registros.';
END $$;
\echo ''
-- ============================================
-- TEST: Listado de todos los índices
-- ============================================
\echo '[INDEX 6] Listando todos los índices de rioCata...'
SELECT
schemaname AS schema,
tablename AS tabla,
indexname AS indice,
indexdef AS definicion
FROM pg_indexes
WHERE schemaname = 'public'
AND tablename IN ('sesion', 'sesion_participante', 'muestra', 'evaluacion')
ORDER BY tablename, indexname;
\echo ''
\echo '=========================================='
\echo 'Tests de Índices completados'
\echo '=========================================='
\echo ''
\echo 'NOTA IMPORTANTE:'
\echo 'Los índices GIN y funcionales se aprovechan mejor con grandes'
\echo 'volúmenes de datos (miles de registros). Con datos de prueba'
\echo 'pequeños, PostgreSQL puede optar por Seq Scan que es más eficiente.'
\echo ''

View File

@@ -0,0 +1,364 @@
-- ============================================
-- rioCata - Tests de Queries Típicas
-- ============================================
-- Tests que verifican queries comunes con datos
-- generados dinámicamente y validaciones específicas
-- ============================================
\echo '=========================================='
\echo 'Tests de Queries Típicas'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST: Query promedio de parámetro afectivo
-- ============================================
\echo '[QUERY 1] Probando query: Promedio de parámetro afectivo...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user1_id uuid := gen_random_uuid();
test_user2_id uuid := gen_random_uuid();
test_part1_id uuid := gen_random_uuid();
test_part2_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
avg_dulzor numeric;
expected_avg numeric := 7.5; -- (5 + 10) / 2
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre) VALUES
(test_user1_id, 'test_query1_u1@test.com', 'User 1'),
(test_user2_id, 'test_query1_u2@test.com', 'User 2');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-QUERY-1', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id) VALUES
(test_part1_id, test_sesion_id, test_user1_id),
(test_part2_id, test_sesion_id, test_user2_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M1');
-- Crear 2 evaluaciones con dulzor 5 y 10
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades) VALUES
(test_muestra_id, test_part1_id,
'{"dulzor":{"descriptiva":5,"afectiva":5}}'::jsonb),
(test_muestra_id, test_part2_id,
'{"dulzor":{"descriptiva":10,"afectiva":10}}'::jsonb);
-- Ejecutar query
SELECT AVG(((e.intensidades->'dulzor'->>'afectiva')::int))
INTO avg_dulzor
FROM sesion s
JOIN muestra m ON m.sesion_id = s.id
JOIN evaluacion e ON e.muestra_id = m.id
WHERE s.id = test_sesion_id;
-- Validar resultado
IF avg_dulzor = expected_avg THEN
RAISE NOTICE ' ✓ Promedio calculado correctamente: % (esperado: %)',
avg_dulzor, expected_avg;
ELSE
RAISE EXCEPTION ' ✗ Promedio incorrecto: % (esperado: %)',
avg_dulzor, expected_avg;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id IN (test_user1_id, test_user2_id);
RAISE NOTICE '✓ Query promedio de parámetro afectivo funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Query buscar por defecto específico
-- ============================================
\echo '[QUERY 2] Probando query: Buscar evaluaciones con defecto específico...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra1_id uuid := gen_random_uuid();
test_muestra2_id uuid := gen_random_uuid();
count_fenolico int;
count_mohoso int;
expected_fenolico int := 2;
expected_mohoso int := 1;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_query2@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-QUERY-2', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo) VALUES
(test_muestra1_id, test_sesion_id, 'TEST-M1'),
(test_muestra2_id, test_sesion_id, 'TEST-M2');
-- Crear 2 evaluaciones con defecto Fenólico y 1 con Mohoso
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades, defecto)
SELECT test_muestra1_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
'Fenólico'::defecto_tipo
UNION ALL
SELECT test_muestra2_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
'Fenólico'::defecto_tipo;
-- Insertar una con Mohoso en nueva muestra
DELETE FROM muestra WHERE id = test_muestra1_id;
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra1_id, test_sesion_id, 'TEST-M3');
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades, defecto)
VALUES (test_muestra1_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
'Mohoso'::defecto_tipo);
-- Query para Fenólico
SELECT COUNT(*) INTO count_fenolico
FROM evaluacion
WHERE defecto = 'Fenólico';
-- Query para Mohoso
SELECT COUNT(*) INTO count_mohoso
FROM evaluacion
WHERE defecto = 'Mohoso';
-- Validar
IF count_fenolico = expected_fenolico AND count_mohoso = expected_mohoso THEN
RAISE NOTICE ' ✓ Búsqueda por defecto correcta: Fenólico=%, Mohoso=%',
count_fenolico, count_mohoso;
ELSE
RAISE EXCEPTION ' ✗ Búsqueda incorrecta: Fenólico=% (esp: %), Mohoso=% (esp: %)',
count_fenolico, expected_fenolico, count_mohoso, expected_mohoso;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Query buscar por defecto funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Query buscar taza defectuosa con operador @>
-- ============================================
\echo '[QUERY 3] Probando query: Buscar taza defectuosa específica (índice GIN)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra1_id uuid := gen_random_uuid();
test_muestra2_id uuid := gen_random_uuid();
test_muestra3_id uuid := gen_random_uuid();
count_taza5 int;
count_taza3 int;
expected_taza5 int := 2;
expected_taza3 int := 1;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_query3@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-QUERY-3', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo) VALUES
(test_muestra1_id, test_sesion_id, 'TEST-M1'),
(test_muestra2_id, test_sesion_id, 'TEST-M2'),
(test_muestra3_id, test_sesion_id, 'TEST-M3');
-- Crear evaluaciones con tazas defectuosas
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades, tazas_defectuosas) VALUES
(test_muestra1_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[5]::smallint[]),
(test_muestra2_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[3, 5]::smallint[]),
(test_muestra3_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb,
ARRAY[1, 2]::smallint[]);
-- Query usando operador @> (debe usar índice GIN)
SELECT COUNT(*) INTO count_taza5
FROM evaluacion
WHERE tazas_defectuosas @> ARRAY[5]::smallint[];
SELECT COUNT(*) INTO count_taza3
FROM evaluacion
WHERE tazas_defectuosas @> ARRAY[3]::smallint[];
-- Validar
IF count_taza5 = expected_taza5 AND count_taza3 = expected_taza3 THEN
RAISE NOTICE ' ✓ Operador @> funciona correctamente: taza5=%, taza3=%',
count_taza5, count_taza3;
ELSE
RAISE EXCEPTION ' ✗ Operador @> incorrecto: taza5=% (esp: %), taza3=% (esp: %)',
count_taza5, expected_taza5, count_taza3, expected_taza3;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Query con operador @> funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Query top muestras por puntaje
-- ============================================
\echo '[QUERY 4] Probando query: Top muestras por puntaje final...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra1_id uuid := gen_random_uuid();
test_muestra2_id uuid := gen_random_uuid();
test_muestra3_id uuid := gen_random_uuid();
top_muestra text;
top_puntaje int;
expected_muestra text := 'HIGH';
expected_puntaje int := 80;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_query4@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-QUERY-4', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
-- Crear 3 muestras con diferentes puntajes
INSERT INTO muestra (id, sesion_id, codigo) VALUES
(test_muestra1_id, test_sesion_id, 'LOW'),
(test_muestra2_id, test_sesion_id, 'MID'),
(test_muestra3_id, test_sesion_id, 'HIGH');
-- Evaluaciones con puntajes 20, 50, 80
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades) VALUES
(test_muestra1_id, test_part_id,
'{"fragancia":{"descriptiva":1,"afectiva":2},"aroma":{"descriptiva":1,"afectiva":3},"sabor":{"descriptiva":1,"afectiva":2},"saborResidual":{"descriptiva":1,"afectiva":3},"acidez":{"descriptiva":1,"afectiva":3},"dulzor":{"descriptiva":1,"afectiva":2},"sensacionBoca":{"descriptiva":1,"afectiva":3},"impresionGlobal":{"afectiva":2}}'::jsonb),
(test_muestra2_id, test_part_id,
'{"fragancia":{"descriptiva":5,"afectiva":6},"aroma":{"descriptiva":5,"afectiva":6},"sabor":{"descriptiva":5,"afectiva":7},"saborResidual":{"descriptiva":5,"afectiva":6},"acidez":{"descriptiva":5,"afectiva":6},"dulzor":{"descriptiva":5,"afectiva":7},"sensacionBoca":{"descriptiva":5,"afectiva":6},"impresionGlobal":{"afectiva":6}}'::jsonb),
(test_muestra3_id, test_part_id,
'{"fragancia":{"descriptiva":10,"afectiva":10},"aroma":{"descriptiva":10,"afectiva":10},"sabor":{"descriptiva":10,"afectiva":10},"saborResidual":{"descriptiva":10,"afectiva":10},"acidez":{"descriptiva":10,"afectiva":10},"dulzor":{"descriptiva":10,"afectiva":10},"sensacionBoca":{"descriptiva":10,"afectiva":10},"impresionGlobal":{"afectiva":10}}'::jsonb);
-- Query top 1
SELECT m.codigo, e.puntaje_final
INTO top_muestra, top_puntaje
FROM muestra m
JOIN evaluacion e ON e.muestra_id = m.id
JOIN sesion_participante sp ON sp.id = e.sesion_participante_id
WHERE m.sesion_id = test_sesion_id
ORDER BY e.puntaje_final DESC
LIMIT 1;
-- Validar
IF top_muestra = expected_muestra AND top_puntaje = expected_puntaje THEN
RAISE NOTICE ' ✓ Top muestra correcta: % con puntaje %',
top_muestra, top_puntaje;
ELSE
RAISE EXCEPTION ' ✗ Top muestra incorrecta: % (esp: %), puntaje: % (esp: %)',
top_muestra, expected_muestra, top_puntaje, expected_puntaje;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Query top muestras funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Query filtrar por acidez alta (índice funcional)
-- ============================================
\echo '[QUERY 5] Probando query: Filtrar por acidez afectiva >= 8...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_part_id uuid := gen_random_uuid();
test_muestra1_id uuid := gen_random_uuid();
test_muestra2_id uuid := gen_random_uuid();
test_muestra3_id uuid := gen_random_uuid();
count_high_acidez int;
expected_count int := 2;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_query5@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-QUERY-5', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_part_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo) VALUES
(test_muestra1_id, test_sesion_id, 'TEST-M1'),
(test_muestra2_id, test_sesion_id, 'TEST-M2'),
(test_muestra3_id, test_sesion_id, 'TEST-M3');
-- Crear evaluaciones con acidez 5, 8, 10
INSERT INTO evaluacion (muestra_id, sesion_participante_id, intensidades) VALUES
(test_muestra1_id, test_part_id,
'{"acidez":{"descriptiva":5,"afectiva":5}}'::jsonb),
(test_muestra2_id, test_part_id,
'{"acidez":{"descriptiva":8,"afectiva":8}}'::jsonb),
(test_muestra3_id, test_part_id,
'{"acidez":{"descriptiva":10,"afectiva":10}}'::jsonb);
-- Query con índice funcional
SELECT COUNT(*) INTO count_high_acidez
FROM evaluacion
WHERE ((intensidades->'acidez'->>'afectiva')::int) >= 8;
-- Validar
IF count_high_acidez = expected_count THEN
RAISE NOTICE ' ✓ Filtro por acidez >= 8 correcto: % evaluaciones',
count_high_acidez;
ELSE
RAISE EXCEPTION ' ✗ Filtro incorrecto: % (esperado: %)',
count_high_acidez, expected_count;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Query con filtro por parámetro JSONB funciona correctamente';
END $$;
\echo ''
\echo '=========================================='
\echo 'Tests de Queries completados'
\echo '=========================================='

View File

@@ -0,0 +1,278 @@
-- ============================================
-- rioCata - Tests de Estructura de BD
-- ============================================
-- Tests que verifican la existencia y correcta
-- configuración de la estructura de la base de datos
-- ============================================
\echo '=========================================='
\echo 'Tests de Estructura de Base de Datos'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST: Verificar existencia de tablas
-- ============================================
\echo '[STRUCTURE 1] Verificando existencia de tablas...'
DO $$
DECLARE
tabla_count int;
BEGIN
SELECT COUNT(*) INTO tabla_count
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN ('sesion', 'sesion_participante', 'muestra', 'evaluacion');
IF tabla_count = 4 THEN
RAISE NOTICE ' ✓ Todas las tablas principales existen (4/4)';
ELSE
RAISE EXCEPTION ' ✗ Faltan tablas. Encontradas: %/4', tabla_count;
END IF;
-- Verificar tabla auth.users
SELECT COUNT(*) INTO tabla_count
FROM information_schema.tables
WHERE table_schema = 'auth' AND table_name = 'users';
IF tabla_count = 1 THEN
RAISE NOTICE ' ✓ Tabla auth.users existe';
ELSE
RAISE EXCEPTION ' ✗ Tabla auth.users no existe';
END IF;
RAISE NOTICE '✓ Todas las tablas existen correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Verificar tipo ENUM defecto_tipo
-- ============================================
\echo '[STRUCTURE 2] Verificando tipo ENUM defecto_tipo...'
DO $$
DECLARE
enum_exists boolean;
enum_values text[];
expected_values text[] := ARRAY['Mohoso', 'Fenólico', 'Papa'];
BEGIN
SELECT EXISTS (
SELECT 1 FROM pg_type WHERE typname = 'defecto_tipo'
) INTO enum_exists;
IF NOT enum_exists THEN
RAISE EXCEPTION ' ✗ Tipo ENUM defecto_tipo no existe';
END IF;
-- Verificar valores del ENUM
SELECT array_agg(enumlabel ORDER BY enumsortorder)
INTO enum_values
FROM pg_enum
WHERE enumtypid = 'defecto_tipo'::regtype;
IF enum_values = expected_values THEN
RAISE NOTICE ' ✓ Tipo ENUM defecto_tipo existe con valores correctos: %',
array_to_string(enum_values, ', ');
ELSE
RAISE EXCEPTION ' ✗ Valores ENUM incorrectos. Encontrados: %, Esperados: %',
enum_values, expected_values;
END IF;
RAISE NOTICE '✓ Tipo ENUM defecto_tipo correcto';
END $$;
\echo ''
-- ============================================
-- TEST: Verificar triggers
-- ============================================
\echo '[STRUCTURE 3] Verificando triggers...'
DO $$
DECLARE
trigger_count int;
trigger_names text[];
BEGIN
SELECT COUNT(*), array_agg(trigger_name ORDER BY trigger_name)
INTO trigger_count, trigger_names
FROM information_schema.triggers
WHERE event_object_table = 'evaluacion'
AND trigger_name IN ('trg_eval_updated_at', 'trg_eval_score_bi');
IF trigger_count >= 2 THEN
RAISE NOTICE ' ✓ Triggers principales existen (% encontrados): %',
trigger_count, array_to_string(trigger_names, ', ');
ELSE
RAISE EXCEPTION ' ✗ Faltan triggers. Encontrados: %', trigger_count;
END IF;
RAISE NOTICE '✓ Todos los triggers existen';
END $$;
\echo ''
-- ============================================
-- TEST: Verificar índices
-- ============================================
\echo '[STRUCTURE 4] Verificando índices...'
DO $$
DECLARE
index_count int;
expected_min int := 15;
BEGIN
SELECT COUNT(*) INTO index_count
FROM pg_indexes
WHERE schemaname = 'public'
AND indexname LIKE 'idx_%';
IF index_count >= expected_min THEN
RAISE NOTICE ' ✓ Índices creados correctamente (% encontrados, esperado: >= %)',
index_count, expected_min;
ELSE
RAISE WARNING ' ⚠ Se esperaban al menos % índices, se encontraron: %',
expected_min, index_count;
END IF;
RAISE NOTICE '✓ Verificación de índices completada';
END $$;
\echo ''
-- ============================================
-- TEST: Verificar funciones auxiliares
-- ============================================
\echo '[STRUCTURE 5] Verificando funciones auxiliares...'
DO $$
DECLARE
func_count int;
func_names text[];
BEGIN
SELECT COUNT(*), array_agg(proname ORDER BY proname)
INTO func_count, func_names
FROM pg_proc
WHERE proname IN ('get_promedio_parametro_afectivo', 'get_top_muestras')
AND pronamespace = 'public'::regnamespace;
IF func_count = 2 THEN
RAISE NOTICE ' ✓ Todas las funciones auxiliares existen (2/2): %',
array_to_string(func_names, ', ');
ELSE
RAISE EXCEPTION ' ✗ Faltan funciones. Encontradas: %/2', func_count;
END IF;
RAISE NOTICE '✓ Funciones auxiliares existen';
END $$;
\echo ''
-- ============================================
-- TEST: Verificar claves foráneas
-- ============================================
\echo '[STRUCTURE 6] Verificando claves foráneas...'
DO $$
DECLARE
fk_count int;
expected_fks int := 5;
BEGIN
SELECT COUNT(*) INTO fk_count
FROM information_schema.table_constraints
WHERE constraint_schema = 'public'
AND constraint_type = 'FOREIGN KEY';
IF fk_count >= expected_fks THEN
RAISE NOTICE ' ✓ Claves foráneas configuradas (% encontradas)', fk_count;
ELSE
RAISE WARNING ' ⚠ Se esperaban al menos % FK, encontradas: %',
expected_fks, fk_count;
END IF;
RAISE NOTICE '✓ Verificación de claves foráneas completada';
END $$;
\echo ''
-- ============================================
-- TEST: Verificar columnas de evaluacion
-- ============================================
\echo '[STRUCTURE 7] Verificando columnas de tabla evaluacion...'
DO $$
DECLARE
required_columns text[] := ARRAY[
'id', 'muestra_id', 'sesion_participante_id',
'intensidades', 'fragancia_aroma_notas', 'sabor_notas',
'tazas_no_uniformes', 'tazas_defectuosas',
'sensacion_en_boca', 'gustos_predominantes',
'defecto', 'otras_notas', 'puntaje_final',
'created_at', 'updated_at'
];
found_columns text[];
missing_columns text[];
BEGIN
SELECT array_agg(column_name)
INTO found_columns
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'evaluacion'
AND column_name = ANY(required_columns);
-- Buscar columnas faltantes
SELECT array_agg(col)
INTO missing_columns
FROM unnest(required_columns) AS col
WHERE col NOT IN (SELECT unnest(found_columns));
IF missing_columns IS NULL THEN
RAISE NOTICE ' ✓ Todas las columnas requeridas existen (15/15)';
ELSE
RAISE EXCEPTION ' ✗ Columnas faltantes: %', array_to_string(missing_columns, ', ');
END IF;
RAISE NOTICE '✓ Columnas de evaluacion correctas';
END $$;
\echo ''
-- ============================================
-- TEST: Verificar tipos de datos de columnas críticas
-- ============================================
\echo '[STRUCTURE 8] Verificando tipos de datos...'
DO $$
DECLARE
wrong_types text;
BEGIN
SELECT string_agg(
column_name || ' (' || data_type || ')',
', '
)
INTO wrong_types
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'evaluacion'
AND (
(column_name = 'intensidades' AND data_type != 'jsonb') OR
(column_name = 'fragancia_aroma_notas' AND data_type != 'jsonb') OR
(column_name = 'sabor_notas' AND data_type != 'jsonb') OR
(column_name = 'tazas_no_uniformes' AND data_type != 'ARRAY') OR
(column_name = 'tazas_defectuosas' AND data_type != 'ARRAY') OR
(column_name = 'puntaje_final' AND data_type NOT IN ('integer', 'bigint'))
);
IF wrong_types IS NOT NULL THEN
RAISE EXCEPTION ' ✗ Tipos de datos incorrectos: %', wrong_types;
END IF;
RAISE NOTICE ' ✓ Tipos de datos correctos para campos críticos';
RAISE NOTICE '✓ Tipos de datos verificados';
END $$;
\echo ''
\echo '=========================================='
\echo 'Tests de Estructura completados'
\echo '=========================================='

View File

@@ -0,0 +1,302 @@
-- ============================================
-- rioCata - Tests de Triggers
-- ============================================
-- Tests que verifican el funcionamiento correcto
-- de los triggers automáticos
-- ============================================
\echo '=========================================='
\echo 'Tests de Triggers'
\echo '=========================================='
\echo ''
-- ============================================
-- TEST: Trigger updated_at se actualiza automáticamente
-- ============================================
\echo '[TRIGGER 1] Validando trigger updated_at...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
test_eval_id uuid;
initial_updated_at timestamptz;
new_updated_at timestamptz;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_trigger1@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-TRIG-1', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M1');
-- Crear evaluación
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{"fragancia":{"descriptiva":5,"afectiva":5}}'::jsonb
) RETURNING id, updated_at INTO test_eval_id, initial_updated_at;
RAISE NOTICE ' ✓ Evaluación creada con updated_at: %', initial_updated_at;
-- Esperar 1 segundo
PERFORM pg_sleep(1);
-- Actualizar la evaluación
UPDATE evaluacion
SET intensidades = '{"fragancia":{"descriptiva":6,"afectiva":6}}'::jsonb
WHERE id = test_eval_id;
-- Verificar que updated_at cambió
SELECT updated_at INTO new_updated_at
FROM evaluacion
WHERE id = test_eval_id;
IF new_updated_at > initial_updated_at THEN
RAISE NOTICE ' ✓ updated_at se actualizó automáticamente (% -> %)',
initial_updated_at, new_updated_at;
ELSE
RAISE EXCEPTION ' ✗ updated_at NO se actualizó (% == %)',
initial_updated_at, new_updated_at;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Trigger updated_at funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Trigger puntaje_final se calcula automáticamente
-- ============================================
\echo '[TRIGGER 2] Validando trigger puntaje_final (cálculo automático)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
calculated_score int;
expected_score int;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_trigger2@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-TRIG-2', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M2');
-- Test 1: Crear evaluación con todos los parámetros
-- Suma esperada: 5+6+7+8+9+10+8+7 = 60
expected_score := 60;
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{
"fragancia": {"descriptiva": 5, "afectiva": 5},
"aroma": {"descriptiva": 6, "afectiva": 6},
"sabor": {"descriptiva": 7, "afectiva": 7},
"saborResidual": {"descriptiva": 8, "afectiva": 8},
"acidez": {"descriptiva": 9, "afectiva": 9},
"dulzor": {"descriptiva": 10, "afectiva": 10},
"sensacionBoca": {"descriptiva": 8, "afectiva": 8},
"impresionGlobal": {"descriptiva": null, "afectiva": 7}
}'::jsonb
);
SELECT puntaje_final INTO calculated_score
FROM evaluacion
WHERE muestra_id = test_muestra_id;
IF calculated_score = expected_score THEN
RAISE NOTICE ' ✓ Puntaje calculado correctamente: % (esperado: %)',
calculated_score, expected_score;
ELSE
RAISE EXCEPTION ' ✗ Puntaje incorrecto: % (esperado: %)',
calculated_score, expected_score;
END IF;
-- Test 2: Actualizar intensidades y verificar recálculo
DELETE FROM evaluacion WHERE muestra_id = test_muestra_id;
-- Suma esperada: 1+1+1+1+1+1+1+1 = 8
expected_score := 8;
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{
"fragancia": {"descriptiva": 1, "afectiva": 1},
"aroma": {"descriptiva": 1, "afectiva": 1},
"sabor": {"descriptiva": 1, "afectiva": 1},
"saborResidual": {"descriptiva": 1, "afectiva": 1},
"acidez": {"descriptiva": 1, "afectiva": 1},
"dulzor": {"descriptiva": 1, "afectiva": 1},
"sensacionBoca": {"descriptiva": 1, "afectiva": 1},
"impresionGlobal": {"descriptiva": null, "afectiva": 1}
}'::jsonb
);
SELECT puntaje_final INTO calculated_score
FROM evaluacion
WHERE muestra_id = test_muestra_id;
IF calculated_score = expected_score THEN
RAISE NOTICE ' ✓ Puntaje mínimo calculado correctamente: % (esperado: %)',
calculated_score, expected_score;
ELSE
RAISE EXCEPTION ' ✗ Puntaje mínimo incorrecto: % (esperado: %)',
calculated_score, expected_score;
END IF;
-- Test 3: Puntaje máximo
DELETE FROM evaluacion WHERE muestra_id = test_muestra_id;
-- Suma esperada: 10+10+10+10+10+10+10+10 = 80
expected_score := 80;
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{
"fragancia": {"descriptiva": 15, "afectiva": 10},
"aroma": {"descriptiva": 15, "afectiva": 10},
"sabor": {"descriptiva": 15, "afectiva": 10},
"saborResidual": {"descriptiva": 15, "afectiva": 10},
"acidez": {"descriptiva": 15, "afectiva": 10},
"dulzor": {"descriptiva": 15, "afectiva": 10},
"sensacionBoca": {"descriptiva": 15, "afectiva": 10},
"impresionGlobal": {"descriptiva": null, "afectiva": 10}
}'::jsonb
);
SELECT puntaje_final INTO calculated_score
FROM evaluacion
WHERE muestra_id = test_muestra_id;
IF calculated_score = expected_score THEN
RAISE NOTICE ' ✓ Puntaje máximo calculado correctamente: % (esperado: %)',
calculated_score, expected_score;
ELSE
RAISE EXCEPTION ' ✗ Puntaje máximo incorrecto: % (esperado: %)',
calculated_score, expected_score;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Trigger puntaje_final funciona correctamente';
END $$;
\echo ''
-- ============================================
-- TEST: Trigger puntaje_final se recalcula en UPDATE
-- ============================================
\echo '[TRIGGER 3] Validando trigger puntaje_final (recálculo en UPDATE)...'
DO $$
DECLARE
test_sesion_id uuid := gen_random_uuid();
test_user_id uuid := gen_random_uuid();
test_participante_id uuid := gen_random_uuid();
test_muestra_id uuid := gen_random_uuid();
test_eval_id uuid;
initial_score int;
updated_score int;
expected_updated_score int := 40;
BEGIN
-- Crear datos de prueba
INSERT INTO auth.users (id, email, nombre)
VALUES (test_user_id, 'test_trigger3@test.com', 'Test User');
INSERT INTO sesion (id, codigo, fecha)
VALUES (test_sesion_id, 'TEST-TRIG-3', CURRENT_DATE);
INSERT INTO sesion_participante (id, sesion_id, catador_id)
VALUES (test_participante_id, test_sesion_id, test_user_id);
INSERT INTO muestra (id, sesion_id, codigo)
VALUES (test_muestra_id, test_sesion_id, 'TEST-M3');
-- Crear evaluación con puntaje inicial 24 (8*3)
INSERT INTO evaluacion (
muestra_id, sesion_participante_id, intensidades
) VALUES (
test_muestra_id, test_participante_id,
'{
"fragancia": {"descriptiva": 5, "afectiva": 3},
"aroma": {"descriptiva": 5, "afectiva": 3},
"sabor": {"descriptiva": 5, "afectiva": 3},
"saborResidual": {"descriptiva": 5, "afectiva": 3},
"acidez": {"descriptiva": 5, "afectiva": 3},
"dulzor": {"descriptiva": 5, "afectiva": 3},
"sensacionBoca": {"descriptiva": 5, "afectiva": 3},
"impresionGlobal": {"descriptiva": null, "afectiva": 3}
}'::jsonb
) RETURNING id, puntaje_final INTO test_eval_id, initial_score;
RAISE NOTICE ' ✓ Puntaje inicial: %', initial_score;
-- Actualizar a puntaje 40 (8*5)
UPDATE evaluacion
SET intensidades = '{
"fragancia": {"descriptiva": 5, "afectiva": 5},
"aroma": {"descriptiva": 5, "afectiva": 5},
"sabor": {"descriptiva": 5, "afectiva": 5},
"saborResidual": {"descriptiva": 5, "afectiva": 5},
"acidez": {"descriptiva": 5, "afectiva": 5},
"dulzor": {"descriptiva": 5, "afectiva": 5},
"sensacionBoca": {"descriptiva": 5, "afectiva": 5},
"impresionGlobal": {"descriptiva": null, "afectiva": 5}
}'::jsonb
WHERE id = test_eval_id;
SELECT puntaje_final INTO updated_score
FROM evaluacion
WHERE id = test_eval_id;
IF updated_score = expected_updated_score THEN
RAISE NOTICE ' ✓ Puntaje recalculado correctamente en UPDATE: % -> % (esperado: %)',
initial_score, updated_score, expected_updated_score;
ELSE
RAISE EXCEPTION ' ✗ Puntaje no se recalculó correctamente: % -> % (esperado: %)',
initial_score, updated_score, expected_updated_score;
END IF;
-- Cleanup
DELETE FROM sesion WHERE id = test_sesion_id;
DELETE FROM auth.users WHERE id = test_user_id;
RAISE NOTICE '✓ Trigger puntaje_final se recalcula correctamente en UPDATE';
END $$;
\echo ''
\echo '=========================================='
\echo 'Tests de Triggers completados'
\echo '=========================================='