import { hechosSchema, diagnosticoSchema, redaccionSchema, aJsonSchema } from "@/lib/propuesta/schemas"; let fallas = 0; function check(n: string, ok: boolean, d = "") { console.log(`${ok ? " OK " : " FALLA"} | ${n}${d ? " -> " + d : ""}`); if (!ok) fallas++; } // ── P1 por construccion: ningun schema declara un campo de dinero de E3 ── const todos = JSON.stringify([ aJsonSchema(hechosSchema), aJsonSchema(diagnosticoSchema), aJsonSchema(redaccionSchema), ]); for (const prohibido of ["precio", "total", "subtotal", "iva", "montoMinimo", "tarifaHora", "descuento"]) { check(`ningun schema declara la propiedad "${prohibido}"`, !todos.includes(`"${prohibido}"`)); } // montoAnualMXN si debe existir: es el costo del problema del cliente, no un precio nuestro. check("montoAnualMXN si existe (costo del problema del cliente)", todos.includes("montoAnualMXN")); // ── Formato de las claves ── const redaccionBase = { hero: { titulo: "Titulo de prueba", subtitulo: "x".repeat(25) }, citaDestacadaId: "C01", alcance: [{ refPartida: "P01", descripcionResultado: "x".repeat(25) }], beneficios: [], exclusiones: [{ texto: "x".repeat(20), razon: "porque si" }], backlogEvolucion: [], notasInternas: [], }; check("una redaccion bien formada valida", redaccionSchema.safeParse(redaccionBase).success); check("refPartida en minuscula se rechaza", !redaccionSchema.safeParse({ ...redaccionBase, alcance: [{ refPartida: "p01", descripcionResultado: "x".repeat(25) }] }).success); check("refPartida sin ceros se rechaza", !redaccionSchema.safeParse({ ...redaccionBase, alcance: [{ refPartida: "P1", descripcionResultado: "x".repeat(25) }] }).success); check("citaDestacadaId con otro prefijo se rechaza", !redaccionSchema.safeParse({ ...redaccionBase, citaDestacadaId: "H01" }).success); check("exige al menos una exclusion", !redaccionSchema.safeParse({ ...redaccionBase, exclusiones: [] }).success); check("rechaza propiedades desconocidas (strict)", !redaccionSchema.safeParse({ ...redaccionBase, precioTotal: 99999 }).success); // ── El calculo del valor debe venir desglosado ── const diagBase = { hallazgos: [{ id: "D01", urgencia: "rojo", titulo: "Titulo del hallazgo", descripcion: "x".repeat(25), confianza: "confirmado", citas: [], hechos: [] }], valorProblema: { dimensiones: [{ tipo: "tiempo", descripcion: "x".repeat(12), factores: [{ nombre: "a", valor: 8, confianza: "confirmado" }, { nombre: "b", valor: 52, confianza: "estimado" }], montoAnualMXN: 416, confianza: "estimado", hechos: [], }], notaMetodologia: "n", }, resultados: [], }; check("un diagnostico bien formado valida", diagnosticoSchema.safeParse(diagBase).success, JSON.stringify(diagnosticoSchema.safeParse(diagBase).error?.issues?.[0] ?? "")); check("con monto, exige al menos 2 factores", !diagnosticoSchema.safeParse({ ...diagBase, valorProblema: { ...diagBase.valorProblema, dimensiones: [{ ...diagBase.valorProblema.dimensiones[0], factores: [{ nombre: "a", valor: 1, confianza: "estimado" }], montoAnualMXN: 1 }] } }).success); check("sin monto, factores vacio es lo correcto", diagnosticoSchema.safeParse({ ...diagBase, valorProblema: { ...diagBase.valorProblema, dimensiones: [{ ...diagBase.valorProblema.dimensiones[0], factores: [], montoAnualMXN: null }] } }).success); // Una dimension SIN la clave montoAnualMXN: es lo que manda el modelo cuando no tiene // cifras, y por eso el campo lleva .default(null) en vez de solo .nullable(). const dimSinMonto = { tipo: "tiempo", descripcion: "x".repeat(12), factores: [], confianza: "por_validar", hechos: [] }; const sinMonto = { ...diagBase, valorProblema: { ...diagBase.valorProblema, dimensiones: [dimSinMonto] } }; check("omitir montoAnualMXN se tolera", diagnosticoSchema.safeParse(sinMonto).success, JSON.stringify(diagnosticoSchema.safeParse(sinMonto).error?.issues?.[0] ?? "")); check("al omitirlo, Zod lo rellena con null", diagnosticoSchema.parse(sinMonto).valorProblema.dimensiones[0].montoAnualMXN === null); check("la dimension quedo plana: sin nivel `calculo`", !JSON.stringify(aJsonSchema(diagnosticoSchema)).includes('"calculo"')); check("un factor sin confianza se rechaza", !diagnosticoSchema.safeParse({ ...diagBase, valorProblema: { ...diagBase.valorProblema, dimensiones: [{ ...diagBase.valorProblema.dimensiones[0], factores: [{ nombre: "a", valor: 1 }, { nombre: "b", valor: 2 }], montoAnualMXN: 2 }] } }).success); check("exige al menos un hallazgo", !diagnosticoSchema.safeParse({ ...diagBase, hallazgos: [] }).success); check("urgencia fuera del semaforo se rechaza", !diagnosticoSchema.safeParse({ ...diagBase, hallazgos: [{ ...diagBase.hallazgos[0], urgencia: "morado" }] }).success); // ── Hechos ── check("una cita corta se rechaza", !hechosSchema.safeParse({ citas: [{ id: "C01", textoLiteral: "hola", quienLoDijo: "x" }], hechos: [], materialesPendientes: [], decisionesPendientes: [], mencionesFueraDeAlcance: [], redFlags: [] }).success); check("hechos vacios son validos (material pobre)", hechosSchema.safeParse({ citas: [], hechos: [], materialesPendientes: [], decisionesPendientes: [], mencionesFueraDeAlcance: [], redFlags: [] }).success); // ── Conversion a JSON Schema ── const js = aJsonSchema(redaccionSchema); check("el JSON Schema no lleva $schema", !("$schema" in js)); check("conserva additionalProperties", JSON.stringify(js).includes("additionalProperties")); check("conserva las descripciones para el modelo", JSON.stringify(js).includes("description")); check("el JSON Schema es serializable", typeof JSON.stringify(js) === "string" && JSON.stringify(js).length > 200); console.log(fallas === 0 ? "\ntodo paso\n" : `\n${fallas} fallas\n`); process.exit(fallas === 0 ? 0 : 1);