Skip to main content

Configuración

Importa las utilidades de testing desde @jelou/functions/testing:
import {
  createMockContext,
  createMockCronContext,
  createMockEventContext,
  createMockRequest,
  createMockAuthContext,
  createMockAuthRequest,
  createMockJelouClient,
  createMockMemoryClient,
} from "@jelou/functions/testing";

createMockContext(overrides?)

Crea un context con valores por defecto sensatos para testing.
const ctx = createMockContext();

const ctx2 = createMockContext({
  company: { id: 42, name: "Tienda ABC" },
  user: { id: 99, names: "María García" },
  bot: { id: "bot-123", name: "Bot de Soporte", channel: "whatsapp" },
  env: {
    get: (key) => key === "CRM_API_KEY" ? "sk-test-123" : undefined,
    has: (key) => key === "CRM_API_KEY",
    toObject: () => ({ CRM_API_KEY: "sk-test-123" }),
  },
});

createMockCronContext(expression, overrides?)

Crea un context con isCron: true y trigger.type: "cron".
const ctx = createMockCronContext("0 9 * * *");
// ctx.isCron === true
// ctx.trigger === { type: "cron", cron: "0 9 * * *" }
// ctx.isHttp === false

createMockEventContext(eventName, overrides?)

Crea un context con isEvent: true y trigger.type: "event".
const ctx = createMockEventContext("pago.completado");
// ctx.isEvent === true
// ctx.trigger === { type: "event", event: "pago.completado" }

createMockRequest(body?, options?)

Crea un objeto Web Request estándar.
const req = createMockRequest();
// GET http://localhost:8000/

const req2 = createMockRequest({ telefono: "593987654321" });
// POST con body JSON, Content-Type: application/json

const req3 = createMockRequest(null, {
  method: "PUT",
  url: "https://consultar-cliente.fn.jelou.ai/clientes/42",
  headers: { "x-api-key": "sk-test-123" },
});

createMockAuthContext(overrides?)

Crea un context con auth: { authenticated: true } por defecto. Útil para testing de funciones protegidas.
const ctx = createMockAuthContext();
// ctx.auth === { authenticated: true }

const ctx2 = createMockAuthContext({
  company: { id: 42, name: "Tienda ABC" },
});
// ctx2.auth === { authenticated: true }
// ctx2.company === { id: 42, name: "Tienda ABC" }

createMockAuthRequest(token, body?, options?)

Crea un Request con header Authorization: Bearer <token>.
const req = createMockAuthRequest("mi-token-secreto");
// GET con Authorization: Bearer mi-token-secreto

const req2 = createMockAuthRequest("mi-token", { query: "test" });
// POST con body JSON + Authorization: Bearer mi-token

const req3 = createMockAuthRequest("mi-token", null, {
  method: "PUT",
  url: "https://mi-funcion.fn.jelou.ai/admin",
});

createMockJelouClient(options?)

Crea un mock del cliente de mensajería con grabación de llamadas.
const mockJelou = createMockJelouClient();
const ctx = createMockContext({ jelou: mockJelou });

await ctx.jelou.send({ type: "text", to: "+593987654321", text: "Hola" });

// Inspeccionar llamadas
mockJelou.calls.length;          // 1
mockJelou.calls[0].method;       // "send"
mockJelou.calls[0].args.type;    // "text"
mockJelou.calls[0].timestamp;    // Date.now()

Resultados personalizados

const mockJelou = createMockJelouClient({
  sendResult: { messageId: "msg-custom-123" },
  templateResult: [{ id: "tmpl-1", destination: "+593987654321" }],
});

Reset entre tests

mockJelou.reset(); // limpia las llamadas grabadas

createMockMemoryClient(options?)

Crea un mock del cliente de memoria con un store in-memory.
const mockMemory = createMockMemoryClient({
  store: { paso: "inicio", intentos: 0 },
});
const ctx = createMockContext({ memory: mockMemory });

const paso = await ctx.memory.get("paso", "desconocido"); // "inicio"
await ctx.memory.set("paso", "confirmacion", 3600);

Inspeccionar llamadas

mockMemory.calls.length;         // 2 (get + set)
mockMemory.calls[1].method;      // "set"
mockMemory.calls[1].args;        // ["paso", "confirmacion", 3600]

Reset entre tests

mockMemory.reset(); // limpia las llamadas y restaura el store inicial

Ejemplo completo

index.test.ts
import { assertEquals } from "jsr:@std/assert";
import { define, z } from "@jelou/functions";
import { createMockContext, createMockCronContext, createMockRequest } from "@jelou/functions/testing";

const fn = define({
  name: "consultar-cliente",
  description: "Busca información de un cliente por teléfono",
  input: z.object({
    telefono: z.string().min(10),
  }),
  output: z.object({
    nombre: z.string(),
    plan: z.string(),
    companyId: z.number(),
  }),
  handler: async (input, ctx) => {
    return {
      nombre: "María García",
      plan: "Premium",
      companyId: ctx.company.id,
    };
  },
});

Deno.test("retorna info del cliente con el company ID correcto", async () => {
  const ctx = createMockContext({
    company: { id: 42, name: "Tienda ABC" },
    bot: { id: "bot-123", name: "Bot Soporte", channel: "whatsapp" },
  });
  const req = createMockRequest({ telefono: "593987654321" });

  const result = await fn.handler(
    { telefono: "593987654321" },
    ctx,
    req,
  );

  assertEquals(result.nombre, "María García");
  assertEquals(result.plan, "Premium");
  assertEquals(result.companyId, 42);
});

Deno.test("cron skip cuando no es trigger cron", async () => {
  const cronFn = define({
    name: "limpieza-diaria",
    input: z.object({}),
    config: {
      cron: [{ expression: "0 3 * * *" }],
    },
    handler: async (_input, ctx) => {
      if (!ctx.isCron) return { skipped: true };
      return { cleaned: true };
    },
  });

  const httpCtx = createMockContext();
  const req = createMockRequest();
  const result = await cronFn.handler({}, httpCtx, req);
  assertEquals(result, { skipped: true });

  const cronCtx = createMockCronContext("0 3 * * *");
  const result2 = await cronFn.handler({}, cronCtx, req);
  assertEquals(result2, { cleaned: true });
});

createMockApp(tools, config?)

Crea un EdgeApp mock para testing de funciones multi-tool.
import { assertEquals } from "jsr:@std/assert";
import { createMockApp, createMockContext, createMockRequest } from "@jelou/functions/testing";
import { define, z } from "@jelou/functions";

const myApp = createMockApp({
  enviarEmail: define({
    description: "Envía un correo electrónico",
    input: z.object({ to: z.string() }),
    handler: async (input) => ({ sent: true }),
  }),
  leerBandeja: define({
    description: "Lee los mensajes de la bandeja de entrada",
    input: z.object({}),
    handler: async () => ({ messages: [] }),
  }),
});

Deno.test("app tiene los tools correctos", () => {
  assertEquals(Object.keys(myApp.tools), ["enviarEmail", "leerBandeja"]);
  assertEquals(myApp.__jelou_edge_app, true);
});

Deno.test("cada tool funciona independientemente", async () => {
  const ctx = createMockContext();
  const req = createMockRequest({ to: "[email protected]" });
  const result = await myApp.tools.enviarEmail.handler(
    { to: "[email protected]" },
    ctx,
    req,
  );
  assertEquals(result, { sent: true });
});
Ejecuta los tests con Deno:
deno test