Files
adaptador-planilhas/tests/unit/pilot-policy.test.ts
T
camilafagundes 419325db12
ci / verify (push) Has been cancelled
Inicio do Projeto Piloto
2026-10-02 23:59:36 -03:00

83 lines
3.4 KiB
TypeScript

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");
});
});