fix(tests): sin pdo_sqlite — db() falso en PHP puro

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 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-19 22:30:56 -05:00
co-authored by Claude Sonnet 4.6
parent 1f7d33b40d
commit 9ae4b0dee3
2 changed files with 61 additions and 19 deletions
+59 -16
View File
@@ -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 ───────────────────────────────────────────