conector-scardua/app/v1/compras.py
Ricardo 7a9b0f8aa5 Adiciona testes do conector com fixtures offline
Cobre o caminho completo sem depender da VPS estar no ar: cifra com uma
chave de teste descartavel, troca a chave do app por monkeypatch e exercita
a rota.

8 casos: recusa sem token e com token errado; caminho feliz; envelope
adulterado (o GCM tem que estourar, nao passar); cifrado com outra chave;
payload fora do contrato (422, nao 200 nem 500); e dois travando o contrato
com a API da VPS — se o PayloadCompras dos dois lados divergir, quebra aqui
em vez de em producao.

Fixtures em tests/fixtures/: par de chaves so pra teste, o payload real do
Holmes e o payload ja tratado. O .gitignore abre excecao estreita pra
tests/fixtures/*.pem — chave real continua barrada.

Tambem:
- 422 passa a usar HTTP_422_UNPROCESSABLE_CONTENT (o _ENTITY deprecou)
- app/services/holmes.py removido: o conector nao fala com o Holmes
- app/utils/cesar.py entra como veio

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 06:13:13 -03:00

56 lines
2 KiB
Python

import logging
from app.config import settings
from app.schemas import Envelope, PayloadCompras
from app.seguranca import autorizar
from app.services import cripto
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import ValidationError
router = APIRouter(prefix="/compras", tags=["compras"])
# Carregada uma vez no import: parsear PEM a cada request e desperdicio.
_CHAVE = cripto.carregar_chave_privada(settings.vps.chave_privada)
@router.post("/dados", dependencies=[Depends(autorizar)])
async def receber_compras(envelope: Envelope) -> dict:
"""
Recebe o envelope cifrado da API da VPS, abre e valida.
Fase atual: so registra o que chegou. O INSERT no Oracle entra depois —
quando entrar, tem que ser MERGE por id_processo, senao reentrega de
webhook duplica linha.
"""
try:
bruto = cripto.descriptografar(envelope.model_dump(), _CHAVE)
except Exception as e:
# Chave errada, envelope adulterado ou algoritmo diferente.
logging.error(f"Falha ao abrir envelope: {type(e).__name__}: {e}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="envelope invalido"
)
try:
dados = PayloadCompras.model_validate(bruto)
except ValidationError as e:
# Decifrou mas o formato mudou: os dois lados sairam de sincronia.
logging.error(f"Payload fora do contrato: {e}")
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
detail="payload nao bate com o contrato esperado",
)
logging.info(
f"Compra recebida | processo={dados.id_processo} "
f"protocolo={dados.protocolo} cnpj={dados.cnpj} "
f"pedido={dados.pedido_linx} total={dados.valor_total} "
f"parcelas={len(dados.parcelas)}"
)
return {
"status": "recebido",
"id_processo": dados.id_processo,
"parcelas": len(dados.parcelas),
"persistido": False, # vira True quando o Oracle entrar
}