import { describe, expect, it } from "vitest"; import { tempTtlSeconds } from "@/config/retention"; import { collectProductionIssues } from "@/config/env"; import { shouldRequeue } from "@/domain/spreadsheet-adapter/failure-policy"; import { devAuthEnabled, verifyDevSession } from "@/server/auth/dev-session"; import { buildCentralLoginUrl, safeReturnPath } from "@/server/auth/return-to"; import { buildSentryPayload } from "@/infra/observability/capture"; describe("política de piloto", () => { it("recusa returnTo externo e aceita caminho interno", () => { expect(safeReturnPath("https://evil.example/phish")).toBe("/adaptador"); expect(safeReturnPath("//evil.example")).toBe("/adaptador"); expect(safeReturnPath("/adaptador/operacao")).toBe("/adaptador/operacao"); const url = buildCentralLoginUrl("https://login.elo.example/entrar", "https://evil.example", "https://app.elo.example"); expect(url).not.toContain("evil.example"); expect(url).toContain("returnTo="); }); it("bloqueia autenticação de desenvolvimento em produção", () => { const previous = process.env.NODE_ENV; process.env.NODE_ENV = "production"; process.env.ADAPTER_DEV_AUTH = "1"; process.env.ADAPTER_DEV_AUTH_SECRET = "segredo-local"; try { expect(devAuthEnabled()).toBe(false); expect(verifyDevSession("corpo.assinatura")).toBeNull(); } finally { process.env.NODE_ENV = previous; } }); it("recusa produção sem Redis, com inline, com bucket público ou TTL inválido", () => { const issues = collectProductionIssues({ NODE_ENV: "production", ADAPTER_DEV_AUTH: "1", ADAPTER_JOB_MODE: "inline", SUPABASE_STORAGE_BUCKET: "public", SPREADSHEET_TEMP_TTL_SECONDS: "0", }); const names = issues.map((issue) => issue.variable); expect(names).toContain("REDIS_URL"); expect(names).toContain("ADAPTER_DEV_AUTH"); expect(names).toContain("ADAPTER_JOB_MODE"); expect(names).toContain("SUPABASE_STORAGE_BUCKET"); expect(names).toContain("SPREADSHEET_TEMP_TTL_SECONDS"); }); it("exige TTL explícito em produção", () => { const node = process.env.NODE_ENV; const ttl = process.env.SPREADSHEET_TEMP_TTL_SECONDS; process.env.NODE_ENV = "production"; delete process.env.SPREADSHEET_TEMP_TTL_SECONDS; try { expect(() => tempTtlSeconds()).toThrow(/obrigatório/); process.env.SPREADSHEET_TEMP_TTL_SECONDS = "abc"; expect(() => tempTtlSeconds()).toThrow(/inválido/); } finally { process.env.NODE_ENV = node; if (ttl === undefined) delete process.env.SPREADSHEET_TEMP_TTL_SECONDS; else process.env.SPREADSHEET_TEMP_TTL_SECONDS = ttl; } }); it("não reenfileira timeout e reenfileira falha transitória", () => { expect(shouldRequeue("TIMEOUT", 504, 1)).toBe(false); expect(shouldRequeue("PROCESS_FAILED", 500, 1)).toBe(true); expect(shouldRequeue("PROCESS_FAILED", 500, 3)).toBe(false); }); it("não envia célula sentinela ao payload do Sentry", () => { const sentinel = "Ana Silva =CMD()"; const payload = JSON.stringify( buildSentryPayload(new Error(`valor da célula ${sentinel}`), { job_id: "11111111-1111-4111-8111-111111111111", error_code: "PROCESS_FAILED", }), ); expect(payload).not.toContain(sentinel); expect(payload).not.toContain("Ana Silva"); expect(payload).toContain("UNEXPECTED"); }); });