Files
dtf-system/tests/test_jadlog.py
Cauê Faleiros e20c8673bb
All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 2m19s
Build and deploy / Secret scan and release gate (push) Successful in 7s
Build and deploy / Publish images (push) Successful in 1m33s
fix: send the Jadlog account exactly as configured
Jadlog issues the account as 000000-0 and documents a six-character field;
stripping the dash sent seven digits. The value now goes as typed, so the
accepted form can be found on the account without a new release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:00:44 -03:00

148 lines
6.7 KiB
Python

"""The Jadlog quote client against a fake HTTP transport: payload and errors.
This proves the manual's contract only; app.jadlog_probe must still confirm
it on the client's account. Runs where httpx is installed.
"""
import json
import os
import unittest
from unittest import mock
import httpx
from app.jadlog import QUOTE_URL, JadlogError, JadlogFreight, JadlogQuotes
from app.jadlog_probe import run
CNPJ = '11.222.333/0001-81'
def client(handler, **settings):
return JadlogQuotes(token=settings.pop('token', 'tok-1'), cnpj=CNPJ, conta='123456',
transport=httpx.MockTransport(handler), **settings)
class JadlogQuoteTest(unittest.TestCase):
def test_payload_follows_the_manual_and_price_becomes_centavos(self):
seen = []
def handler(request):
seen.append(request)
item = json.loads(request.content)['frete'][0]
return httpx.Response(200, json={'frete': [{**item, 'vltotal': 23.455, 'prazo': 4}]})
result = client(handler).quote('01310-100', 1.5, 12990)
request = seen[0]
self.assertEqual(str(request.url), QUOTE_URL)
self.assertEqual(request.headers['authorization'], 'tok-1')
item = json.loads(request.content)['frete'][0]
self.assertEqual(item, {'cepori': '14402310', 'cepdes': '01310100', 'frap': 'N', 'peso': 1.5,
'cnpj': '11222333000181', 'conta': '123456', 'contrato': None,
'modalidade': 3, 'tpentrega': 'D', 'tpseguro': 'N',
'vldeclarado': 129.9, 'vlcoleta': 0})
self.assertEqual((result['total_cents'], result['days']), (2346, 4))
def test_contract_is_sent_only_when_configured(self):
def handler(request):
item = json.loads(request.content)['frete'][0]
self.assertEqual(item['contrato'], '042')
return httpx.Response(200, json={'frete': [{'vltotal': 10, 'prazo': 2}]})
client(handler, contrato='042').quote('01310100', 1, 100)
def test_account_is_sent_as_configured(self):
def handler(request):
self.assertEqual(json.loads(request.content)['frete'][0]['conta'], '123456-7')
return httpx.Response(200, json={'frete': [{'vltotal': 10, 'prazo': 2}]})
JadlogQuotes(token='tok', cnpj=CNPJ, conta=' 123456-7 ', transport=httpx.MockTransport(handler)).quote('01310100', 1, 100)
def test_account_and_item_errors_are_raised_with_jadlog_text(self):
replies = [
httpx.Response(200, json={'frete': [{}], 'error': {'id': -1, 'descricao': 'frete[0].contrato Numero de contrato invalido'}}),
httpx.Response(200, json={'frete': [{'erro': {'id': 2, 'descricao': 'CEP destino invalido'}}]}),
httpx.Response(200, json={'frete': [{'prazo': 3}]}),
httpx.Response(502, text='<html>bad gateway</html>'),
]
messages = ['contrato invalido', 'CEP destino invalido', 'no freight value', 'not JSON']
for reply, message in zip(replies, messages):
with self.subTest(message=message), self.assertRaisesRegex(JadlogError, message):
client(lambda request, reply=reply: reply).quote('01310100', 1, 100)
def test_missing_credentials_refuse_to_start(self):
with self.assertRaises(RuntimeError):
JadlogQuotes(token='', cnpj=CNPJ)
with self.assertRaises(RuntimeError):
JadlogQuotes(token='tok', cnpj='123')
class JadlogFreightTest(unittest.TestCase):
WEIGHTS = {'JADLOG_PESO_BASE_KG': '0.2', 'JADLOG_PESO_POR_METRO_KG': '0.15', 'FREIGHT_PRODUCTION_DAYS': '2'}
def freight(self, handler, **env):
with mock.patch.dict(os.environ, {**self.WEIGHTS, **env}):
return JadlogFreight(client(handler))
def test_package_weight_value_and_days(self):
seen = []
def handler(request):
seen.append(json.loads(request.content)['frete'][0])
return httpx.Response(200, json={'frete': [{'vltotal': 18.4, 'prazo': 3}]})
quote = self.freight(handler).quote('jadlog', '01310100', '2.8', 6972)
# 0.2 kg of packaging plus 0.15 kg for each of the 2.8 billed metres.
self.assertEqual((seen[0]['peso'], seen[0]['vldeclarado'], seen[0]['cepdes']), (0.62, 69.72, '01310100'))
self.assertEqual((quote['total_cents'], quote['days'], quote['service']), (1840, 5, 'jadlog'))
def test_pickup_is_free_and_bad_requests_are_refused(self):
freight = self.freight(lambda request: httpx.Response(500))
self.assertEqual(freight.quote('pickup', '')['total_cents'], 0)
for args in (('mock-standard', '01310100', '1'), ('jadlog', '0131', '1'), ('jadlog', '01310100', None)):
with self.subTest(args=args), self.assertRaises(ValueError):
freight.quote(*args)
def test_a_jadlog_failure_is_a_clear_refusal(self):
freight = self.freight(lambda request: httpx.Response(200, json={'frete': [{}], 'error': {'id': -1, 'descricao': 'CEP destino invalido'}}))
with self.assertRaisesRegex(ValueError, 'CEP destino invalido'):
freight.quote('jadlog', '01310100', '1', 100)
def test_the_weight_has_no_default(self):
with mock.patch.dict(os.environ, {}, clear=True), self.assertRaises(KeyError):
JadlogFreight(client(lambda request: httpx.Response(200)))
with self.assertRaises(RuntimeError):
self.freight(lambda request: httpx.Response(200), JADLOG_PESO_POR_METRO_KG='0')
class JadlogProbeTest(unittest.TestCase):
def test_bearer_is_tried_once_after_a_401_and_kept(self):
headers = []
def handler(request):
headers.append(request.headers['authorization'])
if not request.headers['authorization'].startswith('Bearer '):
return httpx.Response(401, json={'descricao': 'TOKEN INVALIDO', 'id': 1})
return httpx.Response(200, json={'frete': [{'vltotal': 12.5, 'prazo': 3}]})
lines = []
status = run(client(handler), ['01310100', '20040002'], ['1'], 10000, lines.append)
self.assertEqual(status, 0)
self.assertEqual(headers, ['tok-1', 'Bearer tok-1', 'Bearer tok-1'])
self.assertIn('Bearer', lines[0])
self.assertIn('R$ 12,50', lines[1])
def test_first_failure_stops_the_run(self):
calls = []
def handler(request):
calls.append(request)
return httpx.Response(200, json={'frete': [{}], 'error': {'id': -1, 'descricao': 'CNPJ invalido'}})
lines = []
self.assertEqual(run(client(handler), ['01310100', '20040002'], ['1', '2'], 10000, lines.append), 1)
self.assertEqual(len(calls), 1)
self.assertIn('CNPJ invalido', lines[0])
if __name__ == '__main__':
unittest.main()