From 9ae4b0dee3c7cab234b817e281ce1cfbbb2f3648 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 19 Aug 2026 22:30:56 -0500 Subject: [PATCH] =?UTF-8?q?fix(tests):=20sin=20pdo=5Fsqlite=20=E2=80=94=20?= =?UTF-8?q?db()=20falso=20en=20PHP=20puro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El servidor no trae el driver de SQLite y el arnes reventaba al arrancar. NormalBot solo hace dos consultas —el endpoint por clave y el perfil por numero— asi que dos mapas en memoria alcanzan y el arnes deja de depender de cualquier driver de PDO. Co-Authored-By: Claude Sonnet 4.6 --- setup/tests/bootstrap.php | 75 +++++++++++++++++++++++++++++-------- setup/tests/test_flujos.php | 5 +-- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/setup/tests/bootstrap.php b/setup/tests/bootstrap.php index 0d206d4..ac4b035 100644 --- a/setup/tests/bootstrap.php +++ b/setup/tests/bootstrap.php @@ -4,8 +4,8 @@ declare(strict_types=1); /** * Arnés para ejecutar NormalBot de verdad, sin MySQL ni WhatsApp: * - * db() -> SQLite en memoria, con los endpoints del seed - * reescritos hacia el servidor de fixtures + * db() -> falso en PHP puro (sin drivers), con los endpoints + * del seed reescritos hacia el servidor de fixtures * ConversationContext -> estado en memoria, un contexto por teléfono * WhatsAppSender -> captura los envíos directos en ::$enviados * AiBot -> respuestas guionadas en ::$respuestas @@ -15,28 +15,71 @@ declare(strict_types=1); const FIXTURE_BASE = 'http://127.0.0.1:8973/fixtures_api.php'; -// ── db(): SQLite con los endpoints reales del seed ─────────────────────────── -function db(): PDO +// ── db(): falso en PHP puro ────────────────────────────────────────────────── +// Sin SQLite: los servidores no siempre traen pdo_sqlite. NormalBot solo hace +// dos consultas —el endpoint por clave y el perfil por número— así que un par +// de mapas en memoria alcanza y no dependemos de ningún driver. + +class FakeStmt { - static $pdo = null; - if ($pdo !== null) return $pdo; + private $row = false; + public function __construct(private string $sql) {} - $pdo = new PDO('sqlite::memory:', null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); - $pdo->exec("CREATE TABLE company_endpoints ( - company_id INT, endpoint_key TEXT, direction TEXT, url TEXT, - method TEXT DEFAULT 'GET', params TEXT DEFAULT NULL, is_active INT DEFAULT 1)"); - $pdo->exec("CREATE TABLE company_phones ( - company_id INT, wa_number TEXT, label TEXT, permission_type INT, - tercero_id INT NULL, modulos_json TEXT NULL, es_supervisor INT DEFAULT 0, is_active INT DEFAULT 1)"); + public function execute(array $p = []): bool + { + if (str_contains($this->sql, 'company_endpoints')) { + $key = $p[1] ?? ''; + $url = FakeDb::$endpoints[$key] ?? null; + $this->row = $url === null ? false + : ['url' => $url, 'method' => 'GET', 'params' => null]; + } elseif (str_contains($this->sql, 'company_phones')) { + $this->row = FakeDb::$phones[$p[1] ?? ''] ?? false; + } else { + $this->row = false; + } + return true; + } + public function fetch() { return $this->row; } + public function fetchColumn() { return $this->row ? array_values($this->row)[0] : false; } + public function fetchAll(): array { return $this->row ? [$this->row] : []; } +} + +class FakeDb +{ + /** endpoint_key => url absoluta hacia el servidor de fixtures */ + public static array $endpoints = []; + /** wa_number => fila de company_phones */ + public static array $phones = []; + + public function prepare(string $sql): FakeStmt { return new FakeStmt($sql); } + public function query(string $sql): FakeStmt { $s = new FakeStmt($sql); $s->execute(); return $s; } + public function exec(string $sql): int { return 0; } +} + +function db(): FakeDb +{ + static $db = null; + if ($db !== null) return $db; + + $db = new FakeDb(); // Los endpoints salen del seed real: mismas claves y placeholders, otra base. $seed = file_get_contents(dirname(__DIR__) . '/seed_palmas.php'); preg_match_all("/\['key' => '([^']+)',\s*'dir' => '([^']+)',\s*'url' => \\\$BASE \. '([^']*)'/", $seed, $m); - $ins = $pdo->prepare("INSERT INTO company_endpoints (company_id, endpoint_key, direction, url) VALUES (1, ?, ?, ?)"); foreach ($m[1] as $i => $key) { - $ins->execute([$key, $m[2][$i], FIXTURE_BASE . $m[3][$i]]); + FakeDb::$endpoints[$key] = FIXTURE_BASE . $m[3][$i]; } - return $pdo; + return $db; +} + +/** Registra un número con perfil para los escenarios de trabajador/supervisor. */ +function fakePhone(string $wa, ?int $tercero, int $esSupervisor): void +{ + FakeDb::$phones[$wa] = [ + 'tercero_id' => $tercero, + 'modulos_json' => null, + 'es_supervisor' => $esSupervisor, + ]; } // ── ConversationContext en memoria ─────────────────────────────────────────── diff --git a/setup/tests/test_flujos.php b/setup/tests/test_flujos.php index a0c7c9d..5cdbeda 100644 --- a/setup/tests/test_flujos.php +++ b/setup/tests/test_flujos.php @@ -84,9 +84,8 @@ check('y la trazabilidad', $post['body']['telefono'] ?? null, '57300AUSEN'); // ════ 3. Ausentismo según el perfil del número ═══════════════════════════════ echo "\nPerfil — trabajador vinculado y supervisor\n"; -db()->exec("INSERT INTO company_phones (company_id, wa_number, label, permission_type, tercero_id, es_supervisor) - VALUES (1, '57300TRABAJADOR', 'Julio', 1, 412, 0), - (1, '57300JEFE', 'Sup', 3, 412, 1)"); +fakePhone('57300TRABAJADOR', 412, 0); +fakePhone('57300JEFE', 412, 1); $ctx = contexto('57300TRABAJADOR'); $r = NormalBot::processInteractive($co, $ctx, 'registrar_ausentismo');