☕ Um Café no Bellacosa Mainframe
Gostou do conteúdo? Ajude a manter o café quente, o COBOL compilando e o mainframe acordado. 😄
☕ Pague um café ao Bellacosa

Translate

Mostrar mensagens com a etiqueta API Testing. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta API Testing. Mostrar todas as mensagens

quarta-feira, 8 de dezembro de 2021

🤖 NATHAN BATEMAN E A API QUE ACHAVA QUE ERA HUMANA

 

Bellacosa Mainframe e o teste de api

☕ Um Café no Bellacosa Mainframe

🤖 NATHAN BATEMAN E A API QUE ACHAVA QUE ERA HUMANA

HTTP, REST, JSON, Postman, OpenAPI, testes positivos e negativos, OAuth, RACF, Db2, CICS, COBOL, contratos, performance, segurança, CI/CD, observabilidade — e o dia em que um programador COBOL descobriu que receber 200 OK não provava absolutamente nada.

Sob a tutela de Nathan Bateman, de Ex Machina — porque, se existe alguém capaz de olhar para uma interface aparentemente perfeita e perguntar “mas o que realmente está acontecendo atrás dela?”, é Nathan.



🎬 PRÓLOGO — BEM-VINDO À CASA, PROGRAMADOR

A porta se fecha atrás de você.

Não existe maçaneta.

À sua frente há vidro, concreto, câmeras, servidores e uma quantidade desconfortável de tecnologia escondida nas paredes.

Nathan coloca uma cerveja sobre a mesa.

— Você programa COBOL?

— Sim.

— CICS?

— Sim.

— Db2?

— Também.

Ele sorri.

— Excelente. Então hoje você vai testar uma API.

Você olha para ele como se tivesse acabado de pedir para compilar Java usando um IBM 029.

API?

REST?

JSON?

Bearer Token?

OpenAPI?

Postman?

Você passou anos pensando em:

COBOL
JCL
CICS
Db2
VSAM
RACF
MQ

E agora aparece uma tela mostrando:

POST /api/v1/payments

Nathan aponta para o monitor.

{
  "account": "123456",
  "amount": 500.00
}

Depois surge:

HTTP/1.1 200 OK

Nathan pergunta:

— Funcionou?

Você responde imediatamente:

— Sim.

Ele sorri novamente.

Era uma armadilha.

Porque acabamos de cometer um dos erros mais perigosos em testes de APIs:

confundir uma resposta tecnicamente bem-sucedida com uma transação de negócio corretamente executada.

Bem-vindo ao laboratório.

Hoje não vamos apenas aprender API Testing.

Vamos descobrir o que existe atrás do vidro.



🧠 CAPÍTULO 1 — A API É AVA?

Em Ex Machina, Caleb inicialmente vê Ava através de uma interface.

Existe uma parede entre eles.

Ele conversa com ela.

Ela responde.

Pergunta:

Caleb
  ↓
interface
  ↓
Ava

Para Caleb, aquilo parece simples.

Mas por trás da interface existe uma quantidade enorme de tecnologia que ele não vê.

Uma API funciona de maneira parecida.

Imagine um aplicativo bancário.

O usuário toca:

CONSULTAR SALDO

O aplicativo envia:

GET /accounts/12345/balance

Recebe:

{
  "account": "12345",
  "balance": 7342.91
}

Para o celular, acabou.

Mas nós somos profissionais de mainframe.

Queremos abrir a parede.

Talvez exista:

APP MOBILE
    ↓
INTERNET
    ↓
API GATEWAY
    ↓
z/OS CONNECT
    ↓
CICS
    ↓
COBOL
    ↓
DB2

Ou:

API
 ↓
IMS
 ↓
COBOL
 ↓
IMS DB

Ou ainda:

API
 ↓
CICS
 ↓
COBOL
 ↓
MQ
 ↓
OUTRO SISTEMA

Essa é a primeira lição.

API não substitui necessariamente o mainframe. API pode simplesmente fornecer uma nova porta para chegar até ele.



🔌 CAPÍTULO 2 — API NÃO É MÁGICA

API significa Application Programming Interface.

Em termos simples, ela estabelece uma forma definida para dois softwares conversarem.

Temos:

CLIENTE
   |
 REQUEST
   ↓
  API
   |
 PROCESSAMENTO
   ↓
 RESPONSE
   |
   ↓
CLIENTE

Para quem vem do COBOL, isso não deveria parecer tão alienígena.

Pense numa COMMAREA.

Um programa recebe uma estrutura:

01 WS-REQUEST.
   05 WS-CUSTOMER-ID PIC 9(10).
   05 WS-OPERATION   PIC X(01).

Processa e devolve:

01 WS-RESPONSE.
   05 WS-RETURN-CODE PIC 9(04).
   05 WS-NAME        PIC X(40).
   05 WS-BALANCE     PIC S9(9)V99 COMP-3.

Uma API também recebe dados e devolve dados.

A representação mudou.

Podemos receber:

{
  "customerId": 123456
}

e responder:

{
  "name": "BELLACOSA",
  "balance": 317.00
}

O jovem programador olha para JSON e pensa:

— Moderníssimo!

O veterano COBOL olha e pensa:

— É um layout de dados usando chaves, colchetes e bastante marketing.

Nathan provavelmente aprovaria a segunda resposta.



🌐 CAPÍTULO 3 — HTTP: O PROTOCOLO QUE CARREGA A CONVERSA

Muitas APIs REST utilizam HTTP.

Uma requisição pode conter:

URL
METHOD
HEADERS
QUERY PARAMETERS
BODY

Exemplo:

POST /customers HTTP/1.1
Host: api.banco.com
Content-Type: application/json
Authorization: Bearer eyJ...

Body:

{
  "name": "Caleb",
  "age": 26
}

Observe as peças.

Endpoint é o endereço lógico do recurso:

/customers

Headers carregam informações adicionais:

Content-Type
Authorization
Accept
Correlation-ID

Body contém os dados enviados.

Isso nos leva aos verbos HTTP.



🛠️ CAPÍTULO 4 — GET, POST, PUT, PATCH E DELETE

Os cinco verbos mais importantes para nosso iniciante são:

GET     consultar
POST    criar/processar
PUT     substituir
PATCH   alterar parcialmente
DELETE  remover

Podemos construir uma analogia COBOL/Db2 apenas para aprendizado:

GET       ≈ SELECT
POST      ≈ INSERT/operação
PUT       ≈ UPDATE completo
PATCH     ≈ UPDATE parcial
DELETE    ≈ DELETE

Mas escreva em letras enormes no caderno:

ANALOGIA NÃO É EQUIVALÊNCIA.

Considere:

POST /payments

Ele talvez execute:

