mod common;
use std::time::Duration;
use apibrasil::ErrorKind;
use common::{build_api, http_error};
use serde_json::json;
async fn erro_para(status: u16, body: serde_json::Value) -> apibrasil::Error {
let (api, transport) = build_api();
transport.respond_with(http_error(status, body, &[]));
api.consulta()
.cpf(json!({ "cpf": "00000000000" }))
.await
.expect_err("a chamada deveria falhar")
}
#[tokio::test]
async fn status_viram_categorias_de_erro() {
let casos = [
(400, ErrorKind::Validation),
(401, ErrorKind::Authentication),
(402, ErrorKind::InsufficientBalance),
(403, ErrorKind::Permission),
(404, ErrorKind::NotFound),
(410, ErrorKind::NotFound),
(418, ErrorKind::Api),
(422, ErrorKind::Validation),
(429, ErrorKind::RateLimit),
(500, ErrorKind::Server),
(503, ErrorKind::Server),
];
for (status, esperado) in casos {
let error = erro_para(status, json!({ "message": "falhou" })).await;
assert_eq!(error.kind(), esperado, "status {status}");
assert_eq!(error.status, Some(status));
}
}
#[tokio::test]
async fn erro_preserva_mensagem_codigo_e_corpo() {
let error = erro_para(404, json!({ "message": "sem dados", "code": "NOT_FOUND" })).await;
assert_eq!(error.message, "sem dados");
assert_eq!(error.code.as_deref(), Some("NOT_FOUND"));
assert!(error.is_not_found());
assert_eq!(error.response.as_ref().unwrap()["code"], json!("NOT_FOUND"));
}
#[tokio::test]
async fn erro_usa_o_campo_error_como_mensagem() {
let error = erro_para(400, json!({ "error": "payload inválido" })).await;
assert_eq!(error.message, "payload inválido");
}
#[tokio::test]
async fn erro_sem_mensagem_descreve_o_status() {
let error = erro_para(500, json!({})).await;
assert_eq!(error.message, "A API respondeu com HTTP 500.");
}
#[tokio::test]
async fn display_do_erro_traz_status_e_codigo() {
let error = erro_para(402, json!({ "message": "sem saldo", "code": "NO_BALANCE" })).await;
assert_eq!(error.to_string(), "sem saldo (HTTP 402) [NO_BALANCE]");
assert!(error.is_insufficient_balance());
}
#[tokio::test]
async fn rate_limit_le_o_retry_after_em_segundos() {
let (api, transport) = build_api();
transport.respond_with(http_error(
429,
json!({ "message": "devagar" }),
&[("Retry-After", "12")],
));
let error = api.consulta().cpf(None).await.unwrap_err();
assert!(error.is_rate_limit());
assert_eq!(error.retry_after, Some(Duration::from_secs(12)));
}
#[tokio::test]
async fn rate_limit_le_o_retry_after_como_data_http() {
let (api, transport) = build_api();
transport.respond_with(http_error(
429,
json!({}),
&[("retry-after", "Wed, 21 Oct 2015 07:28:00 GMT")],
));
let error = api.consulta().cpf(None).await.unwrap_err();
assert_eq!(error.retry_after, None);
}
#[tokio::test]
async fn falha_de_rede_vira_erro_de_rede() {
let (api, transport) = build_api();
transport.fail_with(apibrasil::Error::network("conexão recusada"));
let error = api.consulta().cpf(None).await.unwrap_err();
assert_eq!(error.kind(), ErrorKind::Network);
assert!(error.is_network());
assert!(!error.is_timeout());
assert_eq!(error.status, None);
}
#[tokio::test]
async fn timeout_conta_como_falha_de_rede_mas_e_distinguivel() {
let (api, transport) = build_api();
transport.fail_with(apibrasil::Error::timeout("tempo limite"));
let error = api.consulta().cpf(None).await.unwrap_err();
assert_eq!(error.kind(), ErrorKind::Timeout);
assert!(error.is_network());
assert!(error.is_timeout());
}
#[tokio::test]
async fn erro_de_decodificacao_e_de_validacao() {
let (api, transport) = build_api();
transport.respond_with(common::ok(json!({ "response": "texto" })));
#[derive(Debug, serde::Deserialize)]
struct Esperado {
#[allow(dead_code)]
id: u32,
}
let response = api.whatsapp().send_text(None).await.unwrap();
let error = response.decode::<Esperado>().unwrap_err();
assert!(error.is_validation());
}