☕ 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 John Constantine. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta John Constantine. Mostrar todas as mensagens

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...