POST
 ↓
z/OS Connect
 ↓
CICS
 ↓
COBOL
 ├── SELECT DB2
 ├── UPDATE DB2
 ├── INSERT DB2
 ├── MQ PUT
 └── COMMIT

Portanto POST não significa necessariamente INSERT.

Ele significa que estamos solicitando determinada operação segundo o contrato daquela API.


🚦 CAPÍTULO 5 — STATUS CODE É O RETURN CODE DA INTERNET?

Mais uma analogia útil, mas imperfeita.

O mainframer conhece:

RC=0000
RC=0004
RC=0008
RC=0012
S0C7
S0C4

HTTP possui famílias de códigos:

1xx → informação
2xx → sucesso
3xx → redirecionamento
4xx → problema relacionado à requisição
5xx → falha do servidor

Alguns essenciais:

200 OK
201 Created
204 No Content

400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
429 Too Many Requests

500 Internal Server Error
503 Service Unavailable

Mas aqui Nathan interrompe a aula.

— Você recebeu 200. Então funcionou?

Não necessariamente.

Veja:

HTTP/1.1 200 OK
{
  "transaction": "FAILED",
  "reason": "INSUFFICIENT_FUNDS"
}

HTTP funcionou.

A API respondeu.

Mas a operação de negócio falhou.

Temos, portanto, pelo menos três perguntas diferentes:

TRANSPORTE
HTTP funcionou?
       ↓
CONTRATO
Resposta possui a estrutura correta?
       ↓
NEGÓCIO
A operação produziu o resultado correto?

Esse pequeno detalhe separa teste superficial de teste sério.


📜 CAPÍTULO 6 — OPENAPI: O COPYBOOK DO MUNDO REST?

Calma.

Não literalmente.

Mas para ensinar COBOL é uma analogia fantástica.

Um copybook pode definir:

01 CUSTOMER.
   05 CUSTOMER-ID     PIC 9(10).
   05 CUSTOMER-NAME   PIC X(40).
   05 CUSTOMER-STATUS PIC X(01).

Uma especificação OpenAPI pode descrever uma representação semelhante:

Customer:
  type: object
  required:
    - customerId
    - customerName
  properties:
    customerId:
      type: integer
    customerName:
      type: string
    status:
      type: string

Antes de testar uma API, leia sua documentação.

Procure:

endpoints
métodos
parâmetros
headers
schemas
autenticação
exemplos
responses
erros

Não comece clicando freneticamente em Send no Postman.

Primeiro descubra qual deveria ser o comportamento.

Teste sem requisito vira adivinhação automatizada.


🧪 CAPÍTULO 7 — POSTMAN: O TERMINAL 3270 DO EXPLORADOR DE APIs

Imagine esta evolução:

3270
 ↓
CICS
 ↓
COBOL

Agora:

Postman
 ↓
HTTP
 ↓
API
 ↓
z/OS Connect
 ↓
CICS
 ↓
COBOL

O Postman permite montar requisições, headers, parâmetros, autenticação e body, além de examinar respostas e organizar chamadas em collections.

Comece simples.

Faça:

GET /customers/123

Clique em Send.

Não comemore ainda.

Inspecione:

status code
headers
body
schema
response time
business rules

Uma resposta não deve ser considerada correta simplesmente porque apareceu alguma coisa na tela.


😊 CAPÍTULO 8 — HAPPY PATH: PRIMEIRO PROVE QUE AVA CONSEGUE CONVERSAR

O teste positivo verifica comportamento esperado com entradas válidas.

Exemplo:

POST /customers
{
  "name": "Nathan Bateman",
  "type": "PREMIUM"
}

Esperamos talvez:

201 Created

e:

{
  "customerId": 12345,
  "name": "Nathan Bateman",
  "type": "PREMIUM"
}

Validamos:

dados válidos
fluxo correto
permissão adequada
resultado esperado
persistência correta

Ótimo.

Mas isso prova apenas que o sistema funciona quando todos se comportam.

Produção não possui essa delicadeza.


😈 CAPÍTULO 9 — O TESTE COMEÇA QUANDO AS COISAS DÃO ERRADO

Agora Nathan fica interessado.

Envie:

{
  "amount": -500
}

Depois:

{
  "amount": "CINCO REAIS"
}

Depois:

{}

Depois:

{
  "amount": null
}

Teste:

  • campos ausentes;

  • tipos inválidos;

  • limites;

  • JSON malformado;

  • dados duplicados;

  • métodos não suportados;

  • valores negativos;

  • valores enormes;

  • strings vazias;

  • recursos inexistentes;

  • credenciais inválidas.

O sistema bom não é aquele que apenas sabe funcionar.

É aquele que sabe falhar corretamente.


💣 CAPÍTULO 10 — PIC 9(7)V99 ENCONTRA JSON

Aqui aparece um problema particularmente interessante para mainframers.

COBOL:

05 WS-AMOUNT PIC 9(7)V99.

API:

{
  "amount": 999999999999999999.99
}

Quem impede isso?

Gateway?

Schema?

z/OS Connect?

Programa intermediário?

COBOL?

Db2?

E se ninguém impedir?

Esse é um maravilhoso teste de boundary.

Teste:

0
0.01
9999999.99
10000000.00
-0.01
NULL
"ABC"

A fronteira é justamente onde muitos defeitos ficam escondidos.


🔐 CAPÍTULO 11 — "QUEM É VOCÊ?" NÃO É "O QUE VOCÊ PODE FAZER?"

Autenticação:

Quem é você?

Autorização:

O que você pode acessar?

Essa distinção é vital.

No IBM Z podemos encontrar uma cadeia como:

OAuth/OIDC
    ↓
API Gateway
    ↓
token
    ↓
z/OS
    ↓
SAF
    ↓
RACF

Mas autenticar alguém não significa permitir qualquer operação.

Teste:

sem token
token inválido
token expirado
token correto
scope incorreto
usuário sem privilégio
usuário privilegiado
recurso de outro usuário

Um 401 e um 403 não significam exatamente a mesma coisa.

E existe uma pergunta ainda mais interessante:

Um usuário autorizado para consultar seus dados consegue trocar /123 por /124 e consultar dados de outra pessoa?

Isso testa autorização no nível do objeto, não apenas login.


🧬 CAPÍTULO 12 — TEST DATA: NÃO USE PRODUÇÃO COMO PARQUINHO

Dados de teste precisam ser:

controlados
repetíveis
realistas
seguros
isolados
limpáveis

No mainframe isso pode ser particularmente delicado.

Existem ambientes com dados acumulados durante décadas.

Você encontrará:

EBCDIC
COMP
COMP-3
campos redefinidos
datas históricas
códigos descontinuados
valores especiais
copybooks antigos
registros migrados

