Caderno operacional — bytes, contratos e perdas

Situação concreta

Você precisa entregar ordens de manutenção a outro sistema por arquivo JSON. A equipe garante que “é só texto”, mas ninguém testou acentos, custo negativo, campos ausentes ou alteração durante a cópia. Neste laboratório, você criará um produtor e um validador pequenos, usando somente a biblioteca padrão do Python e dados fictícios.

Pré-requisitos, objetivos e artefatos

Leia Do bit ao significado. Tenha Python 3 e um editor. Crie laboratorio-dados e produza contrato.py, ordem.json, ordens.csv e relatorio.md.

O objetivo é provar quatro propriedades: texto usa UTF-8; a estrutura restaurada equivale à original; entradas inválidas são recusadas pelo motivo esperado; alteração de bytes muda o checksum.

Vocabulário operacional

  • Payload é a representação de dados carregada numa mensagem ou arquivo.
  • Round trip é o caminho valor → representação → valor equivalente.
  • Hash transforma bytes em um resumo de tamanho fixo.
  • SHA-256 é uma função hash padronizada; aqui será usada para detectar diferença, não para provar autoria.
  • Injeção de fórmula CSV ocorre quando uma planilha interpreta conteúdo não confiável como fórmula.

Modelo mental e limite

Fluxo: Criar valor do domínio, Validar contrato, Serializar em JSON UTF-8, Calcular checksum, Ler e desserializar, Comparar significado, Registrar evidênciaCriar valor do domínioValidar contratoSerializar em JSON UTF-8Calcular checksumLer e desserializarComparar significadoRegistrar evidência
Ler o fluxo em texto
  1. 1. Criar valor do domínio
  2. 2. Validar contrato
  3. 3. Serializar em JSON UTF-8
  4. 4. Calcular checksum
  5. 5. Ler e desserializar
  6. 6. Comparar significado
  7. 7. Registrar evidência

O experimento valida um contrato manual reduzido. Em produção, use uma implementação de JSON Schema avaliada, restrições no armazenamento e testes de integração. Um hash igual mostra bytes iguais; não mostra que a coleta era verdadeira ou autorizada.

Antes e agora

Antes, integrações aceitavam amostras felizes e problemas apareciam na importação mensal. Hoje, contratos versionados e casos negativos podem rodar a cada mudança. IA pode gerar amostras, mas o responsável pelo domínio define o significado e revisa casos perigosos; dados reais não devem ser enviados a ferramentas sem autorização.

Passo 1 — escreva a validação

Crie contrato.py com imports e regra:

import csv
import hashlib
import json
from pathlib import Path

def validar(ordem: dict) -> list[str]:
    erros = []
    obrigatorios = {"schemaVersion", "ordemId", "custoCentavos", "status"}
    ausentes = obrigatorios - ordem.keys()
    if ausentes:
        erros.append("ausentes:" + ",".join(sorted(ausentes)))
    if ordem.get("schemaVersion") != 1:
        erros.append("versao_incompativel")
    if not isinstance(ordem.get("ordemId"), str) or not ordem.get("ordemId"):
        erros.append("ordemId_invalido")
    custo = ordem.get("custoCentavos")
    if not isinstance(custo, int) or isinstance(custo, bool) or custo < 0:
        erros.append("custo_invalido")
    if ordem.get("status") not in {"aberta", "concluida"}:
        erros.append("status_invalido")
    return erros

O conjunto declara obrigatoriedade. As verificações seguintes produzem códigos estáveis. Em Python, bool é subtipo de int, então a condição o exclui explicitamente. Isso demonstra por que “tipo número” pode precisar de precisão adicional.

Passo 2 — serialize, restaure e compare

Acrescente:

ordem = {
    "schemaVersion": 1,
    "ordemId": "OS-SÃO-JOSÉ-01",
    "custoCentavos": 1050,
    "status": "aberta",
    "observacao": None,
}

assert validar(ordem) == []
destino = Path("ordem.json")
destino.write_text(
    json.dumps(ordem, ensure_ascii=False, indent=2),
    encoding="utf-8",
)
bytes_gravados = destino.read_bytes()
restaurada = json.loads(bytes_gravados.decode("utf-8"))
assert restaurada == ordem
print("caracteres:", len(destino.read_text(encoding="utf-8")))
print("bytes:", len(bytes_gravados))
print("sha256:", hashlib.sha256(bytes_gravados).hexdigest())

ensure_ascii=False mantém caracteres legíveis; UTF-8 define os bytes. A igualdade confirma o round trip dos tipos suportados. Uma data transformada em string exigiria reconstrução do tipo do domínio.

Passo 3 — execute casos negativos

Acrescente uma tabela de casos:

casos_invalidos = [
    ({"schemaVersion": 1}, "ausentes:"),
    ({**ordem, "schemaVersion": 2}, "versao_incompativel"),
    ({**ordem, "ordemId": 1842}, "ordemId_invalido"),
    ({**ordem, "custoCentavos": -1}, "custo_invalido"),
    ({**ordem, "status": "?"}, "status_invalido"),
]

for numero, (caso, esperado) in enumerate(casos_invalidos, start=1):
    observados = validar(caso)
    assert any(erro.startswith(esperado) for erro in observados)
    print("caso", numero, "recusado:", observados)

Execute python contrato.py. Os cinco casos precisam falhar pelo motivo esperado. Recusar por acidente, com exceção não prevista, não satisfaz o teste.

Passo 4 — demonstre checksum e bytes

