Files
agenciapsilmno/src/features/patients/composables/__tests__/useCep.spec.js
T
Leonardo d6eb992f71 Sessoes 6cont-10: hardening em 6 areas + scan completo do SaaS
Continuacao de 7c20b51. Esta etapa fechou TODA revisao senior do SaaS
(15 areas auditadas) + refator parcial de pacientes.

Ver commit.md para descricao completa por sessao.

# Estado final do projeto
- A# auditoria abertos: 1 (A#31 Deploy real)
- V# verificacoes abertos: 14 (todos medios/baixos adiados com plano)
- Criticos: 0
- Altos: 0
- Vitest: 208/208 (era 192, +16 nos novos composables)
- SQL integration: 33/33
- E2E (Playwright): 5/5
- Areas auditadas: 15

# Highlights
- Documentos 100% fechado (V#50/51/52: portal-paciente policy + content_sha256 + 4 cron jobs retention)
- Tenants V#1 P0: tenant_invites com RLS off + 0 policies (mesmo padrao A#30)
- Calendario 100% fechado: feriados WITH CHECK
- Addons V#1 P0 (dinheiro): addon_transactions WITH CHECK saas_admin
- Central SaaS V#1: faq write so saas_admin (era tenant_admin)
- Servicos/Prontuarios 100% fechado: services/medicos/insurance_plans + cascades
- Pacientes V#9: 2 composables novos (useCep, usePatientSupportContacts) + repo estendido + script extraido (template intocado, fica para quando houver E2E)

# 8 migrations novas neste commit
- 20260419000011_documents_portal_patient_policy.sql
- 20260419000012_documents_content_hash.sql
- 20260419000013_cron_retention_jobs.sql
- 20260419000014_financial_security_hardening.sql
- 20260419000015_communication_security_hardening.sql
- 20260419000016_tenants_calendario_hardening.sql
- 20260419000017_addons_central_saas_hardening.sql
- 20260419000018_servicos_prontuarios_hardening.sql

Total acumulado: 18 migrations (Sessoes 1-10).

# A#31 reformulado pra proxima sessao
"Deploy real" muda escopo: como nao ha cloud Supabase nem secrets reais
ainda (MVP), proxima sessao vira "Preparacao completa pra deploy" (DEPLOY.md,
validar migrations num container limpo, audit edge functions, listar env vars,
script db.cjs deploy-check).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:00:06 -03:00

68 lines
2.5 KiB
JavaScript

/**
* useCep.spec.js — V#9 (composable extraído)
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
const fetchMock = vi.fn();
globalThis.fetch = fetchMock;
const { useCep } = await import('../useCep.js');
beforeEach(() => fetchMock.mockReset());
afterEach(() => fetchMock.mockReset());
describe('useCep — busca ViaCEP', () => {
it('retorna null se CEP tem menos de 8 dígitos (no-op)', async () => {
const { fetchCep } = useCep();
expect(await fetchCep('123')).toBe(null);
expect(await fetchCep('')).toBe(null);
expect(await fetchCep(null)).toBe(null);
expect(fetchMock).not.toHaveBeenCalled();
});
it('aceita CEP com máscara (digitsOnly normaliza)', async () => {
fetchMock.mockResolvedValue({ ok: true, json: async () => ({ localidade: 'São Paulo', uf: 'SP', logradouro: 'Av Paulista', bairro: 'Bela Vista', complemento: '' }) });
const { fetchCep } = useCep();
const r = await fetchCep('01310-100');
expect(r).toEqual({
cidade: 'São Paulo',
uf: 'SP',
bairro: 'Bela Vista',
endereco: 'Av Paulista',
complemento: ''
});
expect(fetchMock).toHaveBeenCalledWith('https://viacep.com.br/ws/01310100/json/');
});
it('retorna null se ViaCEP devolve {erro: true}', async () => {
fetchMock.mockResolvedValue({ ok: true, json: async () => ({ erro: true }) });
const { fetchCep } = useCep();
expect(await fetchCep('00000000')).toBe(null);
});
it('retorna null se HTTP falha (sem propagar erro)', async () => {
fetchMock.mockResolvedValue({ ok: false, status: 500 });
const { fetchCep, error } = useCep();
expect(await fetchCep('01310100')).toBe(null);
expect(error.value).toContain('500');
});
it('captura exception de rede como null', async () => {
fetchMock.mockRejectedValue(new Error('network'));
const { fetchCep, error } = useCep();
expect(await fetchCep('01310100')).toBe(null);
expect(error.value).toBe('network');
});
it('loading reflete o ciclo da request', async () => {
let resolveIt;
fetchMock.mockReturnValue(new Promise((res) => { resolveIt = res; }));
const { fetchCep, loading } = useCep();
const p = fetchCep('01310100');
expect(loading.value).toBe(true);
resolveIt({ ok: true, json: async () => ({ localidade: 'A', uf: 'B' }) });
await p;
expect(loading.value).toBe(false);
});
});