E existe ainda segurança e privacidade.

Mascarar dados sensíveis é fundamental quando dados semelhantes aos de produção precisam alimentar testes.

E nunca coloque senha, token ou segredo dentro do script:

PASSWORD=SUPERSECRET123

Nathan certamente encontraria.

O auditor também.


🗄️ CAPÍTULO 13 — DATABASE VALIDATION: OLHE ATRÁS DO VIDRO

Agora chegamos ao coração do problema.

Você envia:

POST /transfers
{
  "from": "100001",
  "to": "200002",
  "amount": 500.00
}

Recebe:

200 OK

Fim?

Não.

Precisamos descobrir o efeito.

Antes:

A = 2000
B = 1000

Depois:

A = 1500
B = 1500

Precisamos validar:

débito correto
crédito correto
nenhum registro duplicado
histórico criado
mensagem MQ correta
audit trail
commit

Agora provoque uma falha depois do débito e antes do crédito.

O resultado correto provavelmente deverá envolver:

ROLLBACK

e não:

A = 1500
B = 1000

Essa é a diferença entre testar uma tela JSON e testar uma transação.


🔄 CAPÍTULO 14 — COMMIT, ROLLBACK E A UNIT OF WORK

Mainframe conhece esse problema há décadas.

Uma API pode iniciar uma cadeia:

REQUEST
 ↓
CICS
 ↓
COBOL
 ↓
DB2
 ↓
MQ

Agora surgem perguntas realmente interessantes:

O que pertence à mesma unidade de trabalho?

Quando ocorre COMMIT?

O que acontece se MQ falhar?

E se Db2 estiver indisponível?

E se a API sofrer timeout?

E se o cliente repetir a chamada?

Chegamos à palavra que deveria estar colada no monitor de qualquer desenvolvedor de pagamentos:

IDEMPOTÊNCIA

Imagine:

03:16:55 pagamento enviado

03:17:00 processamento iniciado

03:17:10 timeout

O aplicativo não sabe se funcionou.

Então repete.

Se o backend executar novamente:

R$500
R$500

acabamos de cobrar duas vezes.

Uma estratégia pode envolver uma chave de idempotência:

Idempotency-Key: PAY-ABC-123

O backend reconhece a repetição.

E sim: 03:17 apareceu por acaso.

Quem acompanha o Bellacosa Mainframe sabe que acidentes temporais desse tipo costumam ser cuidadosamente planejados. ☕


📐 CAPÍTULO 15 — CONTRACT TESTING: AVA MUDOU A LINGUAGEM

Hoje:

{
  "customerId": 123,
  "status": "ACTIVE"
}

Amanhã alguém decide "modernizar":

{
  "customerNumber": 123,
  "customerStatus": "ACTIVE"
}

O serviço continua funcionando.

Mas dez consumidores quebram.

É o equivalente moderno do sujeito que altera um copybook compartilhado sem estudar impacto e depois pergunta por que metade da madrugada está em conference call.

Contract testing procura detectar mudanças incompatíveis antes que cheguem aos consumidores.

Aqui o mainframer possui uma vantagem cultural enorme.

Ele já conhece o medo saudável de:

"Quem mais usa isso?"


🏎️ CAPÍTULO 16 — PERFORMANCE: 120 MILISSEGUNDOS NÃO CONTAM TODA A HISTÓRIA

Uma API responde em:

120 ms

Excelente.

Com um usuário.

Agora tente 100.

1.000.

10.000.

50.000.

Precisamos observar:

latency — tempo de resposta;

throughput — quantidade processada;

error rate — percentual de falhas;

concurrency — usuários/operações simultâneos.

Existem ainda quatro cenários clássicos:

LOAD
carga normal esperada

SPIKE
explosão repentina

STRESS
além do limite normal

SOAK
carga sustentada durante longo período

No IBM Z, não pare no tempo HTTP.

Investigue:

API Gateway
     ↓
z/OS Connect
     ↓
CICS
     ↓
COBOL
     ↓
Db2
     ↓
CPU / I/O / LOCKS

Pode ser que HTTP esteja apenas mostrando o sintoma.


🕵️ CAPÍTULO 17 — SEGURANÇA: NATHAN NÃO CONFIA NA INTERFACE

API Security Testing precisa procurar problemas como:

autenticação
autorização
input validation
rate limiting
exposição de dados
configurações inseguras

No mainframe adicionamos outra camada:

SAF
RACF
TLS
AT-TLS
certificados
SMF
proteção CICS
privilégios Db2
permissões USS
segregação de funções
auditoria

Um erro particularmente perigoso é retornar informação demais.

Exemplo:

{
  "name": "USER",
  "balance": 500,
  "internalRacfUser": "ABC123",
  "databaseHost": "PRODDB2",
  "debugMessage": "SQLCODE -..."
}

Talvez três desses campos jamais devessem sair dali.

Erro também é dado.

Log também é dado.

Stack trace também é dado.

Portanto teste o que o sistema revela quando falha.


🤖 CAPÍTULO 18 — AUTOMATIZE, MAS NÃO AUTOMATIZE BURRICE

Depois dos testes manuais estáveis, automatizamos.

Ferramentas possíveis incluem:

Postman/Newman
REST Assured
Playwright API
SuperTest

Um framework saudável separa:

config/
api-clients/
test-data/
tests/
reports/

Isso evita aquele script de 4.000 linhas onde URL, senha, payload, teste, relatório e provavelmente o CPF do desenvolvedor estão todos misturados.

Automação boa precisa ser:

repetível
independente
legível
determinística
diagnosticável
manutenível

Automatizar um teste ruim produz um teste ruim que roda mais rápido.


🏭 CAPÍTULO 19 — CI/CD: O COBOL ENTRA NA ESTEIRA

Agora o teste deixa de depender de alguém clicar em Send.

Podemos ter:

COMMIT
   ↓
BUILD
   ↓
UNIT TEST
   ↓
API TEST
   ↓
CONTRACT TEST
   ↓
SECURITY CHECK
   ↓
DEPLOY DEV
   ↓
INTEGRATION TEST
   ↓
QUALITY GATE
   ↓
PROMOTION

Isso também é DevOps no mainframe.

COBOL não precisa deixar de ser COBOL.

CICS não precisa fingir que é Kubernetes.

Db2 não precisa colocar boné para trás.

O objetivo é introduzir práticas modernas de engenharia em torno das plataformas adequadas.


🔭 CAPÍTULO 20 — OBSERVABILIDADE: AVA RESPONDEU, MAS O QUE ELA ESTAVA FAZENDO?

Chegamos ao estágio mais interessante.

Testes dizem:

"Sob estas condições, obtive este resultado."

