use futures::future::BoxFuture;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::coding::error::WebFetchError;
use crate::coding::tools::web_fetch::{HttpClient, HttpResponse};
#[derive(Debug, Clone)]
pub struct FakeHttpClient {
responses: Arc<Mutex<HashMap<String, FakeResponse>>>,
fetch_history: Arc<Mutex<Vec<String>>>,
default_response: Option<HttpResponse>,
}
impl Default for FakeHttpClient {
fn default() -> Self {
Self::new()
}
}
impl FakeHttpClient {
pub fn new() -> Self {
Self {
responses: Arc::new(Mutex::new(HashMap::new())),
fetch_history: Arc::new(Mutex::new(Vec::new())),
default_response: None,
}
}
pub fn with_response(self, url: &str, response: HttpResponse) -> Self {
self.responses.lock().unwrap().insert(url.to_string(), FakeResponse::Ok(response));
self
}
pub fn with_html(self, url: &str, html: &str) -> Self {
self.with_response(
url,
HttpResponse {
final_url: url.to_string(),
status_code: 200,
body: html.to_string(),
content_type: Some("text/html".to_string()),
},
)
}
pub fn with_timeout(self, url: &str, timeout_ms: u64) -> Self {
self.responses.lock().unwrap().insert(url.to_string(), FakeResponse::Timeout(timeout_ms));
self
}
pub fn with_request_failed(self, url: &str, message: &str) -> Self {
self.responses.lock().unwrap().insert(url.to_string(), FakeResponse::RequestFailed(message.to_string()));
self
}
pub fn with_default(mut self, response: HttpResponse) -> Self {
self.default_response = Some(response);
self
}
pub fn fetch_count(&self) -> usize {
self.fetch_history.lock().unwrap().len()
}
pub fn fetched_urls(&self) -> Vec<String> {
self.fetch_history.lock().unwrap().clone()
}
}
impl HttpClient for FakeHttpClient {
fn fetch<'a>(&'a self, url: &'a str, _timeout: Duration) -> BoxFuture<'a, Result<HttpResponse, WebFetchError>> {
self.fetch_history.lock().unwrap().push(url.to_string());
let responses = self.responses.lock().unwrap();
Box::pin(std::future::ready(match responses.get(url) {
Some(response) => response.clone().into_result(),
None => self
.default_response
.clone()
.ok_or_else(|| WebFetchError::RequestFailed(format!("No fake response configured for URL: {url}"))),
}))
}
}
#[derive(Debug, Clone)]
enum FakeResponse {
Ok(HttpResponse),
Timeout(u64),
RequestFailed(String),
}
impl FakeResponse {
fn into_result(self) -> Result<HttpResponse, WebFetchError> {
match self {
FakeResponse::Ok(response) => Ok(response),
FakeResponse::Timeout(timeout_ms) => Err(WebFetchError::Timeout(timeout_ms)),
FakeResponse::RequestFailed(message) => Err(WebFetchError::RequestFailed(message)),
}
}
}