import { describe, expect, it } from "bun:test" import z from "zod" import { codigosResposta, respostaComuns, zCodigosRespostaErro, zResposta, zRespostaErro, zRespostaSucesso, zRespostas, type tipoZResposta, } from "../src/respostas" describe("zRespostas - Sucesso", () => { it("deve validar resposta de sucesso com schema específico (string)", () => { const schema = zRespostas(z.string()) const res = respostaComuns.valor("Operação realizada com sucesso") const parsed = schema.parse(res) expect(parsed.cod).toBe(codigosResposta.sucesso) expect(parsed.eCerto).toBe(true) expect(parsed.eErro).toBe(false) if (parsed.eCerto) { expect(parsed.valor).toBe("Operação realizada com sucesso") } }) it("deve validar resposta de sucesso com schema de objeto complexo", () => { const zUsuario = z.object({ id: z.number(), nome: z.string(), ativo: z.boolean(), }) const schema = zRespostas(zUsuario) const res = respostaComuns.valor({ id: 10, nome: "Maria", ativo: true }, [ "detalhe 1", ]) const parsed = schema.parse(res) expect(parsed.eCerto).toBe(true) if (parsed.eCerto) { expect(parsed.valor.id).toBe(10) expect(parsed.valor.nome).toBe("Maria") expect(parsed.valor.ativo).toBe(true) expect(parsed.detalhes).toEqual(["detalhe 1"]) } }) it("deve aceitar qualquer valor quando invocado sem parâmetros (default z.any())", () => { const schema = zRespostas() const resString = respostaComuns.valor("qualquer texto") const resNumero = respostaComuns.valor(12345) const resBool = respostaComuns.valorTrue() expect(schema.parse(resString).eCerto).toBe(true) expect(schema.parse(resNumero).eCerto).toBe(true) expect(schema.parse(resBool).eCerto).toBe(true) }) it("deve funcionar com zRespostaSucesso isoladamente", () => { const schema = zRespostaSucesso(z.number()) const res = respostaComuns.valor(99) const parsed = schema.parse(res) expect(parsed.cod).toBe(200) expect(parsed.valor).toBe(99) }) it("deve rejeitar sucesso quando o tipo de valor for incompatível", () => { const schema = zRespostas(z.string()) const resInvalido = { cod: codigosResposta.sucesso, eCerto: true, eErro: false, valor: 12345, // número em vez de string } expect(() => schema.parse(resInvalido)).toThrow() }) it("deve aceitar detalhes nullish (null, undefined ou ausente)", () => { const schema = zRespostas(z.string()) const resComNull = { cod: codigosResposta.sucesso, eCerto: true, eErro: false, valor: "ok", detalhes: null, } const resSemDetalhes = { cod: codigosResposta.sucesso, eCerto: true, eErro: false, valor: "ok", } expect(schema.parse(resComNull).detalhes).toBeNull() expect(schema.parse(resSemDetalhes).detalhes).toBeUndefined() }) }) describe("zRespostas - Erro", () => { const schema = zRespostas(z.string()) it("deve validar erroConhecido (400)", () => { const res = respostaComuns.erro("Dados inválidos", ["campo X obrigatório"]) const parsed = schema.parse(res) expect(parsed.cod).toBe(codigosResposta.erroConhecido) expect(parsed.eCerto).toBe(false) expect(parsed.eErro).toBe(true) if (parsed.eErro) { expect(parsed.mensagem).toBe("Dados inválidos") expect(parsed.detalhes).toEqual(["campo X obrigatório"]) } }) it("deve validar erroPermissao (401)", () => { const res = respostaComuns.erroPermissao("Acesso negado") const parsed = schema.parse(res) expect(parsed.cod).toBe(codigosResposta.erroPermissao) expect(parsed.eErro).toBe(true) if (parsed.eErro) { expect(parsed.mensagem).toBe("Acesso negado") } }) it("deve validar erroNaoEncontrado (404)", () => { const res = respostaComuns.naoEncontrado("Usuário não encontrado") const parsed = schema.parse(res) expect(parsed.cod).toBe(codigosResposta.erroNaoEncontrado) expect(parsed.eErro).toBe(true) if (parsed.eErro) { expect(parsed.mensagem).toBe("Usuário não encontrado") } }) it("deve validar erroDesconhecido (500)", () => { const res = respostaComuns.erroInterno({ erro: new Error("Falha no banco"), local: "usuario.service", mensagem: "Erro inesperado", }) const parsed = schema.parse(res) expect(parsed.cod).toBe(codigosResposta.erroDesconhecido) expect(parsed.eErro).toBe(true) if (parsed.eErro) { expect(parsed.mensagem).toBe("Erro inesperado") } }) it("deve validar tempoEsgotado (504)", () => { const res = respostaComuns.tempoEsgotado("Gateway Timeout") const parsed = schema.parse(res) expect(parsed.cod).toBe(codigosResposta.tempoEsgotado) expect(parsed.eErro).toBe(true) if (parsed.eErro) { expect(parsed.mensagem).toBe("Gateway Timeout") } }) it("deve funcionar com zRespostaErro isoladamente", () => { const res = respostaComuns.erro("Falha simples") const parsed = zRespostaErro.parse(res) expect(parsed.cod).toBe(400) expect(parsed.mensagem).toBe("Falha simples") }) it("deve rejeitar códigos de erro não pertencentes a codigosResposta", () => { const resInvalido = { cod: 403, // 403 não é código do enum codigosResposta (p-respostas usa 401) eCerto: false, eErro: true, mensagem: "Proibido", } expect(() => schema.parse(resInvalido)).toThrow() }) it("deve rejeitar erro sem mensagem", () => { const resSemMsg = { cod: codigosResposta.erroConhecido, eCerto: false, eErro: true, } expect(() => schema.parse(resSemMsg)).toThrow() }) }) describe("zRespostas - Consistência e Aliases", () => { it("zResposta deve ser idêntico a zRespostas", () => { expect(zResposta).toBe(zRespostas) }) it("zCodigosRespostaErro deve aceitar apenas os 5 códigos de erro", () => { expect(zCodigosRespostaErro.parse(400)).toBe(400) expect(zCodigosRespostaErro.parse(401)).toBe(401) expect(zCodigosRespostaErro.parse(404)).toBe(404) expect(zCodigosRespostaErro.parse(500)).toBe(500) expect(zCodigosRespostaErro.parse(504)).toBe(504) expect(() => zCodigosRespostaErro.parse(200)).toThrow() expect(() => zCodigosRespostaErro.parse(403)).toThrow() expect(() => zCodigosRespostaErro.parse(502)).toThrow() }) it("deve permitir narrowing estático em TypeScript", () => { const schema = zRespostas(z.string()) const res: tipoZResposta = schema.parse( respostaComuns.valor("dados"), ) if (res.eCerto) { const v: string = res.valor expect(v).toBe("dados") } else { const m: string = res.mensagem expect(m).toBeDefined() } if (res.cod === codigosResposta.sucesso) { const v: string = res.valor expect(v).toBe("dados") } else { const m: string = res.mensagem expect(m).toBeDefined() } }) })