#![allow(dead_code)]
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use apibrasil::core::transport::{BoxFuture, Transport, TransportRequest, TransportResponse};
use apibrasil::{ApiBrasil, Config, Error, Json, Method, ResponseData, Result, RetryConfig};
use serde_json::{json, Value};
pub const BASE_URL: &str = "https://gateway.apibrasil.io/api/v2";
enum Programmed {
Response(TransportResponse),
Failure(Error),
}
#[derive(Default)]
struct State {
calls: Vec<TransportRequest>,
queue: Vec<Programmed>,
fallback: Option<TransportResponse>,
}
#[derive(Clone, Default)]
pub struct FakeTransport {
state: Arc<Mutex<State>>,
}
impl FakeTransport {
pub fn new() -> Self {
let transport = Self::default();
transport.state.lock().unwrap().fallback = Some(ok(json!({ "ok": true })));
transport
}
pub fn respond_with(&self, response: TransportResponse) -> &Self {
self.state
.lock()
.unwrap()
.queue
.push(Programmed::Response(response));
self
}
pub fn fail_with(&self, error: Error) -> &Self {
self.state
.lock()
.unwrap()
.queue
.push(Programmed::Failure(error));
self
}
pub fn set_fallback(&self, response: TransportResponse) -> &Self {
self.state.lock().unwrap().fallback = Some(response);
self
}
pub fn last(&self) -> TransportRequest {
self.state
.lock()
.unwrap()
.calls
.last()
.cloned()
.expect("nenhuma requisição foi feita")
}
pub fn last_body(&self) -> Option<Json> {
self.last().json_body()
}
pub fn last_header(&self, name: &str) -> Option<String> {
let request = self.last();
request
.headers
.iter()
.find(|(key, _)| key.eq_ignore_ascii_case(name))
.map(|(_, value)| value.clone())
}
pub fn calls(&self) -> Vec<TransportRequest> {
self.state.lock().unwrap().calls.clone()
}
pub fn count(&self) -> usize {
self.state.lock().unwrap().calls.len()
}
}
impl Transport for FakeTransport {
fn execute<'a>(
&'a self,
request: TransportRequest,
) -> BoxFuture<'a, Result<TransportResponse>> {
let next = {
let mut state = self.state.lock().unwrap();
state.calls.push(request);
if state.queue.is_empty() {
Programmed::Response(
state
.fallback
.clone()
.unwrap_or_else(|| ok(json!({ "ok": true }))),
)
} else {
state.queue.remove(0)
}
};
Box::pin(async move {
match next {
Programmed::Response(response) => Ok(response),
Programmed::Failure(error) => Err(error),
}
})
}
}
pub fn ok(data: Value) -> TransportResponse {
TransportResponse::json(200, data)
}
pub fn http_error(status: u16, data: Value, headers: &[(&str, &str)]) -> TransportResponse {
let mut response = TransportResponse::json(status, data);
response.headers = headers
.iter()
.map(|(name, value)| (name.to_ascii_lowercase(), value.to_string()))
.collect();
response
}
pub fn raw_bytes(status: u16, body: &[u8]) -> TransportResponse {
TransportResponse {
status,
headers: HashMap::new(),
data: ResponseData::Bytes(body.to_vec()),
body: body.to_vec(),
}
}
pub fn build_api() -> (ApiBrasil, FakeTransport) {
build_api_with(Config::default())
}
pub fn build_api_anonymous() -> (ApiBrasil, FakeTransport) {
build_api_with(Config {
bearer_token: Some(String::new()),
device_token: Some(String::new()),
..Default::default()
})
}
pub fn build_api_with(overrides: Config) -> (ApiBrasil, FakeTransport) {
let transport = FakeTransport::new();
let config = Config {
transport: Some(Arc::new(transport.clone())),
retry: Some(RetryConfig::none()),
base_url: Some(BASE_URL.to_string()),
bearer_token: Some("jwt".to_string()),
device_token: Some("dev".to_string()),
..Default::default()
}
.merge(overrides);
(ApiBrasil::new(config), transport)
}
#[track_caller]
pub fn assert_request(transport: &FakeTransport, method: Method, path: &str, body: Option<Value>) {
let request = transport.last();
assert_eq!(request.method, method, "método da requisição");
assert_eq!(
request.url,
format!("{BASE_URL}{path}"),
"url da requisição"
);
if let Some(expected) = body {
assert_eq!(transport.last_body(), Some(expected), "body da requisição");
}
}