Observabilidade pergunta:

"O que o sistema está realmente fazendo?"

Precisamos de evidências.

logs
metrics
traces
request IDs
test reports
artifacts

No IBM Z temos uma riqueza enorme de telemetria:

SMF
RMF
CICS statistics
Db2 accounting
Db2 statistics
MQ statistics
WLM
logs
network telemetry

Imagine colocar:

X-Request-ID: CALEB-0317

e acompanhar:

CALEB-0317
    ↓
API Gateway
    ↓
z/OS Connect
    ↓
CICS
    ↓
COBOL
    ↓
Db2

De repente não temos apenas:

"A API demorou 4 segundos."

Temos:

"A requisição chegou ao z/OS, entrou na transação CICS, esperou determinado recurso e a maior parte do tempo foi consumida naquela etapa."

Isso muda completamente a investigação.


🧪 CAPÍTULO 21 — O LABORATÓRIO FINAL DE NATHAN

Sua missão é testar:

POST /accounts/transfer

Payload:

{
  "from": "100001",
  "to": "200002",
  "amount": 500.00
}

Arquitetura:

CLIENT
  ↓
HTTPS
  ↓
API GATEWAY
  ↓
z/OS CONNECT
  ↓
CICS
  ↓
COBOL
  ↓
DB2
  ↓
MQ

Comece pelo happy path.

Depois execute:

conta origem inexistente
conta destino inexistente
saldo insuficiente
amount = 0
amount negativo
amount máximo
amount acima do máximo
amount como texto
campo ausente
JSON quebrado
token ausente
token expirado
scope incorreto
usuário sem autorização
requisição duplicada
timeout
Db2 indisponível
MQ indisponível
100 requests simultâneos
1.000 requests
spike
soak

Depois de cada cenário, pergunte:

HTTP correto?
JSON correto?
Schema correto?
Regra de negócio correta?
Db2 correto?
MQ correto?
Commit correto?
Rollback correto?
RACF correto?
Logs corretos?
SMF mostra o esperado?
Performance aceitável?
Dados sensíveis protegidos?

Agora você não está mais testando simplesmente uma API.

Está testando um serviço de negócio distribuído de ponta a ponta.


🧠 CAPÍTULO 22 — O TESTE DE TURING DO MAINFRAME

Nathan volta à sala.

Na tela aparecem duas respostas:

{
  "status": "SUCCESS"
}

e:

{
  "status": "SUCCESS"
}

São idênticas.

Nathan pergunta:

— Qual delas está correta?

Você não responde.

Abre o Db2.

Verifica a transação CICS.

Consulta evidências.

Confere MQ.

Verifica autorização.

Procura duplicidades.

Analisa logs.

Correlaciona Request ID.

Observa métricas.

Confere o contrato.

Finalmente responde:

— A primeira.

Nathan pergunta:

— Como sabe?

E aí está toda a diferença:

Porque eu não confiei na interface. Eu validei o sistema.


☕ EPÍLOGO — A API NÃO PRECISAVA SER HUMANA

Existe uma deliciosa ironia nessa história.

Muitos conceitos vendidos como absolutamente modernos são problemas que o mundo mainframe enfrenta há décadas.

Hoje dizemos:

schema

O mainframer lembra de layouts e copybooks.

Hoje:

transaction consistency

Ele pensa em unit of work, commit e rollback.

Hoje:

authorization

Ele pensa em SAF/RACF e também nas regras de autorização da aplicação.

Hoje:

observability

Ele pergunta:

— Quais registros SMF temos?

Hoje:

high availability

Ele sorri.

Hoje:

backward compatibility

Agora ele começa a rir.

Isso não significa que REST, OpenAPI, OAuth, CI/CD ou observabilidade moderna sejam apenas nomes novos para tecnologias antigas. Não são.

Significa algo mais interessante.

Os problemas fundamentais da computação permanecem surpreendentemente constantes:

Quem pediu?

Pode pedir?

Os dados são válidos?

O processamento aconteceu?

A informação foi persistida?

A transação terminou inteira?

Se falhou, voltou ao estado consistente?

Se repetiu, duplicou?

Quanto demorou?

Quantas conseguimos processar?

Quem consegue observar isso?

Quem consegue alterar isso?

Conseguimos provar depois o que aconteceu?

E aqui está talvez a maior lição para o programador COBOL iniciante.

Não tenha medo quando alguém colocar na sua frente:

REST
JSON
OAuth
OpenAPI
Postman
CI/CD
Observability

Você não está abandonando tudo o que aprendeu.

Está aumentando seu campo de visão.

Ontem você enxergava:

3270
 ↓
CICS
 ↓
COBOL
 ↓
DB2

Hoje precisa enxergar:

                    INTERNET
                       │
                       ▼
                  API GATEWAY
                       │
                       ▼
                 z/OS CONNECT
                       │
              ┌────────┴────────┐
              ▼                 ▼
            CICS               IMS
              │                 │
              └───────┬─────────┘
                      ▼
                    COBOL
                      │
          ┌───────────┼───────────┐
          ▼           ▼           ▼
         DB2         VSAM         MQ
          │           │           │
          └───────────┼───────────┘
                      ▼
                COMMIT/ROLLBACK
                      │
                      ▼
               RESPONSE JSON

E ao redor de tudo isso:

        SECURITY
            │
            ▼
   ┌───────────────────┐
   │                   │
RACF/SAF            OAuth
   │                   │
   └─────────┬─────────┘
             │
             ▼
          AUDIT
             │
             ▼
       OBSERVABILITY
             │
     ┌───────┼────────┐
     ▼       ▼        ▼
    SMF     LOGS    METRICS

Essa é a arquitetura que o teste precisa enxergar.

A API é apenas o vidro.

Atrás dela existe uma máquina inteira.


🥚 EASTER EGG — 03:17

Às 03:17, todas as luzes do laboratório apagam.

O programador COBOL continua sentado.

Nathan pergunta:

— Você não está preocupado?

— Não.

— Por quê?

Ele aponta para o monitor.

HTTP 200

Nathan sorri.

O programador responde:

— Também não confio nisso.

Abre outra janela.

CICS: OK
DB2 : COMMIT
MQ  : MESSAGE PUT
RACF: AUTHORIZED
SMF : RECORDED

Então pega o café.

Agora sim.

Porque no Bellacosa Mainframe existe uma regra que Nathan Bateman aprenderia rapidamente:

Não importa o quão bonita seja a interface, o quão perfeito seja o JSON ou quantos 200 OK apareçam na tela. Em sistemas de missão crítica, confiança não vem da aparência da resposta. Vem da evidência de que toda a cadeia fez exatamente aquilo que deveria fazer.

