384 lines
15 KiB
PHP
384 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Tests para WhatsAppService y BotService
|
|
* Fecha: 3 de enero de 2026
|
|
*/
|
|
|
|
class ServiceTestSuite {
|
|
private $whatsappService;
|
|
private $botService;
|
|
private $db;
|
|
|
|
public function __construct() {
|
|
$this->db = Database::getInstance();
|
|
}
|
|
|
|
public function runAllServiceTests() {
|
|
echo "<h2>🔧 Tests Exhaustivos de Servicios</h2>";
|
|
|
|
$this->testWhatsAppService();
|
|
$this->testBotService();
|
|
$this->testWebhookClass();
|
|
}
|
|
|
|
private function testWhatsAppService() {
|
|
echo "<h3>📱 Tests WhatsAppService</h3>";
|
|
|
|
$this->serviceTest("Inicialización WhatsAppService", function() {
|
|
$this->whatsappService = new WhatsAppService();
|
|
|
|
if (!$this->whatsappService) {
|
|
throw new Exception("No se pudo crear instancia de WhatsAppService");
|
|
}
|
|
|
|
// Verificar propiedades mediante reflexión
|
|
$reflection = new ReflectionClass($this->whatsappService);
|
|
$properties = $reflection->getProperties();
|
|
|
|
$requiredProps = ['token', 'phoneNumberId', 'apiUrl', 'db'];
|
|
foreach ($requiredProps as $prop) {
|
|
$found = false;
|
|
foreach ($properties as $property) {
|
|
if ($property->getName() === $prop) {
|
|
$found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$found) {
|
|
throw new Exception("Propiedad requerida '$prop' no encontrada");
|
|
}
|
|
}
|
|
|
|
return "WhatsAppService inicializado con todas las propiedades";
|
|
});
|
|
|
|
$this->serviceTest("Método formatPhoneNumber", function() {
|
|
if (!$this->whatsappService) {
|
|
$this->whatsappService = new WhatsAppService();
|
|
}
|
|
|
|
$reflection = new ReflectionClass($this->whatsappService);
|
|
$method = $reflection->getMethod('formatPhoneNumber');
|
|
$method->setAccessible(true);
|
|
|
|
// Tests de diferentes formatos
|
|
$tests = [
|
|
['input' => '573001234567', 'expected' => '573001234567'],
|
|
['input' => '+573001234567', 'expected' => '573001234567'],
|
|
['input' => '57 300 123 4567', 'expected' => '573001234567'],
|
|
['input' => '+57 (300) 123-4567', 'expected' => '573001234567'],
|
|
];
|
|
|
|
foreach ($tests as $test) {
|
|
$result = $method->invokeArgs($this->whatsappService, [$test['input']]);
|
|
if ($result !== $test['expected']) {
|
|
throw new Exception("Formato incorrecto para '{$test['input']}': esperado '{$test['expected']}', obtenido '$result'");
|
|
}
|
|
}
|
|
|
|
return "Formateo de números funciona para todos los casos";
|
|
});
|
|
|
|
$this->serviceTest("Validación de estructura de mensaje de texto", function() {
|
|
if (!$this->whatsappService) {
|
|
$this->whatsappService = new WhatsAppService();
|
|
}
|
|
|
|
$reflection = new ReflectionClass($this->whatsappService);
|
|
|
|
// Verificar que el método sendTextMessage existe
|
|
if (!$reflection->hasMethod('sendTextMessage')) {
|
|
throw new Exception("Método sendTextMessage no existe");
|
|
}
|
|
|
|
return "Método sendTextMessage existe y es accesible";
|
|
});
|
|
|
|
$this->serviceTest("Validación de estructura de template message", function() {
|
|
if (!$this->whatsappService) {
|
|
$this->whatsappService = new WhatsAppService();
|
|
}
|
|
|
|
$reflection = new ReflectionClass($this->whatsappService);
|
|
|
|
// Verificar que el método sendTemplateMessage existe
|
|
if (!$reflection->hasMethod('sendTemplateMessage')) {
|
|
throw new Exception("Método sendTemplateMessage no existe");
|
|
}
|
|
|
|
$method = $reflection->getMethod('sendTemplateMessage');
|
|
$params = $method->getParameters();
|
|
|
|
// Verificar parámetros esperados
|
|
$expectedParams = ['to', 'templateName', 'language', 'parameters'];
|
|
if (count($params) < 2) {
|
|
throw new Exception("sendTemplateMessage debe tener al menos 2 parámetros");
|
|
}
|
|
|
|
return "Método sendTemplateMessage tiene estructura correcta";
|
|
});
|
|
|
|
$this->serviceTest("Test método buildMessage", function() {
|
|
if (!$this->whatsappService) {
|
|
$this->whatsappService = new WhatsAppService();
|
|
}
|
|
|
|
$reflection = new ReflectionClass($this->whatsappService);
|
|
|
|
if ($reflection->hasMethod('buildMessage')) {
|
|
$method = $reflection->getMethod('buildMessage');
|
|
$method->setAccessible(true);
|
|
|
|
// Test con datos de texto
|
|
$textData = [
|
|
'messaging_product' => 'whatsapp',
|
|
'to' => '573001234567',
|
|
'type' => 'text',
|
|
'text' => ['body' => 'Test message']
|
|
];
|
|
|
|
$result = $method->invokeArgs($this->whatsappService, [$textData]);
|
|
|
|
if ($result !== 'Test message') {
|
|
throw new Exception("buildMessage no procesó correctamente mensaje de texto");
|
|
}
|
|
|
|
return "buildMessage funciona correctamente";
|
|
}
|
|
|
|
return "Método buildMessage no existe (puede ser privado o diferente implementación)";
|
|
});
|
|
|
|
$this->serviceTest("Configuración de tokens", function() {
|
|
if (!defined('WHATSAPP_TOKEN') || empty(WHATSAPP_TOKEN) || WHATSAPP_TOKEN === 'TU_TOKEN_DE_WHATSAPP_AQUI') {
|
|
throw new Exception("WHATSAPP_TOKEN no está configurado correctamente");
|
|
}
|
|
|
|
if (!defined('WHATSAPP_PHONE_NUMBER_ID') || empty(WHATSAPP_PHONE_NUMBER_ID) || WHATSAPP_PHONE_NUMBER_ID === 'TU_PHONE_ID_AQUI') {
|
|
throw new Exception("WHATSAPP_PHONE_NUMBER_ID no está configurado correctamente");
|
|
}
|
|
|
|
if (!defined('WHATSAPP_API_URL') || empty(WHATSAPP_API_URL)) {
|
|
throw new Exception("WHATSAPP_API_URL no está configurado");
|
|
}
|
|
|
|
return "Todos los tokens de WhatsApp están configurados";
|
|
});
|
|
}
|
|
|
|
private function testBotService() {
|
|
echo "<h3>🤖 Tests BotService</h3>";
|
|
|
|
$this->serviceTest("Inicialización BotService", function() {
|
|
$this->botService = new BotService();
|
|
|
|
if (!$this->botService) {
|
|
throw new Exception("No se pudo crear instancia de BotService");
|
|
}
|
|
|
|
return "BotService inicializado correctamente";
|
|
});
|
|
|
|
$this->serviceTest("Método processMessage existe", function() {
|
|
if (!$this->botService) {
|
|
$this->botService = new BotService();
|
|
}
|
|
|
|
$reflection = new ReflectionClass($this->botService);
|
|
|
|
if (!$reflection->hasMethod('processMessage')) {
|
|
throw new Exception("Método processMessage no existe");
|
|
}
|
|
|
|
$method = $reflection->getMethod('processMessage');
|
|
$params = $method->getParameters();
|
|
|
|
if (count($params) < 1) {
|
|
throw new Exception("processMessage debe recibir al menos 1 parámetro");
|
|
}
|
|
|
|
return "Método processMessage tiene estructura correcta";
|
|
});
|
|
|
|
$this->serviceTest("Test de respuestas automáticas", function() {
|
|
// Verificar que hay respuestas automáticas configuradas
|
|
$responses = $this->db->fetchAll(
|
|
"SELECT * FROM autoresponses WHERE trigger_type = 'keyword'"
|
|
);
|
|
|
|
if (empty($responses)) {
|
|
throw new Exception("No hay respuestas automáticas configuradas");
|
|
}
|
|
|
|
// Verificar estructura de respuestas
|
|
$firstResponse = $responses[0];
|
|
$requiredFields = ['trigger_value', 'response_text'];
|
|
|
|
foreach ($requiredFields as $field) {
|
|
if (!isset($firstResponse[$field])) {
|
|
throw new Exception("Campo requerido '$field' faltante en autoresponses");
|
|
}
|
|
}
|
|
|
|
return "Respuestas automáticas configuradas correctamente: " . count($responses) . " respuestas";
|
|
});
|
|
|
|
$this->serviceTest("Test procesamiento de menús", function() {
|
|
// Verificar que hay menús configurados
|
|
$menus = $this->db->fetchAll(
|
|
"SELECT * FROM menus WHERE is_active = 1"
|
|
);
|
|
|
|
if (empty($menus)) {
|
|
throw new Exception("No hay menús activos configurados");
|
|
}
|
|
|
|
// Verificar que el menú raíz existe
|
|
$rootMenu = $this->db->fetch(
|
|
"SELECT * FROM menus WHERE is_root = 1 AND is_active = 1"
|
|
);
|
|
|
|
if (!$rootMenu) {
|
|
throw new Exception("No hay menú raíz configurado");
|
|
}
|
|
|
|
// Verificar opciones del menú raíz
|
|
$options = $this->db->fetchAll(
|
|
"SELECT * FROM menu_options WHERE menu_id = :id ORDER BY option_number",
|
|
['id' => $rootMenu['id']]
|
|
);
|
|
|
|
return "Sistema de menús configurado: " . count($menus) . " menús, " . count($options) . " opciones en menú raíz";
|
|
});
|
|
|
|
$this->serviceTest("Test gestión de usuarios", function() {
|
|
// Generar teléfono de prueba único para evitar colisiones
|
|
$testPhone = '573' . rand(100000000, 999999999);
|
|
|
|
// Asegurar limpieza previa (por si quedó un registro previo con mismo número)
|
|
$this->db->execute("DELETE FROM users WHERE phone_number = :phone", ['phone' => $testPhone]);
|
|
|
|
// Crear usuario de prueba
|
|
$userId = $this->db->insert('users', [
|
|
'phone_number' => $testPhone,
|
|
'name' => 'Usite',
|
|
'status' => 'active',
|
|
'current_step' => 0,
|
|
'created_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
|
|
if (!$userId) {
|
|
throw new Exception("No se pudo crear usuario de prueba");
|
|
}
|
|
|
|
// Verificar que se creó correctamente
|
|
$user = $this->db->fetch(
|
|
"SELECT * FROM users WHERE id = :id",
|
|
['id' => $userId]
|
|
);
|
|
|
|
if ($user['phone_number'] !== $testPhone) {
|
|
throw new Exception("Usuario no se creó correctamente");
|
|
}
|
|
|
|
// Test actualización de estado
|
|
$updated = $this->db->update('users',
|
|
['current_step' => 1],
|
|
'id = :id',
|
|
['id' => $userId]
|
|
);
|
|
|
|
if (!$updated) {
|
|
throw new Exception("No se pudo actualizar usuario");
|
|
}
|
|
|
|
// Limpiar
|
|
$this->db->execute("DELETE FROM users WHERE id = :id", ['id' => $userId]);
|
|
|
|
return "Gestión de usuarios funciona correctamente";
|
|
});
|
|
}
|
|
|
|
private function testWebhookClass() {
|
|
echo "<h3>🔗 Tests Clase WhatsAppWebhook</h3>";
|
|
|
|
$this->serviceTest("Clase WhatsAppWebhook existe", function() {
|
|
if (!class_exists('WhatsAppWebhook')) {
|
|
// Intentar cargar el archivo
|
|
if (file_exists(__DIR__ . '/../api/webhook.php')) {
|
|
// Cambiar directorio temporalmente
|
|
$originalDir = getcwd();
|
|
chdir(__DIR__ . '/..');
|
|
|
|
include_once 'api/webhook.php';
|
|
|
|
// Restaurar directorio
|
|
chdir($originalDir);
|
|
}
|
|
|
|
if (!class_exists('WhatsAppWebhook')) {
|
|
throw new Exception("Clase WhatsAppWebhook no existe");
|
|
}
|
|
}
|
|
|
|
return "Clase WhatsAppWebhook disponible";
|
|
});
|
|
|
|
$this->serviceTest("Inicialización WhatsAppWebhook", function() {
|
|
$webhook = new WhatsAppWebhook();
|
|
|
|
if (!$webhook) {
|
|
throw new Exception("No se pudo crear instancia de WhatsAppWebhook");
|
|
}
|
|
|
|
return "WhatsAppWebhook inicializado correctamente";
|
|
});
|
|
|
|
$this->serviceTest("Métodos requeridos en webhook", function() {
|
|
$webhook = new WhatsAppWebhook();
|
|
$reflection = new ReflectionClass($webhook);
|
|
|
|
$requiredMethods = ['handleRequest'];
|
|
|
|
foreach ($requiredMethods as $method) {
|
|
if (!$reflection->hasMethod($method)) {
|
|
throw new Exception("Método requerido '$method' no existe en WhatsAppWebhook");
|
|
}
|
|
}
|
|
|
|
return "Webhook tiene todos los métodos requeridos";
|
|
});
|
|
|
|
$this->serviceTest("Token de verificación webhook", function() {
|
|
if (!defined('WEBHOOK_VERIFY_TOKEN') || empty(WEBHOOK_VERIFY_TOKEN)) {
|
|
throw new Exception("WEBHOOK_VERIFY_TOKEN no está definido");
|
|
}
|
|
|
|
if (WEBHOOK_VERIFY_TOKEN === 'mi_token_secreto_123') {
|
|
return "⚠️ Usando token por defecto - cambiar en producción";
|
|
}
|
|
|
|
return "Token de webhook configurado correctamente";
|
|
});
|
|
}
|
|
|
|
private function serviceTest($name, $callable) {
|
|
try {
|
|
$result = $callable();
|
|
echo "<div class='alert alert-success'><i class='fas fa-check-circle'></i> <strong>$name:</strong> $result</div>";
|
|
} catch (Exception $e) {
|
|
echo "<div class='alert alert-danger'><i class='fas fa-times-circle'></i> <strong>$name:</strong> " . $e->getMessage() . "</div>";
|
|
}
|
|
}
|
|
}
|
|
|
|
// No ejecutar si es incluido por el runner principal
|
|
if (basename($_SERVER['PHP_SELF']) === 'service_tests.php') {
|
|
$configPath = __DIR__ . '/../config/config.php';
|
|
if (file_exists($configPath)) {
|
|
require_once $configPath;
|
|
}
|
|
$suite = new ServiceTestSuite();
|
|
$suite->runAllServiceTests();
|
|
}
|
|
?>
|