83 lines
3.4 KiB
TypeScript
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");
|
|
});
|
|
});
|