E talvez esse seja o verdadeiro Teste de Turing de uma API mainframe:

não descobrir se ela consegue parecer inteligente,

mas provar que, quando ninguém está olhando, ela continua sendo correta, segura, consistente, observável e confiável.

Um Café no Bellacosa Mainframe

Onde até Ava teria que apresentar evidência de COMMIT antes de receber acesso à produção.

segunda-feira, 7 de outubro de 2019

🕯️ John Constantine e a API dos Condenados Requests, Responses, HTTP, JSON, autenticação, autorização, Test Data, ambientes, segurança, automação e aquele 200 OK

 

Bellacosa Mainframe e as APIs

☕ Um Café no Bellacosa Mainframe

🕯️ John Constantine e a API dos Condenados

Requests, Responses, HTTP, JSON, autenticação, autorização, Test Data, ambientes, segurança, automação e aquele 200 OK que jurava que estava tudo bem

Há uma regra que aprendi depois de muitos anos diante de terminais verdes, JCLs, CICS, dumps, arquivos VSAM e bancos de dados:

quando um sistema diz que está tudo bem depressa demais, comece a desconfiar.

John Constantine provavelmente concordaria.

Constantine não é exatamente o sujeito que você chama quando a torneira está vazando. Ele aparece quando a torneira começa a falar latim, o encanador desaparece e alguém encontra um pentagrama desenhado atrás da caixa-d'água.

Com APIs acontece algo semelhante.

Você manda:

GET /customers/42

e recebe:

HTTP/1.1 200 OK

O programador iniciante sorri.

Constantine acende um cigarro imaginário, olha para o monitor e pergunta:

OK para quem?

Essa é a pergunta que guiará nossa investigação.

Porque uma API pode responder 200 OK enquanto devolve os dados errados. Pode autenticar João e entregar os registros de Maria. Pode registrar uma compra duas vezes. Pode diminuir o estoque três vezes. Pode devolver um CPF que jamais deveria estar naquela resposta.

E existe algo ainda mais diabólico.

Às vezes o sistema está certo...

...e o teste é que está errado.

Bem-vindo ao submundo de API Testing.



🕯️ CAPÍTULO 1 — O que diabos é uma API?

API significa:

Application Programming Interface.

Uma definição simples seria:

uma interface que permite que dois componentes de software se comuniquem seguindo regras previamente estabelecidas.

O exemplo clássico é o restaurante.

Temos:

CLIENTE
   ↓
GARÇOM
   ↓
COZINHA

O cliente não entra na cozinha para preparar o prato.

Ele faz um pedido.

O garçom leva esse pedido à cozinha.

A cozinha trabalha.

O garçom retorna com o resultado.

No mundo computacional:

APLICAÇÃO
    ↓
   API
    ↓
SERVIDOR

Uma aplicação meteorológica poderia solicitar:

GET /weather?city=Itatiba

e receber:

{
  "temp": 28,
  "condition": "Cloudy"
}

A aplicação não precisa saber como o servidor calculou aquilo.

Pode haver Python lá atrás.

Java.

Node.js.

Ou algo bem mais interessante para nós:

APP
 │
 ▼
REST API
 │
 ▼
API Gateway
 │
 ▼
z/OS Connect
 │
 ▼
CICS
 │
 ▼
COBOL
 │
 ├── Db2
 ├── VSAM
 ├── IMS
 └── MQ

Para quem chamou a API, isso deveria ser transparente.

Esse é o primeiro princípio importante:

a API oferece um contrato, não uma visita guiada à implementação.



🔮 CAPÍTULO 2 — O contrato

Constantine conhece contratos.

Normalmente os dele envolvem demônios, almas e cláusulas que ninguém deveria assinar sem ler as letras pequenas.

APIs também possuem contratos.

Se estabelecemos:

GET /users/42

podemos definir que a resposta bem-sucedida será:

200 OK
Content-Type: application/json

com:

{
  "id": 42,
  "name": "Asha",
  "active": true
}

Isso estabelece expectativas.

id deve existir.

id deve ser numérico.

name deve ser texto.

active deve ser booleano.

Portanto:

"id": 42

e:

"id": "42"

não são necessariamente equivalentes.

Um frontend permissivo talvez converta silenciosamente um para o outro.

O contrato não deveria depender dessa benevolência.

É justamente aí que entram schemas e especificações como OpenAPI: eles tornam parte dessas expectativas formalmente verificáveis.



🩸 CAPÍTULO 3 — Request e Response

Toda invocação começa com um pedido.

Por exemplo:

POST /users
Content-Type: application/json

Body:

{
  "name": "Asha",
  "role": "Tester"
}

O servidor poderia responder:

201 Created

e:

{
  "id": 42,
  "name": "Asha",
  "role": "Tester"
}

Temos então:

REQUEST
   ↓
API
   ↓
PROCESSAMENTO
   ↓
RESPONSE

O REQUEST é a pergunta.

O RESPONSE é a resposta.

Mas o investigador competente guarda ambos quando alguma coisa dá errado.

Porque perguntar:

"Por que a API falhou?"

sem saber exatamente qual request foi enviado é quase tão produtivo quanto perguntar a Constantine:

"Alguma coisa sobrenatural aconteceu ontem. Descubra."



🪦 CAPÍTULO 4 — Os cinco rituais HTTP

Os métodos mais comuns são:

GET     → consultar
POST    → criar/processar
PUT     → substituir
PATCH   → alterar parcialmente
DELETE  → remover

Uma sequência CRUD poderia ser:

POST   /users
GET    /users/42
PATCH  /users/42
DELETE /users/42

Mas cuidado com uma simplificação comum.

HTTP não é CRUD.

Podemos perfeitamente encontrar:

POST /payments/123/capture

Nesse caso não estamos simplesmente dizendo "CREATE".

Estamos solicitando uma operação de negócio.

Existem ainda métodos como HEAD e OPTIONS, e os métodos permitidos também fazem parte da superfície que merece testes de segurança. A OWASP recomenda verificar métodos HTTP disponíveis e controles de acesso associados, inclusive tentando operações com métodos alternativos quando apropriado.



🚪 CAPÍTULO 5 — URL: o endereço da casa assombrada

Considere:

https://api.shop.com/v1/products/42?currency=BRL

Podemos desmontá-la:

https://api.shop.com
        │
        └── Base URL

/v1/products/42
        │
        └── endpoint/recurso

42
│
└── path parameter

currency=BRL
│
└── query parameter

E imediatamente surgem testes.

Produto existente:

42

Produto inexistente:

999999999

Valor estranho:

-1

Moeda válida:

BRL

Moeda inválida:

MORDOR

Campo ausente.

Campo vazio.

Valor enorme.

