692 lines
25 KiB
PHP
692 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* Tests específicos para el Sistema de Plantillas
|
|
* Fecha: 3 de enero de 2026
|
|
*/
|
|
|
|
class TemplateTestSuite {
|
|
private $db;
|
|
private $testTemplateIds = [];
|
|
|
|
public function __construct() {
|
|
$this->db = Database::getInstance();
|
|
}
|
|
|
|
public function runAllTemplateTests() {
|
|
echo "<h2>📝 Tests Exhaustivos de Sistema de Plantillas</h2>";
|
|
|
|
$this->testTemplateDatabase();
|
|
$this->testTemplateAPIs();
|
|
$this->testTemplateValidation();
|
|
$this->testTemplateIntegration();
|
|
$this->testTemplateWorkflow();
|
|
|
|
// Limpiar plantillas de prueba
|
|
$this->cleanup();
|
|
}
|
|
|
|
private function testTemplateDatabase() {
|
|
echo "<h3>💾 Tests de Base de Datos de Plantillas</h3>";
|
|
|
|
$this->templateTest("Estructura tabla message_templates", function() {
|
|
$columns = $this->db->fetchAll("DESCRIBE message_templates");
|
|
|
|
$requiredColumns = [
|
|
'id', 'name', 'template_name', 'language_code',
|
|
'category', 'status', 'created_at'
|
|
];
|
|
|
|
$existingColumns = array_column($columns, 'Field');
|
|
|
|
foreach ($requiredColumns as $col) {
|
|
if (!in_array($col, $existingColumns)) {
|
|
throw new Exception("Columna requerida '$col' faltante");
|
|
}
|
|
}
|
|
|
|
// Verificar tipos de datos
|
|
$statusColumn = array_filter($columns, function($col) {
|
|
return $col['Field'] === 'status';
|
|
});
|
|
|
|
$statusColumn = array_values($statusColumn)[0];
|
|
|
|
if (strpos($statusColumn['Type'], 'enum') === false) {
|
|
throw new Exception("Columna status debería ser ENUM");
|
|
}
|
|
|
|
if (strpos($statusColumn['Type'], 'pending') === false ||
|
|
strpos($statusColumn['Type'], 'approved') === false ||
|
|
strpos($statusColumn['Type'], 'rejected') === false) {
|
|
throw new Exception("Estados de plantilla incorrectos en ENUM");
|
|
}
|
|
|
|
return "Estructura de tabla message_templates correcta";
|
|
});
|
|
|
|
$this->templateTest("CRUD básico en message_templates", function() {
|
|
$testName = 'Test CRUD Template ' . time();
|
|
|
|
// CREATE
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => $testName,
|
|
'template_name' => 'test_crud_' . time(),
|
|
'language_code' => 'es',
|
|
'category' => 'utility',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
if (!$id) {
|
|
throw new Exception("No se pudo insertar plantilla");
|
|
}
|
|
|
|
$this->testTemplateIds[] = $id;
|
|
|
|
// READ
|
|
$template = $this->db->fetch(
|
|
"SELECT * FROM message_templates WHERE id = :id",
|
|
['id' => $id]
|
|
);
|
|
|
|
if ($template['name'] !== $testName) {
|
|
throw new Exception("Datos incorrectos al leer plantilla");
|
|
}
|
|
|
|
// UPDATE
|
|
$updated = $this->db->update('message_templates',
|
|
['status' => 'approved'],
|
|
['id' => $id]
|
|
);
|
|
|
|
if (!$updated) {
|
|
throw new Exception("No se pudo actualizar plantilla");
|
|
}
|
|
|
|
// Verificar actualización
|
|
$updatedTemplate = $this->db->fetch(
|
|
"SELECT status FROM message_templates WHERE id = :id",
|
|
['id' => $id]
|
|
);
|
|
|
|
if ($updatedTemplate['status'] !== 'approved') {
|
|
throw new Exception("Estado no se actualizó correctamente");
|
|
}
|
|
|
|
// DELETE se hace en cleanup()
|
|
|
|
return "CRUD completo en message_templates funciona";
|
|
});
|
|
|
|
$this->templateTest("Constrains y validaciones", function() {
|
|
// Test unicidad de nombre
|
|
$duplicateName = 'Unique Test ' . time();
|
|
|
|
$id1 = $this->db->insert('message_templates', [
|
|
'name' => $duplicateName,
|
|
'template_name' => 'unique_test_1',
|
|
'language_code' => 'es',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
$this->testTemplateIds[] = $id1;
|
|
|
|
// Intentar insertar duplicado
|
|
try {
|
|
$id2 = $this->db->insert('message_templates', [
|
|
'name' => $duplicateName,
|
|
'template_name' => 'unique_test_2',
|
|
'language_code' => 'es',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
if ($id2) {
|
|
$this->testTemplateIds[] = $id2;
|
|
throw new Exception("Se permitió nombre duplicado");
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
// Error esperado por constraint
|
|
if (strpos($e->getMessage(), 'Duplicate') !== false ||
|
|
strpos($e->getMessage(), 'UNIQUE') !== false) {
|
|
return "Constraint de unicidad funciona correctamente";
|
|
}
|
|
throw $e;
|
|
}
|
|
|
|
return "Validaciones de base de datos funcionan";
|
|
});
|
|
}
|
|
|
|
private function testTemplateAPIs() {
|
|
echo "<h3>🔌 Tests de APIs de Plantillas</h3>";
|
|
|
|
// Configurar sesión de admin
|
|
$_SESSION['admin_logged_in'] = true;
|
|
$_SESSION['last_activity'] = time();
|
|
|
|
$this->templateTest("API get_templates.php", function() {
|
|
ob_start();
|
|
include 'api/get_templates.php';
|
|
$output = ob_get_clean();
|
|
|
|
$templates = json_decode($output, true);
|
|
|
|
if (!is_array($templates)) {
|
|
throw new Exception("get_templates debe retornar array JSON");
|
|
}
|
|
|
|
return "API get_templates responde correctamente";
|
|
});
|
|
|
|
$this->templateTest("API save_template.php - plantilla válida", function() {
|
|
$testData = [
|
|
'name' => 'API Test Template ' . time(),
|
|
'template_name' => 'api_test_' . time(),
|
|
'language_code' => 'es',
|
|
'category' => 'utility'
|
|
];
|
|
|
|
$_POST = $testData;
|
|
$GLOBALS['HTTP_RAW_POST_DATA'] = json_encode($testData);
|
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
|
|
ob_start();
|
|
|
|
try {
|
|
include 'api/save_template.php';
|
|
$output = ob_get_clean();
|
|
|
|
$result = json_decode($output, true);
|
|
|
|
if (!$result['success']) {
|
|
throw new Exception("Error creando plantilla: " . ($result['error'] ?? 'Unknown'));
|
|
}
|
|
|
|
// Verificar que se creó en BD
|
|
$created = $this->db->fetch(
|
|
"SELECT * FROM message_templates WHERE name = :name",
|
|
['name' => $testData['name']]
|
|
);
|
|
|
|
if (!$created) {
|
|
throw new Exception("Plantilla no se guardó en base de datos");
|
|
}
|
|
|
|
$this->testTemplateIds[] = $created['id'];
|
|
|
|
return "API save_template crea plantilla correctamente";
|
|
|
|
} finally {
|
|
unset($_POST);
|
|
unset($GLOBALS['HTTP_RAW_POST_DATA']);
|
|
ob_end_clean();
|
|
}
|
|
});
|
|
|
|
$this->templateTest("API save_template.php - validaciones", function() {
|
|
$invalidData = [
|
|
['name' => '', 'template_name' => 'test', 'error_expected' => 'nombre es requerido'],
|
|
['name' => 'Test', 'template_name' => '', 'error_expected' => 'nombre de la plantilla'],
|
|
[/* datos vacíos */ 'error_expected' => 'inválidos']
|
|
];
|
|
|
|
foreach ($invalidData as $testCase) {
|
|
$_POST = $testCase;
|
|
unset($testCase['error_expected']);
|
|
$GLOBALS['HTTP_RAW_POST_DATA'] = json_encode($testCase);
|
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
|
|
ob_start();
|
|
|
|
try {
|
|
include 'api/save_template.php';
|
|
$output = ob_get_clean();
|
|
|
|
$result = json_decode($output, true);
|
|
|
|
if ($result['success']) {
|
|
throw new Exception("Debería rechazar datos inválidos");
|
|
}
|
|
|
|
} finally {
|
|
unset($_POST);
|
|
unset($GLOBALS['HTTP_RAW_POST_DATA']);
|
|
}
|
|
}
|
|
|
|
return "API save_template valida datos correctamente";
|
|
});
|
|
|
|
$this->templateTest("API update_template_status.php", function() {
|
|
// Crear plantilla para probar
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => 'Status Update Test',
|
|
'template_name' => 'status_update_test',
|
|
'language_code' => 'es',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
$this->testTemplateIds[] = $id;
|
|
|
|
$updateData = [
|
|
'id' => $id,
|
|
'status' => 'approved'
|
|
];
|
|
|
|
$_POST = $updateData;
|
|
$GLOBALS['HTTP_RAW_POST_DATA'] = json_encode($updateData);
|
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
|
|
ob_start();
|
|
|
|
try {
|
|
include 'api/update_template_status.php';
|
|
$output = ob_get_clean();
|
|
|
|
$result = json_decode($output, true);
|
|
|
|
if (!$result['success']) {
|
|
throw new Exception("Error actualizando estado: " . ($result['error'] ?? 'Unknown'));
|
|
}
|
|
|
|
// Verificar actualización en BD
|
|
$updated = $this->db->fetch(
|
|
"SELECT status FROM message_templates WHERE id = :id",
|
|
['id' => $id]
|
|
);
|
|
|
|
if ($updated['status'] !== 'approved') {
|
|
throw new Exception("Estado no se actualizó en base de datos");
|
|
}
|
|
|
|
return "API update_template_status funciona correctamente";
|
|
|
|
} finally {
|
|
unset($_POST);
|
|
unset($GLOBALS['HTTP_RAW_POST_DATA']);
|
|
}
|
|
});
|
|
}
|
|
|
|
private function testTemplateValidation() {
|
|
echo "<h3">✅ Tests de Validación de Plantillas</h3>";
|
|
|
|
$this->templateTest("Validación de nombres", function() {
|
|
$invalidNames = [
|
|
'', // Vacío
|
|
str_repeat('a', 200), // Muy largo
|
|
'<script>alert(1)</script>', // XSS
|
|
'SELECT * FROM users', // SQL-like
|
|
'template with \x00 null byte'
|
|
];
|
|
|
|
foreach ($invalidNames as $name) {
|
|
try {
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => $name,
|
|
'template_name' => 'test_validation_' . time(),
|
|
'language_code' => 'es',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
if ($id) {
|
|
$this->testTemplateIds[] = $id;
|
|
|
|
// Verificar que se sanitizó o rechazó apropiadamente
|
|
$saved = $this->db->fetch(
|
|
"SELECT name FROM message_templates WHERE id = :id",
|
|
['id' => $id]
|
|
);
|
|
|
|
if ($saved['name'] === $name && (
|
|
strpos($name, '<script>') !== false ||
|
|
strlen($name) > 100 ||
|
|
empty($name)
|
|
)) {
|
|
throw new Exception("Nombre inválido '$name' no fue filtrado");
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
// Error esperado para nombres inválidos
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return "Validación de nombres de plantillas funciona";
|
|
});
|
|
|
|
$this->templateTest("Validación de estados", function() {
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => 'State Test',
|
|
'template_name' => 'state_test',
|
|
'language_code' => 'es',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
$this->testTemplateIds[] = $id;
|
|
|
|
// Estados válidos
|
|
$validStates = ['pending', 'approved', 'rejected'];
|
|
|
|
foreach ($validStates as $state) {
|
|
$updated = $this->db->update('message_templates',
|
|
['status' => $state],
|
|
['id' => $id]
|
|
);
|
|
|
|
if (!$updated) {
|
|
throw new Exception("No se pudo actualizar a estado válido: $state");
|
|
}
|
|
}
|
|
|
|
// Estado inválido
|
|
try {
|
|
$this->db->update('message_templates',
|
|
['status' => 'invalid_status'],
|
|
['id' => $id]
|
|
);
|
|
|
|
throw new Exception("Estado inválido fue aceptado");
|
|
|
|
} catch (Exception $e) {
|
|
// Error esperado por constraint ENUM
|
|
if (strpos($e->getMessage(), 'invalid_status') === false) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
return "Validación de estados funciona correctamente";
|
|
});
|
|
|
|
$this->templateTest("Validación de idiomas", function() {
|
|
$validLanguages = ['es', 'en', 'pt', 'fr'];
|
|
$invalidLanguages = ['', 'español', 'eng', 'xyz123'];
|
|
|
|
// Idiomas válidos
|
|
foreach ($validLanguages as $lang) {
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => "Lang Test $lang",
|
|
'template_name' => "lang_test_$lang",
|
|
'language_code' => $lang,
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
if (!$id) {
|
|
throw new Exception("Idioma válido '$lang' rechazado");
|
|
}
|
|
|
|
$this->testTemplateIds[] = $id;
|
|
}
|
|
|
|
// Idiomas inválidos (dependiendo de validación implementada)
|
|
foreach ($invalidLanguages as $lang) {
|
|
try {
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => "Invalid Lang Test $lang",
|
|
'template_name' => "invalid_lang_$lang",
|
|
'language_code' => $lang,
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
if ($id) {
|
|
$this->testTemplateIds[] = $id;
|
|
// Por ahora, idiomas inválidos pueden ser aceptados
|
|
// En futuras versiones se puede agregar validación más estricta
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
// Error esperado para idiomas inválidos
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return "Validación de idiomas (básica implementada)";
|
|
});
|
|
}
|
|
|
|
private function testTemplateIntegration() {
|
|
echo "<h3">🔗 Tests de Integración de Plantillas</h3>";
|
|
|
|
$this->templateTest("Integración con WhatsAppService", function() {
|
|
// Crear plantilla aprobada
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => 'Integration Test',
|
|
'template_name' => 'integration_test',
|
|
'language_code' => 'es',
|
|
'category' => 'utility',
|
|
'status' => 'approved'
|
|
]);
|
|
|
|
$this->testTemplateIds[] = $id;
|
|
|
|
$whatsappService = new WhatsAppService();
|
|
|
|
// Verificar que el método sendTemplateMessage existe y acepta los parámetros
|
|
$reflection = new ReflectionClass($whatsappService);
|
|
|
|
if (!$reflection->hasMethod('sendTemplateMessage')) {
|
|
throw new Exception("WhatsAppService no tiene método sendTemplateMessage");
|
|
}
|
|
|
|
$method = $reflection->getMethod('sendTemplateMessage');
|
|
$params = $method->getParameters();
|
|
|
|
if (count($params) < 2) {
|
|
throw new Exception("sendTemplateMessage debe tener al menos 2 parámetros");
|
|
}
|
|
|
|
// Test estructura de llamada (sin envío real)
|
|
try {
|
|
// Este test no envía realmente, solo verifica estructura
|
|
$result = $whatsappService->sendTemplateMessage(
|
|
'+573001234567',
|
|
'integration_test',
|
|
'es',
|
|
[]
|
|
);
|
|
|
|
// En un entorno real, esto fallaría por token/conectividad
|
|
// Pero la estructura debería estar correcta
|
|
return "Integración WhatsAppService estructura correcta";
|
|
|
|
} catch (Exception $e) {
|
|
// Error esperado por falta de conectividad real
|
|
if (strpos($e->getMessage(), 'cURL') !== false ||
|
|
strpos($e->getMessage(), 'connection') !== false ||
|
|
strpos($e->getMessage(), 'token') !== false) {
|
|
return "Integración WhatsAppService - estructura OK, falla conectividad (esperado)";
|
|
}
|
|
throw $e;
|
|
}
|
|
});
|
|
|
|
$this->templateTest("Filtrado de plantillas por estado", function() {
|
|
// Crear plantillas con diferentes estados
|
|
$states = ['pending', 'approved', 'rejected'];
|
|
$templateIds = [];
|
|
|
|
foreach ($states as $state) {
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => "Filter Test $state",
|
|
'template_name' => "filter_test_$state",
|
|
'language_code' => 'es',
|
|
'status' => $state
|
|
]);
|
|
|
|
$templateIds[] = $id;
|
|
$this->testTemplateIds[] = $id;
|
|
}
|
|
|
|
// Test filtrar solo aprobadas
|
|
$approved = $this->db->fetchAll(
|
|
"SELECT * FROM message_templates WHERE status = 'approved' AND name LIKE 'Filter Test%'"
|
|
);
|
|
|
|
if (count($approved) !== 1) {
|
|
throw new Exception("Filtro de plantillas aprobadas no funciona");
|
|
}
|
|
|
|
// Test filtrar todas
|
|
$all = $this->db->fetchAll(
|
|
"SELECT * FROM message_templates WHERE name LIKE 'Filter Test%'"
|
|
);
|
|
|
|
if (count($all) !== 3) {
|
|
throw new Exception("No se crearon todas las plantillas de prueba");
|
|
}
|
|
|
|
return "Filtrado de plantillas por estado funciona";
|
|
});
|
|
}
|
|
|
|
private function testTemplateWorkflow() {
|
|
echo "<h3">🔄 Tests de Flujo de Trabajo de Plantillas</h3>";
|
|
|
|
$this->templateTest("Ciclo completo: Crear → Aprobar → Usar", function() {
|
|
// 1. Crear plantilla
|
|
$templateName = 'Workflow Test ' . time();
|
|
$whatsappName = 'workflow_test_' . time();
|
|
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => $templateName,
|
|
'template_name' => $whatsappName,
|
|
'language_code' => 'es',
|
|
'category' => 'utility',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
if (!$id) {
|
|
throw new Exception("No se pudo crear plantilla");
|
|
}
|
|
|
|
$this->testTemplateIds[] = $id;
|
|
|
|
// 2. Verificar estado inicial
|
|
$template = $this->db->fetch(
|
|
"SELECT * FROM message_templates WHERE id = :id",
|
|
['id' => $id]
|
|
);
|
|
|
|
if ($template['status'] !== 'pending') {
|
|
throw new Exception("Estado inicial debería ser 'pending'");
|
|
}
|
|
|
|
// 3. Aprobar plantilla
|
|
$updated = $this->db->update('message_templates',
|
|
['status' => 'approved'],
|
|
['id' => $id]
|
|
);
|
|
|
|
if (!$updated) {
|
|
throw new Exception("No se pudo aprobar plantilla");
|
|
}
|
|
|
|
// 4. Verificar que ahora está disponible para uso
|
|
$approvedTemplate = $this->db->fetch(
|
|
"SELECT * FROM message_templates WHERE id = :id AND status = 'approved'",
|
|
['id' => $id]
|
|
);
|
|
|
|
if (!$approvedTemplate) {
|
|
throw new Exception("Plantilla aprobada no está disponible");
|
|
}
|
|
|
|
// 5. Simular uso en envío de mensaje
|
|
$_SESSION['admin_logged_in'] = true;
|
|
|
|
$messageData = [
|
|
'recipient' => '+573001234567',
|
|
'type' => 'template',
|
|
'template' => $whatsappName
|
|
];
|
|
|
|
$_POST = $messageData;
|
|
$GLOBALS['HTTP_RAW_POST_DATA'] = json_encode($messageData);
|
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
|
|
ob_start();
|
|
|
|
try {
|
|
include 'api/send_message.php';
|
|
$output = ob_get_clean();
|
|
|
|
$result = json_decode($output, true);
|
|
|
|
// En testing, probablemente falle por conectividad, pero la validación debería pasar
|
|
if (isset($result['error']) && strpos($result['error'], $whatsappName) === false) {
|
|
// Si no menciona la plantilla en el error, es porque la validación pasó
|
|
return "Ciclo completo de plantilla: Crear → Aprobar → Validar uso";
|
|
}
|
|
|
|
if ($result['success']) {
|
|
return "Ciclo completo de plantilla exitoso (conexión real funcionando)";
|
|
}
|
|
|
|
return "Ciclo de plantilla completado - falla esperada en envío real";
|
|
|
|
} finally {
|
|
unset($_POST);
|
|
unset($GLOBALS['HTTP_RAW_POST_DATA']);
|
|
ob_end_clean();
|
|
}
|
|
});
|
|
|
|
$this->templateTest("Rechazo de plantillas no aprobadas", function() {
|
|
// Crear plantilla pendiente
|
|
$id = $this->db->insert('message_templates', [
|
|
'name' => 'Pending Test',
|
|
'template_name' => 'pending_test',
|
|
'language_code' => 'es',
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
$this->testTemplateIds[] = $id;
|
|
|
|
// Intentar usar plantilla no aprobada
|
|
$pendingTemplates = $this->db->fetchAll(
|
|
"SELECT * FROM message_templates WHERE status = 'approved' AND template_name = 'pending_test'"
|
|
);
|
|
|
|
if (!empty($pendingTemplates)) {
|
|
throw new Exception("Plantilla pendiente aparece como aprobada");
|
|
}
|
|
|
|
return "Plantillas no aprobadas correctamente excluidas";
|
|
});
|
|
}
|
|
|
|
private function templateTest($name, $callable) {
|
|
try {
|
|
$result = $callable();
|
|
echo "<div class='alert alert-success'><i class='fas fa-check'></i> <strong>$name:</strong> $result</div>";
|
|
} catch (Exception $e) {
|
|
echo "<div class='alert alert-danger'><i class='fas fa-times'></i> <strong>$name:</strong> " . $e->getMessage() . "</div>";
|
|
}
|
|
}
|
|
|
|
private function cleanup() {
|
|
foreach ($this->testTemplateIds as $id) {
|
|
try {
|
|
$this->db->execute("DELETE FROM message_templates WHERE id = :id", ['id' => $id]);
|
|
} catch (Exception $e) {
|
|
// Ignorar errores de limpieza
|
|
}
|
|
}
|
|
}
|
|
|
|
public function __destruct() {
|
|
$this->cleanup();
|
|
}
|
|
}
|
|
|
|
// No ejecutar si es incluido por el runner principal
|
|
if (basename($_SERVER['PHP_SELF']) === 'template_tests.php') {
|
|
$configPath = __DIR__ . '/../config/config.php';
|
|
if (file_exists($configPath)) {
|
|
require_once $configPath;
|
|
}
|
|
$suite = new TemplateTestSuite();
|
|
$suite->runAllTemplateTests();
|
|
}
|
|
?>
|