import { test, before, after } from "node:test"; import assert from "node:assert/strict"; import { pool } from "../db/pool.ts"; import { createApp } from "../index.ts"; import { resetDb, seedMinimal } from "./helpers.ts"; import { esEntidad, ENTIDADES } from "../crm/syncOne.ts"; import type { Server } from "node:http"; process.env.CRM_MASTER_KEY = Buffer.alloc(32, 11).toString("base64"); let ids: Awaited>; let server: Server; let base: string; before(async () => { await resetDb(); ids = await seedMinimal(); server = createApp().listen(0); base = `http://127.0.0.1:${(server.address() as { port: number }).port}`; }); after(async () => { server.close(); await pool.end(); }); const req = (path: string, init: RequestInit = {}) => fetch(`${base}${path}`, { ...init, headers: { "content-type": "application/json", authorization: `Bearer ${ids.ownerUserId}`, ...(init.headers || {}), }, }); test("esEntidad acepta solo las cinco entidades del encargo", () => { for (const e of ENTIDADES) assert.ok(esEntidad(e)); assert.equal(esEntidad("cliente"), false); assert.equal(esEntidad(""), false); assert.equal(esEntidad("../../etc/passwd"), false); assert.equal(esEntidad("CONTACTO"), false); }); test("ENTIDADES son exactamente las cinco, ni una más", () => { assert.deepEqual( [...ENTIDADES].sort(), ["cita", "contacto", "conversacion", "mensaje", "servicio"] ); }); test("una entidad inventada da 400 y dice cuáles valen", async () => { const r = await req("/api/crm/sync/pedido/abc", { method: "POST" }); assert.equal(r.status, 400); const b = await r.json(); assert.match(b.error, /contacto/); assert.match(b.error, /servicio/); }); test("un identificador desmesurado se rechaza antes de salir a la red", async () => { const r = await req(`/api/crm/sync/contacto/${"x".repeat(200)}`, { method: "POST" }); assert.equal(r.status, 400); assert.match((await r.json()).error, /demasiado largo/i); }); test("sin vínculo con el CRM, sincronizar por id da 409, no 500", async () => { const r = await req("/api/crm/sync/contacto/abc123", { method: "POST" }); assert.equal(r.status, 409); assert.match((await r.json()).error, /no está vinculado/i); }); test("el espejo de conversaciones sin vínculo también da 409", async () => { const r = await req("/api/crm/sync/conversations", { method: "POST" }); assert.equal(r.status, 409); }); test("la cita exige un identificador numérico de la plataforma", async () => { // Se vincula con credencial falsa: basta para pasar de `ctxDe` y llegar a la // validación del identificador, que es lo que se prueba aquí. const { guardarCredencial } = await import("../crm/ctx.ts"); await guardarCredencial(ids.businessId, "loc-x", "tok-x"); const r = await req("/api/crm/sync/cita/no-es-un-numero", { method: "POST" }); assert.equal(r.status, 400); assert.match((await r.json()).error, /numérico/i); });