Caracteres especiais.

É aqui que o QA começa a se comportar como Constantine:

não pergunta somente o que deveria funcionar; pergunta também o que acontece quando alguém faz aquilo que não deveria.


🔑 CAPÍTULO 6 — Headers: símbolos desenhados na porta

Uma requisição pode carregar informações adicionais:

Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJ...
X-Request-ID: abc-123

Cada header possui uma função.

Content-Type informa o formato enviado.

Accept pode expressar o formato desejado.

Authorization transporta informações necessárias à autenticação/autorização conforme o mecanismo adotado.

E X-Request-ID, ou outro correlation ID equivalente, pode ser extremamente útil.

Imagine:

Browser
   ↓ abc-123
API Gateway
   ↓ abc-123
z/OS Connect
   ↓ abc-123
CICS
   ↓ abc-123
COBOL
   ↓ abc-123
MQ

Quando tudo explode às 03:17, você procura:

abc-123

e consegue reconstruir a passagem daquela transação pelos diversos componentes.

Nos sistemas distribuídos, correlação é praticamente trabalho de detetive.


👹 CAPÍTULO 7 — Os números da besta... HTTP

Não é exatamente 666.

Temos famílias:

1xx → informacional
2xx → sucesso
3xx → redirecionamento
4xx → problema relacionado à requisição/cliente
5xx → falha do lado servidor

Alguns velhos conhecidos:

200 OK
201 Created
204 No Content
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
429 Too Many Requests
500 Internal Server Error
503 Service Unavailable

Mas existe uma distinção especialmente importante:

401 ≠ 403

Simplificando:

401 — você não está adequadamente autenticado.

403 — sabemos quem você é, mas você não pode fazer aquilo.

É a diferença entre Constantine chegar à porta sem a chave e chegar com a chave correta de uma sala para a qual ele continua não tendo autorização.


🧿 CAPÍTULO 8 — Authentication não é Authorization

Temos duas perguntas:

AUTHENTICATION
Quem é você?

AUTHORIZATION
O que você pode fazer?

Considere:

João → autenticado
Maria → autenticada

Isso não significa:

João → pode consultar dados privados de Maria

Teste:

GET /users/43
Authorization: Bearer TOKEN_DO_JOAO

Se 43 pertence a Maria e João não possui autorização para acessar aquele objeto, a aplicação precisa impedir o acesso.

Esse tipo de vulnerabilidade é importante o bastante para aparecer como Broken Object Level Authorization — BOLA no OWASP API Security Top 10.

O teste profissional, portanto, não pergunta apenas:

Tenho token?

Pergunta:

TOKEN A pode acessar RECURSO A?
TOKEN A pode acessar RECURSO B?
TOKEN B pode acessar RECURSO A?

USER pode executar operação ADMIN?
ADMIN pode?

GET é permitido?
DELETE também?
PATCH?

É uma matriz.

ATOR × RECURSO × AÇÃO

A OWASP descreve justamente esse modelo ator–recurso–ação como uma forma útil de representar e automatizar testes de autorização.


🧪 CAPÍTULO 9 — Positive, Negative e Boundary Testing

Suponha:

idade permitida = 18 até 60

Teste positivo:

25 → ACCEPT

Teste negativo:

"CONSTANTINE" → REJECT

Agora vêm os limites:

17
18
60
61

Por quê?

Porque um humilde:

IF WS-AGE > 18

em vez de:

IF WS-AGE >= 18

pode mandar o jovem de exatamente 18 anos diretamente para o purgatório da regra de negócio.

Os bugs adoram morar nas fronteiras.

Isso vale para:

quantidade
tamanho de senha
valor financeiro
número de itens
datas
paginação
strings
limites de crédito

Sempre pergunte:

o que acontece exatamente antes, exatamente no limite e exatamente depois?


🧟 CAPÍTULO 10 — Um erro correto também é PASS

Esta é deliciosa.

Mandamos:

{}

para uma operação que exige email.

Esperamos:

400

ou talvez 422, conforme o contrato adotado.

E algo como:

{
  "code": "EMAIL_REQUIRED",
  "message": "Email is required",
  "field": "email"
}

Se a aplicação rejeitar corretamente a requisição inválida...

o teste passou.

Parece contraditório somente enquanto confundimos:

API retornou erro

com:

TESTE falhou

Um teste negativo pode perfeitamente produzir:

HTTP ERROR + TEST PASS

O que seria ruim?

Enviar uma requisição inválida e receber:

500 Internal Server Error

quando o contrato previa uma rejeição controlada.

Ou pior:

400 Bad Request

mas metade da transação já foi gravada.

Constantine abre o banco de dados.

E encontra o demônio escondido lá.


🏦 CAPÍTULO 11 — COMMIT e ROLLBACK entram no exorcismo

Agora o mainframeiro começa a sorrir.

Imagine:

Criar pedido
   ↓
Debitar limite
   ↓
Diminuir estoque
   ↓
Criar pagamento

A terceira etapa falha.

O que acontece?

Se o sistema deveria tratar tudo como uma unidade lógica de trabalho, talvez esperemos:

ROLLBACK

e não:

pedido criado
limite debitado
estoque inalterado
pagamento inexistente

Esse é o tipo de criatura que aparece quando testamos apenas o status HTTP.

Recebemos:

500

e pensamos:

"Certo, falhou."

Não.

A investigação ainda não terminou.

Precisamos verificar o estado persistente.


💳 CAPÍTULO 12 — O demônio da duplicidade

Nosso BELLACARD recebe:

COMPRA = R$ 100

O cliente chama a API.

Timeout.

Não sabe se a compra aconteceu.

Tenta novamente.

Agora imagine:

REQUEST #1 → R$100
REQUEST #2 → R$100

Resultado:

R$200

Temos um problema gigantesco.

Em sistemas financeiros, retries, idempotência e mecanismos de deduplicação precisam ser considerados seriamente.

O teste não deveria perguntar somente:

A compra funcionou?

Mas:

O que acontece se a mesma operação chegar novamente?

Em determinadas APIs podemos encontrar mecanismos como:

Idempotency-Key: 8d937...

permitindo reconhecer repetições da mesma intenção.

A pergunta Constantine seria:

Quantas vezes o ritual foi executado quando o cliente jurava tê-lo invocado uma vez?


🪜 CAPÍTULO 13 — A escada dos ambientes

Agora chegamos à nova peça do quebra-cabeça:

LOCAL
  ↓
 QA
  ↓
STAGE
  ↓
PRODUCTION-LIKE

Em organizações reais podemos encontrar inúmeras variações:

DEV
INTEGRATION
QA
SIT
UAT
STAGING
PRE-PROD
PROD

Os nomes são menos importantes que o princípio.

