""" Testes de integração contra o banco de dev (ver test_db_connection.py). Usa CODIGO na faixa "999900"-"999999", reservada para dados de teste deste painel (fora da faixa real de clientes, que é sequencial e baixa — ver licenciamento-n8n/docs/MAPEAMENTO_BANCO.md). Cada teste limpa o que criou. """ import pytest from app.db import clientes as db_clientes from app.db.connection import get_connection CLIENTE_TESTE = { "CODIGO": "999901", "CNPJ": "11222333000181", "RAZAO": "TESTE PAINEL RAZAO", "FANTASIA": "TESTE PAINEL FANTASIA", "IE": "ISENTO", "ENDERECO": "RUA TESTE, 1", "CIDADE": "FEIRA DE SANTANA", "BAIRRO": "CENTRO", "UF": "BA", "CONTATO": "FULANO TESTE", "TELEFONE": "75999090000", "CELULAR": "75999090000", "CEP": "44000000", "IMPLANTACAO": "0.00", "MENSALIDADE": "100.00", "JUROS_MORA": "0.00", "JUROS_MULTA": "0.00", "DATA_CONTRATO": "2026-01-01", "EMAIL": "teste@example.com", "OBS": "Criado por teste automatizado — apagar se sobrar.", "VENCIMENTO_CONTRATO": "2027-01-01", "DIA_VENCIMENTO": "10", } def _limpar(codigo: str) -> None: conn = get_connection() try: with conn.cursor() as cur: cur.execute("DELETE FROM CLIENTE WHERE CODIGO = %s", (codigo,)) finally: conn.close() @pytest.fixture def cliente_id(): novo_id = db_clientes.criar_cliente(CLIENTE_TESTE) yield novo_id _limpar(CLIENTE_TESTE["CODIGO"]) def test_criar_e_obter_cliente(cliente_id): cliente = db_clientes.obter_cliente(cliente_id) assert cliente is not None assert cliente["CODIGO"] == "999901" assert cliente["RAZAO"] == "TESTE PAINEL RAZAO" def test_obter_cliente_inexistente(): assert db_clientes.obter_cliente(999999999) is None def test_listar_clientes_encontra_por_busca(cliente_id): resultado = db_clientes.listar_clientes(busca="TESTE PAINEL RAZAO") codigos = [c["CODIGO"] for c in resultado] assert "999901" in codigos def test_codigo_em_uso(cliente_id): assert db_clientes.codigo_em_uso("999901") is True assert db_clientes.codigo_em_uso("999901", ignorar_id=cliente_id) is False assert db_clientes.codigo_em_uso("000000") is False def test_atualizar_cliente(cliente_id): dados = dict(CLIENTE_TESTE) dados["FANTASIA"] = "TESTE PAINEL FANTASIA EDITADA" db_clientes.atualizar_cliente(cliente_id, dados) cliente = db_clientes.obter_cliente(cliente_id) assert cliente["FANTASIA"] == "TESTE PAINEL FANTASIA EDITADA" def test_excluir_cliente_sem_dependencias(cliente_id): db_clientes.excluir_cliente(cliente_id) assert db_clientes.obter_cliente(cliente_id) is None def test_listar_clientes_busca_e_restrita_a_coluna_escolhida(cliente_id): # ordenar_por="codigo" restringe a busca à coluna CODIGO — o termo # "TESTE PAINEL RAZAO" existe em RAZAO/FANTASIA, mas não deve aparecer # aqui porque a busca não olha para essas colunas quando é "codigo". resultado = db_clientes.listar_clientes(busca="TESTE PAINEL RAZAO", ordenar_por="codigo") assert len(resultado) == 0 # Buscando pelo próprio código, com ordenar_por="codigo", encontra. resultado = db_clientes.listar_clientes(busca="999901", ordenar_por="codigo") assert resultado[0]["CODIGO"] == "999901" def test_listar_clientes_ordenacao_invalida_usa_padrao(cliente_id): # "ordenar_por" fora da whitelist não deve quebrar nem virar SQL arbitrário # — cai no padrão (RAZAO). resultado = db_clientes.listar_clientes(busca="TESTE PAINEL RAZAO", ordenar_por="ID; DROP TABLE CLIENTE") assert any(c["CODIGO"] == "999901" for c in resultado) def test_listar_clientes_direcao_asc_e_desc(cliente_id): # Dois clientes de teste com códigos diferentes, mesma busca por RAZAO, # pra comparar a ordem entre asc/desc de forma determinística. outro_id = db_clientes.criar_cliente(dict(CLIENTE_TESTE, CODIGO="999905")) try: asc = db_clientes.listar_clientes(busca="TESTE PAINEL RAZAO", ordenar_por="codigo", direcao="asc") desc = db_clientes.listar_clientes(busca="TESTE PAINEL RAZAO", ordenar_por="codigo", direcao="desc") codigos_asc = [c["CODIGO"] for c in asc] codigos_desc = [c["CODIGO"] for c in desc] assert codigos_asc == sorted(codigos_asc) assert codigos_desc == sorted(codigos_desc, reverse=True) finally: _limpar("999905") def test_listar_clientes_direcao_invalida_usa_padrao_asc(cliente_id): # Não deve quebrar nem virar SQL arbitrário — cai em ASC. resultado = db_clientes.listar_clientes( busca="999901", ordenar_por="codigo", direcao="ASC; DROP TABLE CLIENTE" ) assert resultado[0]["CODIGO"] == "999901" def test_proximo_codigo_sugerido_ignora_faixa_de_teste(cliente_id): # CLIENTE_TESTE usa CODIGO "999901" (faixa reservada para teste, >= 900000) # — a sugestão não pode considerar isso, senão qualquer sobra de dado de # teste no banco faria a sugestão pular para a casa dos 999xxx. sugerido = db_clientes.proximo_codigo_sugerido() assert int(sugerido) < 900000 assert len(sugerido) == 6 assert db_clientes.codigo_em_uso(sugerido) is False def test_excluir_cliente_com_contato_bloqueia(cliente_id): conn = get_connection() try: with conn.cursor() as cur: cur.execute( "INSERT INTO CLIENTE_CONTATO (CLIENTE_ID, NOME, TELEFONE) VALUES (%s, %s, %s)", (cliente_id, "Contato Teste", "75999090001"), ) finally: conn.close() try: with pytest.raises(db_clientes.ClienteComDependenciasError): db_clientes.excluir_cliente(cliente_id) finally: conn = get_connection() try: with conn.cursor() as cur: cur.execute("DELETE FROM CLIENTE_CONTATO WHERE CLIENTE_ID = %s", (cliente_id,)) finally: conn.close()