// Valida CPFs/CNPJs vindos do Go e do porte JS usando uma checagem // independente (recalcula os DVs a partir dos 9/12 primeiros caracteres). import { generateCPF, formatCPF, generateCNPJ, generateCNPJAlpha, formatCNPJ, } from "./generator.js"; import { execFileSync } from "node:child_process"; import { existsSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; function validCPF(s) { if (!/^\d{11}$/.test(s)) return false; const d = [...s].map(Number); let sum = 0; for (let i = 0; i < 9; i++) sum += d[i] * (10 - i); let r = sum % 11; if (d[9] !== (r < 2 ? 0 : 11 - r)) return false; sum = 0; for (let i = 0; i < 10; i++) sum += d[i] * (11 - i); r = sum % 11; return d[10] === (r < 2 ? 0 : 11 - r); } const P1 = [5,4,3,2,9,8,7,6,5,4,3,2]; const P2 = [6,5,4,3,2,9,8,7,6,5,4,3,2]; const val = (c) => c.charCodeAt(0) - 48; function validCNPJ(s) { if (!/^[0-9A-Z]{12}\d{2}$/.test(s)) return false; const dv = (str, t) => { let tot = 0; for (let i = 0; i < t.length; i++) tot += val(str[i]) * t[i]; const r = tot % 11; return r >= 2 ? 11 - r : 0; }; if (Number(s[12]) !== dv(s, P1)) return false; return Number(s[13]) === dv(s.slice(0, 13), P2); } const here = path.dirname(fileURLToPath(import.meta.url)); const CPF = path.join(here, "..", "bin", "cpfgen"); // cnpjgen vive em outro repo, ao lado deste. const CNPJ = path.join(here, "..", "..", "cnpj_generator", "bin", "cnpjgen"); const run = (bin, args) => execFileSync(bin, args, { encoding: "utf8" }).trim(); let fail = 0; const check = (label, ok) => { if (!ok) { fail++; console.log(`FAIL ${label}`); } }; const N = 300; // Referência: saída dos binários Go, quando compilados. Servem de oráculo para // o porte JS; se não existirem, os testes do JS abaixo rodam do mesmo jeito. const hasCPF = existsSync(CPF); const hasCNPJ = existsSync(CNPJ); if (!hasCPF) console.log("aviso: bin/cpfgen ausente (rode `make build`), pulando"); if (!hasCNPJ) console.log("aviso: cnpj_generator/bin/cnpjgen ausente, pulando"); for (let i = 0; i < N; i++) { if (hasCPF) check("go cpf", validCPF(run(CPF, ["-num"]))); if (hasCNPJ) { check("go cnpj", validCNPJ(run(CNPJ, ["-num"]))); check("go cnpj alpha", validCNPJ(run(CNPJ, ["-alpha", "-num"]))); } } // Porte JS. for (let i = 0; i < N; i++) { check("js cpf", validCPF(generateCPF())); check("js cnpj", validCNPJ(generateCNPJ())); check("js cnpj alpha", validCNPJ(generateCNPJAlpha())); } // Formatação deve casar com a do Go. check("fmt cpf", /^\d{3}\.\d{3}\.\d{3}-\d{2}$/.test(formatCPF(generateCPF()))); check("fmt cnpj", /^\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}$/.test(formatCNPJ(generateCNPJ()))); check("fmt cnpj alpha", /^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}\/[0-9A-Z]{4}-\d{2}$/ .test(formatCNPJ(generateCNPJAlpha()))); // Formato do Go, lado a lado, para confirmar que o layout é idêntico. console.log("go cpf :", run(CPF, [])); console.log("js cpf :", formatCPF(generateCPF())); console.log("go cnpj :", run(CNPJ, [])); console.log("js cnpj :", formatCNPJ(generateCNPJ())); console.log("go alpha :", run(CNPJ, ["-alpha"])); console.log("js alpha :", formatCNPJ(generateCNPJAlpha())); // Distribuição: nenhum dígito verificador deve ficar preso num valor só. const dvs = new Set(); for (let i = 0; i < 200; i++) dvs.add(generateCPF().slice(9)); console.log(`\nDVs de CPF distintos em 200: ${dvs.size}`); if (fail === 0) { console.log("\nOK: geradores JS conferem com os binários Go"); } else { console.log(`\n${fail} falhas`); process.exit(1); }