Queremos promover software por ambientes controlados até chegarmos à produção.

No mundo mainframe isso não deveria causar nenhum choque.

Temos há décadas coisas como:

CICSD
CICST
CICSQ
CICSP

ou:

DB2D
DB2T
DB2Q
DB2P

e datasets:

BELLACOSA.DEV.CLIENTES
BELLACOSA.QA.CLIENTES
BELLACOSA.PROD.CLIENTES

DevOps não inventou a separação de ambientes.

Só mudou boa parte do vocabulário e das ferramentas.


🧙 CAPÍTULO 14 — BASE_URL: o DDNAME das APIs

Podemos ter:

BASE_URL=https://qa.api.example.com
TOKEN=***
USER_ID=42

e o teste:

await request.get(
    `${BASE_URL}/users/${USER_ID}`
);

Em Stage:

BASE_URL=https://stage.api.example.com

Em QA:

BASE_URL=https://qa.api.example.com

O teste permanece essencialmente igual.

Isso deveria soar familiar ao programador COBOL.

Em vez de amarrar recursos diretamente ao programa, usamos mecanismos externos de configuração.

No JCL:

//CLIENTE DD DSN=BELLACOSA.QA.CLIENTES,DISP=SHR

e posteriormente:

//CLIENTE DD DSN=BELLACOSA.PROD.CLIENTES,DISP=SHR

O programa não precisa ser recompilado porque mudamos de dataset.

O mesmo princípio reaparece no mundo moderno:

separe código de configuração.


☠️ CAPÍTULO 15 — .env não é um círculo mágico

Um arquivo:

.env.qa

pode conter configuração de QA.

Ótimo.

Mas cuidado:

.env ≠ Secret Vault

Colocar:

PASSWORD=123456
TOKEN=abc123

fora do source principal não torna magicamente esses valores seguros.

Segredos exigem tratamento apropriado durante armazenamento, distribuição, utilização, logging e rotação. A OWASP recomenda evitar segredos hardcoded, minimizar sua exposição e usar soluções adequadas de gestão de segredos conforme a arquitetura.

E jamais faça:

console.log(token);

e depois mande o log inteiro para:

CI REPORT
LOG SERVER
EMAIL
SLACK
ARQUIVO

Você acabou de transformar observabilidade em distribuição de credenciais.


👥 CAPÍTULO 16 — O fantasma chamado USER_ID=42

Suponha que todos os testes utilizem:

USER_ID=42

Pipeline A:

altera usuário 42

Pipeline B:

remove usuário 42

Pipeline C:

consulta usuário 42

Agora temos:

A ──┐
B ──┼── USER 42
C ──┘

Resultado?

Caos.

Um teste passa.

Outro falha.

Executamos novamente.

Passa.

Executamos amanhã.

Falha.

Nasceu o:

👻 FLAKY TEST

E então surge a frase mais perigosa do departamento de QA:

"Roda de novo."

Quando a segunda execução passa, todos vão embora.

Pouco a pouco ninguém acredita mais na suíte automatizada.

Isso destrói o valor do CI/CD.


🧬 CAPÍTULO 17 — Dê uma identidade própria para cada criatura

Uma alternativa é gerar dados únicos:

email = bellacosa+timestamp@example.test

ou utilizar:

UUID

Assim:

TEST A → USER A
TEST B → USER B
TEST C → USER C

Em vez de:

TEST A ─┐
TEST B ─┼── USER 42
TEST C ─┘

Isso aumenta isolamento e reduz colisões.

Mas abre outra porta.


🧹 CAPÍTULO 18 — Quem limpa o pentagrama depois?

Imagine 10.000 execuções criando:

USERS
CARTS
ORDERS
PAYMENTS
SESSIONS

Depois de meses:

TEST-0000001
TEST-0000002
TEST-0000003
...
TEST-9827719

QA virou um cemitério.

Por isso:

SETUP
  ↓
CREATE DATA
  ↓
EXECUTE
  ↓
ASSERT
  ↓
CLEANUP

E há um detalhe crítico:

FAIL
 ↓
CLEANUP

também.

Se a limpeza ocorrer somente quando o teste termina normalmente, justamente os testes problemáticos deixarão mais sujeira.

Constantine jamais abandonaria o pentagrama aberto no chão.

O QA também não deveria abandonar seus registros temporários.


🕵️ CAPÍTULO 19 — Nunca use o cadáver verdadeiro

A regra:

Never use real customer data.

merece atenção especial.

Copiar produção indiscriminadamente:

PROD DATABASE
      ↓
     COPY
      ↓
 QA DATABASE

pode transportar:

nomes
documentos
endereços
telefones
emails
informações financeiras
dados pessoais

para um ambiente que talvez possua controles diferentes de produção.

Dependendo da necessidade, estratégias podem envolver dados sintéticos, mascaramento, anonimização ou tokenização, considerando requisitos técnicos e regulatórios aplicáveis.

E há outro teste maravilhoso:

compare aquilo que a interface mostra com aquilo que a API realmente devolve.

A tela mostra:

Nome: João
Cidade: Itatiba

Mas o JSON devolve:

{
  "name": "João",
  "city": "Itatiba",
  "internalSalary": 12345,
  "cpf": "...",
  "internalRiskScore": 972,
  "passwordResetToken": "..."
}

A interface simplesmente ignorou os campos extras.

O atacante não ignora.

A OWASP recomenda examinar respostas brutas procurando informações sensíveis ou dados desnecessariamente expostos.


🔥 CAPÍTULO 20 — Não teste somente o Response

Chegamos à maior armadilha de todas.

O teste faz:

POST /orders

Recebe:

201 Created

e declara:

PASS

Constantine pergunta:

— Cadê o pedido?

Verificamos.

Existe.

— E o total?

Errado.

— E o estoque?

Diminuiu duas vezes.

— E o pagamento?

Foi criado três vezes.

— E o MQ?

Duas mensagens.

— E o banco?

COMMIT realizado.

O nosso glorioso:

201 CREATED

acabou de virar:

FAIL

Por isso um teste robusto pode precisar validar:

STATUS
HEADERS
BODY
SCHEMA
BUSINESS RULE
SIDE EFFECT
PERSISTENCE
SECURITY
PERFORMANCE

Estamos deixando de testar HTTP.

Estamos testando o sistema através do HTTP.

Essa diferença é gigantesca.


⏱️ CAPÍTULO 21 — Performance e o demônio da média

Imagine:

média = 200 ms

Parece excelente.

Só que:

90 usuários → 100 ms
10 usuários → 1.100 ms

A média esconde sofrimento.

Por isso aparecem medidas como:

p50
p95
p99

