Suíte de conformidade¶
Para: Dev — Conferido em 2026-10-18.
A suíte de conformidade é o teste de aceite do contrato da API v1. É um programa em Go que fala só HTTP com a sua operação Teste e traz um operador de referência para receber os webhooks. Verde é pré-requisito do go-live.
O que é¶
- É caixa-preta: usa a superfície pública (
/v1/*e/v1/sandbox/*) e um listener local para os webhooks. Não lê banco nem código do servidor. - Traz um operador de referência que recebe o webhook, verifica a assinatura, deduplica, faz o fetch-back e registra o crédito num livro próprio, só na memória da suíte.
- Ela testa o Dracma, usando o operador de referência como contraparte. Ela não testa o seu conector. Para provar o seu lado, rode as Receitas por caminho contra o seu receptor e o seu livro.
- Um caso que precisa de uma configuração que você não passou falha com "configuração ausente"; ele nunca é pulado como verde.
- A suíte é entregue pelo Dracma no onboarding, junto com as credenciais da operação Teste.
Quando usar¶
- Uma vez no onboarding, antes de pedir a operação Real, e guardando a saída como prova do go-live (Checklist de go-live).
- De novo quando o Dracma publicar uma versão nova do contrato ou do
openapi.yaml(Changelog). - Como teste de regressão do seu ambiente: relógio, rede até o Dracma e URL de webhook alcançável.
Como funciona¶
O que ela prova¶
Casos obrigatórios do contrato (PIX). Cada um já foi visto falhar quando a regra correspondente é quebrada.
| # | O que prova | Caso |
|---|---|---|
| 1 | Assinatura inválida, timestamp fora da janela e nonce repetido são recusados (401 com o code certo) e nada é criado |
TestInvalidSignatureRejected, TestTimestampOutOfWindowRejected, TestReplayedNonceRejected, TestT1UnknownKeyAndMissingHeaders |
| 2 | Mesma chave e mesmo corpo devolvem a mesma resposta com Idempotent-Replayed: true; corpo diferente dá 409 idempotency_key_reused |
TestIdempotencySameResponse, TestIdempotencyDifferentBody409 |
| 3 | Depósito, pay, webhook deposit.paid assinado e fetch-back coerente geram exatamente 1 crédito |
TestDepositPaidWebhookAndFetchBack, TestE2EP001DepositPaid |
| 4 | Webhook reentregue com o mesmo Dracma-Event-Id responde 200 sem efeito |
TestWebhookRedeliveredSameEventID |
| 5 | expired é terminal: pagar depois não vira paid e cancelar dá 409 |
TestDepositExpiration |
| 6 | Saque dentro do limite chega a approved (automático) e a paid |
TestWithdrawalWithinMultiplierPaid, TestE2EP002WithdrawalPaid, TestSandboxWithdrawalPayExplicit |
| 7 | Saque acima do limite vai a under_review com AUTO_MULTIPLIER_EXCEEDED |
TestWithdrawalAboveMultiplierUnderReview |
| 8 | review_requested: true nunca é automático |
TestReviewRequestedNeverAutomatic |
| 9 | Objeto de outra operação responde 404 em todas as rotas |
TestCrossOperationObject404, TestT4BOLA, TestDoD6CrossTenantEveryRoute |
| 10 | /sandbox/outage dá 503 repetível, e a retomada com a mesma chave não duplica |
TestSandboxOutageRetrySameKey |
Casos adicionais:
| O que prova | Caso |
|---|---|
Webhook forjado, com corpo alterado, timestamp velho ou t= divergente não credita; nem um deposit.paid válido para um depósito que a API diz pending |
TestT3ForgedWebhooksNoCredit |
Valor divergente vai a under_review com AMOUNT_MISMATCH e não emite deposit.paid |
TestE2EP001AmountMismatchUnderReview |
| Chave PIX de e-mail passa pela consulta de titularidade do simulador | TestE2EC123DictEmailKey |
Máquina de estados: transição inválida dá 409 invalid_state_transition e não muda o estado |
TestT14StateMachine |
Campo proibido no corpo (operation_id, status, decision, version, paid_amount) é recusado ou ignorado, nunca aplicado |
TestT9MassAssignment |
Corpos hostis (SQL, número em vez de texto, notação científica, UTF-8 inválido, corpo acima de 64 KiB) dão 400 ou 413, nunca 5xx |
TestT13InjectionAndCoercion |
A chave de integração não muda regras, webhook, chave nem IP; um pedido de regra fica pending_approval e não vigora |
TestT8OperatorCannotChangeControlPlane |
| 50 saques simultâneos com a mesma chave criam 1 objeto; dois saques simultâneos não passam do limite automático | TestT5ConcurrentIdempotentWithdrawals, TestT6DoubleWithdrawalRace |
| Digest e totais da reconciliação batem com o recalculado de forma independente | TestC253ReconciliationDigestIndependent |
Casos de segurança que precisam de configuração extra (flags da próxima seção): chave de Real contra Teste e o inverso, rotas /sandbox/* em Real, revogação de chave, segredo ausente de log e resposta, rastreio por trace_id, limites de taxa e cadastro de URL de webhook.
Caso de IP fora da lista
O caso TestT17IPAllowlist espera 403 ip_not_allowed para um IP fora da lista e só faz sentido se a operação de teste tiver ao menos um IP liberado. Sem IP liberado a operação aceita qualquer origem, então não passe -blocked-ip-key-id. Veja IPs do conector.
Flags¶
| Flag | Conteúdo |
|---|---|
-dracma-url |
Base da API da operação Teste, incluindo /v1 |
-key-id e -key-secret-file |
Chave key_test_ da operação e o arquivo com o segredo. Nunca passe o segredo na linha de comando |
-webhook-secret-file |
Segredos de webhook ativos, um por linha (1 ou 2 durante a rotação) |
-webhook-listen |
Endereço do listener local do operador de referência (padrão: o endereço local da máquina, em uma porta livre) |
-webhook-url |
URL cadastrada na operação que chega nesse listener (padrão http://<listen>/webhook) |
-currency |
Moeda da carteira (padrão BRL) |
-wait e -webhook-wait |
Espera máxima por uma transição (padrão 30 s) e por um webhook (padrão 90 s) |
-quiesce-timeout |
Quanto esperar, antes dos casos, que os limites de taxa de uma rodada anterior liberem (padrão 5 min) |
-key2-id e -key2-secret-file |
Chave de uma operação de outro tenant, para os casos de isolamento |
-live-url, -live-key-id, -live-key-secret-file |
Operação Real, para a recusa cruzada de chaves e o 404 das rotas de sandbox |
-blocked-ip-key-id, -blocked-ip-key-secret-file |
Chave cuja lista de IPs não inclui o IP da suíte (não se aplica hoje, veja o aviso acima) |
-revoke-key-id, -revoke-key-secret-file, -revoke-cmd, -revoke-deadline |
Revogação de chave, com o comando de revogação e o prazo (padrão 5 min) |
-log-file, -live-log-file |
Log JSON do servidor, para os casos de vazamento de segredo e rastreio |
-recon-date |
Dia UTC já fechado para o caso de reconciliação (padrão: o dia fechado mais recente) |
-openapi |
openapi.yaml de onde as rotas do caso de isolamento são enumeradas |
Os arquivos de segredo ficam fora do repositório, com permissão 0400, entregues pelo seu cofre.
Exemplos¶
Execução do conjunto base, que só precisa da sua operação Teste e do seu receptor:
cd conformance
go test ./conformance -count=1 -v \
-run 'TestInvalidSignature|TestTimestampOutOfWindow|TestReplayedNonce|TestT1UnknownKey|TestIdempotency|TestDeposit|TestWebhookRedelivered|TestWithdrawal|TestReviewRequested|TestSandbox|TestE2E|TestT3|TestT9|TestT13|TestT14' \
-dracma-url="$DRACMA_BASE_URL" \
-key-id=key_test_SUAOPERACAO \
-key-secret-file=/caminho/seguro/segredo-chamada \
-webhook-secret-file=/caminho/seguro/segredos-webhook \
-webhook-listen=localhost:0 \
-webhook-url=https://pagamentos.operador.example/dracma/webhook \
-currency=BRL
- O listener do operador de referência precisa ser alcançável pelo Dracma na
-webhook-urlcadastrada na operação. Se o Dracma não consegue chegar nele, os casos de webhook falham por tempo esgotado em-webhook-wait. - Para a execução completa, acrescente as flags opcionais e tire o
-run. A execução completa inclui o caso de limites de taxa, que consome os baldes da chave e do IP e roda por último; ele pode deixar o IP da suíte limitado por alguns minutos. Rodadas seguidas esperam esse bloqueio passar antes de começar. - Há ainda os testes
TestOffline*, que não precisam de servidor: eles conferem vetores de assinatura, o verificador e o operador de referência.
Erros e casos de borda¶
| Sintoma | Causa provável | O que fazer |
|---|---|---|
configuração ausente: -key2-id |
O caso exige uma segunda operação ou a operação Real, que você não passou | Rode com -run no conjunto base, ou passe as flags do caso |
| Casos de saque caem em revisão em vez de automático | As regras da operação foram mudadas (janela, horário, multiplicador) | Os casos de saque assumem as regras iniciais; restaure-as ou peça a restauração ao suporte |
| Timeout esperando webhook | A URL cadastrada não chega ao listener local, ou o relógio está desviado | Confira a URL, o firewall e o NTP |
429 rate_limited em sequência |
Duas rodadas seguidas, ou o caso de limites de taxa rodou antes | Espere ou aumente -quiesce-timeout |
Falhas só em segurança com -live-* |
A chave de Real aponta para o host errado | Confira o host de Real e o prefixo key_live_ |
Operação Real
Passe -live-* somente com uma chave de Real quando o Dracma pedir. Os casos que usam essa chave só tentam requisições recusadas, mas a chave é de dinheiro real: guarde-a como qualquer outra credencial de produção.
Veja também¶
- Sandbox e Receitas por caminho.
- Checklist de go-live: a suíte verde é um dos itens.
- Autenticação: a ordem de verificação que os casos de assinatura exercitam.
- Exemplos executáveis: os vetores de assinatura que a suíte também confere.