use reqwest::Method;
use serde::Serialize;
use crate::client::Client;
use crate::constants::PATH_PAYMENT_SIMULATION;
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct SimulationService {
client: Client,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PayRequest {
pub order_id: String,
pub amount: i64,
}
#[derive(Debug, Serialize)]
struct RequestBody<'a> {
project: &'a str,
order_id: &'a str,
amount: i64,
api_key: &'a str,
}
impl SimulationService {
pub fn new(client: Client) -> Self {
Self { client }
}
pub async fn pay(&self, request: &PayRequest) -> Result<()> {
if request.order_id.is_empty() {
return Err(Error::invalid_order_id(self.client.language()));
}
if request.amount <= 0 {
return Err(Error::invalid_amount(self.client.language()));
}
let body = serde_json::to_vec(&RequestBody {
project: self.client.project(),
order_id: &request.order_id,
amount: request.amount,
api_key: self.client.api_key(),
})
.map_err(|err| Error::encode_json(self.client.language(), err))?;
self.client
.do_request(Method::POST, PATH_PAYMENT_SIMULATION, Some(body))
.await
.map(|_| ())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Language;
fn dummy_client(language: Language) -> Client {
Client::builder("project", "key")
.base_url("http://127.0.0.1:1")
.retries(0)
.language(language)
.build()
}
#[tokio::test]
async fn pay_rejects_empty_order_id_with_localized_message() {
let service = SimulationService::new(dummy_client(Language::English));
let err = service
.pay(&PayRequest {
order_id: String::new(),
amount: 1,
})
.await
.unwrap_err();
assert!(matches!(err, Error::InvalidOrderId { .. }));
assert_eq!(err.to_string(), "order ID is required");
let service = SimulationService::new(dummy_client(Language::Indonesian));
let err = service
.pay(&PayRequest {
order_id: String::new(),
amount: 1,
})
.await
.unwrap_err();
assert_eq!(err.to_string(), "ID pesanan wajib diisi");
}
#[tokio::test]
async fn pay_rejects_non_positive_amount() {
let service = SimulationService::new(dummy_client(Language::English));
for amount in [0_i64, -1, i64::MIN] {
let err = service
.pay(&PayRequest {
order_id: "x".into(),
amount,
})
.await
.unwrap_err();
assert!(
matches!(err, Error::InvalidAmount { .. }),
"amount={amount}"
);
}
}
#[test]
fn simulation_service_is_clone() {
let service = SimulationService::new(dummy_client(Language::English));
let _ = service.clone();
}
}