Se p95 = 800 ms, aproximadamente 95% das observações ficaram nesse valor ou abaixo, segundo a metodologia empregada.

Isso nos ajuda a enxergar a cauda.

Também podemos observar:

throughput
timeouts
error rate
CPU
memory
disk I/O
database locks
connection pools
queues
threads
retries

E retries merecem carinho especial.

Um servidor começa a ficar lento.

Clientes repetem chamadas.

O servidor recebe ainda mais trabalho.

Fica mais lento.

Clientes repetem ainda mais.

Temos:

SLOW
 ↓
RETRY
 ↓
MORE LOAD
 ↓
SLOWER
 ↓
MORE RETRIES
 ↓
☠️

O inferno distribuído encontrou seu próprio mecanismo de escala.


🤖 CAPÍTULO 22 — Automatizando o exorcismo

Podemos começar com algo pequeno em Playwright:

test('create user', async ({ request }) => {

  const response = await request.post('/users', {
    data: {
      name: 'Asha'
    }
  });

  expect(response.status()).toBe(201);

  const body = await response.json();

  expect(body.name).toBe('Asha');
});

Já é melhor do que testar manualmente toda vez.

Mas podemos evoluir:

status
+
headers
+
schema
+
body
+
business rules
+
authorization
+
side effects
+
cleanup

E executar automaticamente:

COMMIT
  ↓
BUILD
  ↓
UNIT TEST
  ↓
DEPLOY QA
  ↓
API TEST
  ↓
CONTRACT TEST
  ↓
AUTHORIZATION TEST
  ↓
INTEGRATION TEST
  ↓
SECURITY TEST
  ↓
REPORT
  ↓
QUALITY GATE

A OWASP também descreve a automação de testes de autorização como útil para detectar regressões durante o desenvolvimento e os releases.


🖥️ CAPÍTULO 23 — Constantine entra na LPAR

Agora traduzimos tudo para a língua ancestral.

APIMainframe — analogia conceitual
Requestentrada da transação
JSONestrutura de dados
Endpointponto de entrada
HTTP Methodoperação solicitada
HTTP Statusretorno técnico
Tokencontexto de segurança
AuthorizationRACF/controle de acesso conceitualmente
Request IDcorrelation ID
Side Effectresultado transacional
DatabaseDb2/IMS/VSAM
QueueMQ
Rollbackdesfazer LUW
Environmentregião/subsistema/configuração
BASE_URLconfiguração externa/endereço do destino
Test Datamassa de testes

Não são equivalências tecnológicas perfeitas.

São pontes mentais.

E elas mostram uma coisa fascinante:

muito daquilo que o mercado chama hoje de arquitetura moderna possui problemas que mainframeiros conhecem há décadas.

Estado.

Concorrência.

Segurança.

Transações.

Auditoria.

Ambientes.

Configuração.

Recuperação.

Integridade.


🕯️ CAPÍTULO 24 — O exorcismo final

Constantine está diante do monitor.

O teste apresenta:

HTTP/1.1 200 OK

Todo mundo comemora.

Ele não.

Primeira pergunta:

O usuário correto recebeu os dados?

Sim.

O schema está correto?

Sim.

A autorização foi verificada?

Sim.

Existe informação sensível extra?

Não.

O banco ficou consistente?

Sim.

A operação aconteceu uma única vez?

Sim.

As mensagens corretas foram produzidas?

Sim.

O tempo ficou dentro do objetivo definido?

Sim.

O teste utilizou dados isolados?

Sim.

O ambiente era realmente QA?

Sim.

Os segredos ficaram protegidos?

Sim.

A massa criada foi limpa?

Sim.

Agora Constantine olha novamente:

200 OK

E finalmente diz:

Agora talvez esteja OK.


☕ EPÍLOGO — CC 0000 também pode mentir

Aqui está a lição que une o velho mainframe às APIs modernas.

Nós já aprendemos há décadas que:

CC 0000

não significa necessariamente:

NEGÓCIO CORRETO

Um batch COBOL pode terminar lindamente com:

MAXCC=0000

e ter atualizado 50.000 clientes com a tarifa errada.

Tecnicamente terminou.

Funcionalmente foi uma catástrofe.

Da mesma maneira:

HTTP 200 OK

não significa:

TEST PASSED

Nosso verdadeiro ritual é:

REQUEST VÁLIDO
       +
CONTRATO CORRETO
       +
AUTENTICAÇÃO
       +
AUTORIZAÇÃO
       +
DADOS CORRETOS
       +
REGRA DE NEGÓCIO
       +
SIDE EFFECT CORRETO
       +
ESTADO CONSISTENTE
       +
ISOLAMENTO
       +
SEGURANÇA
       +
PERFORMANCE
       +
CLEANUP
       =
       PASS

E existe ainda uma última possibilidade, aquela que faria Constantine sorrir.

O sistema está correto.

O endpoint está correto.

O banco está correto.

A autorização está correta.

O COMMIT está correto.

Mas o pipeline acusa:

FAILED

Porque dois testes resolveram utilizar simultaneamente:

USER_ID=42

Nesse momento o verdadeiro monstro não está na API.

Está na suíte de testes.

E é por isso que o bom investigador nunca pergunta apenas:

“A API falhou?”

Ele pergunta:

“O que exatamente falhou: o request, o contrato, a autenticação, a autorização, a aplicação, a regra de negócio, a persistência, o ambiente, a configuração, a massa de dados... ou o próprio teste?”

Essa é a diferença entre apertar SEND no Postman e realmente compreender API Testing.

Às 03:17, quando o dashboard fica vermelho e alguém aparece no War Room dizendo “mas estava retornando 200!”, talvez seja tarde demais para chamar John Constantine.

Mas ainda dá tempo de chamar um velho programador COBOL.

Ele provavelmente olhará para aquele 200 OK, lembrará de todos os CC 0000 que já viu mentirem durante a carreira e fará a única pergunta que realmente importa:

— Certo. Mas o que foi gravado no banco? ☕🕯️



Vagner Renato Bellacosa, IBM Champion e especialista em IBM Mainframe
IBM Z17 SYSTEM ONLINE
Sobre o autor

Vagner Renato Bellacosa

IBM Champion 2026 • Especialista em IBM Mainframe

Vagner Renato Bellacosa trabalha com IBM Mainframe desde 1988 , é IBM Champion e especialista em COBOL, CICS, Db2, z/OS e IBM Z. Compartilha experiências profissionais, conhecimento técnico, história da computação e práticas do universo mainframe para aproximar novas gerações das tecnologias que sustentam empresas, bancos e governos ao redor do mundo.

IBM Champion IBM Z COBOL CICS Db2 z/OS Mainframe desde 1988
GitHub LinkedIn
Inicializando conteúdo...