No relatório, copie o checksum. Acrescente um espaço ao final de ordem.json e calcule novamente em um terminal Python:

python -c "import hashlib; print(hashlib.sha256(open('ordem.json','rb').read()).hexdigest())"

O JSON ainda pode ser válido, mas o hash muda porque bytes mudaram. Não conclua que houve ataque ou identifique autor; SHA-256 aqui detecta diferença.

Como interpretar a evidência

Não resuma o resultado como “todos os testes passaram”. Relacione cada observação à propriedade demonstrada. A comparação restaurada == ordem sustenta equivalência dos valores que o JSON consegue representar neste exemplo; não prova que uma data voltará como objeto de data. A recusa do custo negativo sustenta uma regra do domínio; não prova que todo custo positivo está correto. O checksum sustenta igualdade de bytes entre duas medições; não sustenta autenticidade.

No relatório, use quatro colunas: propriedade, experimento, resultado e limite. Essa separação evita que uma evidência estreita seja usada para uma afirmação ampla. Se um caso falhar, preserve a entrada e o código da regra. Alterar simultaneamente schema, dado e programa impede saber qual mudança corrigiu o problema.

Passo 5 — exporte CSV com segurança

Acrescente ao programa:

def neutralizar_planilha(valor: str) -> str:
    return "'" + valor if valor.startswith(("=", "+", "-", "@")) else valor

with open("ordens.csv", "w", encoding="utf-8", newline="") as arquivo:
    escritor = csv.writer(arquivo)
    escritor.writerow(["ordemId", "observacao"])
    escritor.writerow([ordem["ordemId"], neutralizar_planilha("=1+1")])

Não monte CSV concatenando vírgulas. csv.writer trata aspas e delimitadores; a função neutraliza um valor destinado a planilha. Essa regra depende do consumidor e deve ser testada nele.

Falha e diagnóstico

Provoque três falhas separadamente:

  1. leia UTF-8 como latin-1 e observe texto corrompido;
  2. troque custo para 10.50 e observe recusa de tipo;
  3. remova status e confirme o código de ausência.

Para cada uma, registre bytes, esperado, observado, fronteira da divergência e correção mínima. Não substitua caracteres corrompidos antes de preservar a amostra.

Aplicação em manutenção/ERP

Antes de integrar, obtenha contrato oficial do ERP: nomes, tamanhos, codificação, unidades, datas e política de erro. Use ambiente de homologação. Reconcilie quantidade de ordens, identificadores e totais; “importação concluída” não prova equivalência. Para versão 2, publique leitor que aceite v1/v2 antes do produtor novo e mantenha rollback.

Uma migração precisa de contagens antes e depois. Registre quantos itens foram lidos, convertidos, recusados e reconciliados. Mantenha o lote original imutável até a conferência. Se dez registros forem recusados, a decisão não é apagá-los para fazer o indicador ficar verde: coloque-os em quarentena, atribua responsável e preserve a causa. Uma restauração só está comprovada quando quantidade, checksums aplicáveis e invariantes do domínio são verificados.

Segurança e privacidade

Use dados sintéticos. Limite tamanho antes de carregar JSON inteiro, não registre payload completo e não aceite caminho fornecido pelo usuário sem confinamento. Neutralização CSV reduz um risco específico, mas não torna o conteúdo confiável. Hash sem autenticação pode ser substituído junto com o arquivo; assinatura exige chaves e fica fora deste laboratório.

Exercício guiado

Acrescente abertaEm como string com offset e valide pelo menos presença de T e deslocamento. Crie um caso sem fuso e registre a limitação da validação manual. Depois acrescente moeda: "BRL" sem remover custoCentavos; explique por que isso é uma expansão compatível.

Faça também um teste de transferência: substitua ordem por leitura de medidor, mantendo a disciplina de contrato. Identificador continua textual, mas custo vira valor e unidade. Escreva antes do código o que significa leitura ausente, zero e inválida. Se você reutilizar null para os três, o consumidor não poderá decidir se deve repetir coleta, ignorar ou alertar.

Desafio e evidência

Entregue os quatro artefatos e peça que outra pessoa execute somente com seu relatório. Ela deve reproduzir cinco recusas, round trip e mudança de checksum.

Critérios de aceite:

  • prosa registra Python, sistema, comando e horário;
  • UTF-8, tipos e unidades estão explícitos;
  • cinco casos negativos falham pelo motivo previsto;
  • nenhum dado real aparece;
  • checksum é descrito como detecção, não autoria;
  • plano v2 contém expansão, migração, reconciliação e contração.

Conclusão e transição

Você observou a cadeia valor, schema, JSON, UTF-8, bytes e restauração. Também viu que arquivo válido pode conter significado errado e que hash responde uma pergunta limitada. No próximo livro, esses bytes atravessarão DNS, transporte, TLS e HTTP até outro processo.

Fontes oficiais

Teste de fixação

Comprove o que você aprendeu

Responda todas as questões. O gabarito comentado só aparece depois do envio.

1. Após serializar uma data e ler o JSON, o resultado é apenas uma string sem reconversão. O que foi preservado?
2. Ao substituir `custoCentavos` por `{moeda, unidadesMenores}`, qual sequência reduz quebra dos consumidores?
3. Um backup foi restaurado e o arquivo abre. Qual verificação ainda é necessária para aceitar a recuperação?

Consulta universal

O que você quer encontrar?

Títulos, capítulos, conceitos, termos, laboratórios e ferramentas em uma única busca.

Digite pelo menos dois caracteres.