Pular para o conteúdo principal

Configuração

Importe os utilitários de teste de @jelou/functions/testing:
import {
  createMockContext,
  createMockCronContext,
  createMockEventContext,
  createMockRequest,
  createMockAuthContext,
  createMockAuthRequest,
  createMockJelouClient,
  createMockMemoryClient,
} from "@jelou/functions/testing";

createMockContext(overrides?)

Cria um contexto com valores padrão sensatos para testes.
const ctx = createMockContext();

const ctx2 = createMockContext({
  company: { id: 42, name: "Store ABC" },
  user: { id: 99, names: "Maria Garcia" },
  bot: { id: "bot-123", name: "Support Bot", 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?)

Cria um contexto com isCron: true e trigger.type: "cron".
const ctx = createMockCronContext("0 9 * * *");
// ctx.isCron === true
// ctx.trigger === { type: "cron", cron: "0 9 * * *" }
// ctx.isHttp === false

createMockEventContext(eventName, overrides?)

Cria um contexto com isEvent: true e trigger.type: "event".
const ctx = createMockEventContext("payment.completed");
// ctx.isEvent === true
// ctx.trigger === { type: "event", event: "payment.completed" }

createMockRequest(body?, options?)

Cria um objeto Request padrão da Web.
const req = createMockRequest();
// GET http://localhost:8000/

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

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

createMockAuthContext(overrides?)

Cria um contexto com auth: { authenticated: true } por padrão. Útil para testar funções protegidas.
const ctx = createMockAuthContext();
// ctx.auth === { authenticated: true }

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

createMockAuthRequest(token, body?, options?)

Cria um Request com o header Authorization: Bearer <token>.
const req = createMockAuthRequest("my-secret-token");
// GET with Authorization: Bearer my-secret-token

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

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

createMockJelouClient(options?)

Cria um cliente de mensagens mock com gravação de chamadas.
const mockJelou = createMockJelouClient();
const ctx = createMockContext({ jelou: mockJelou });

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

// Inspecionar chamadas
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" }],
});

Resetar entre testes

mockJelou.reset(); // clears recorded calls

createMockMemoryClient(options?)

Cria um cliente de memória mock com um armazenamento em memória.
const mockMemory = createMockMemoryClient({
  store: { step: "start", attempts: 0 },
});
const ctx = createMockContext({ memory: mockMemory });

const step = await ctx.memory.get("step", "unknown"); // "start"
await ctx.memory.set("step", "confirmation", 3600);

Inspecionar chamadas

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

Resetar entre testes

mockMemory.reset(); // clears calls and restores the initial store

Exemplo 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: "query-customer",
  description: "Looks up customer information by phone",
  input: z.object({
    phone: z.string().min(10),
  }),
  output: z.object({
    name: z.string(),
    plan: z.string(),
    companyId: z.number(),
  }),
  handler: async (input, ctx) => {
    return {
      name: "Maria Garcia",
      plan: "Premium",
      companyId: ctx.company.id,
    };
  },
});

Deno.test("returns customer info with correct company ID", async () => {
  const ctx = createMockContext({
    company: { id: 42, name: "Store ABC" },
    bot: { id: "bot-123", name: "Support Bot", channel: "whatsapp" },
  });
  const req = createMockRequest({ phone: "593987654321" });

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

  assertEquals(result.name, "Maria Garcia");
  assertEquals(result.plan, "Premium");
  assertEquals(result.companyId, 42);
});

Deno.test("cron skip when not a cron trigger", async () => {
  const cronFn = define({
    name: "daily-cleanup",
    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?)

Cria um EdgeApp mock para testar funções 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({
  sendEmail: define({
    description: "Sends an email",
    input: z.object({ to: z.string() }),
    handler: async (input) => ({ sent: true }),
  }),
  readInbox: define({
    description: "Reads inbox messages",
    input: z.object({}),
    handler: async () => ({ messages: [] }),
  }),
});

Deno.test("app has the correct tools", () => {
  assertEquals(Object.keys(myApp.tools), ["sendEmail", "readInbox"]);
  assertEquals(myApp.__jelou_edge_app, true);
});

Deno.test("each tool works independently", async () => {
  const ctx = createMockContext();
  const req = createMockRequest({ to: "[email protected]" });
  const result = await myApp.tools.sendEmail.handler(
    { to: "[email protected]" },
    ctx,
    req,
  );
  assertEquals(result, { sent: true });
});
Execute os testes com Deno:
deno test