mcp_servers/testing/
web_fetch.rs1use futures::future::BoxFuture;
2use std::collections::HashMap;
3use std::sync::{Arc, Mutex};
4use std::time::Duration;
5
6use crate::coding::error::WebFetchError;
7use crate::coding::tools::web_fetch::{HttpClient, HttpResponse};
8
9#[derive(Debug, Clone, Default)]
14pub struct FakeHttpClient {
15 responses: Arc<Mutex<HashMap<String, Result<HttpResponse, WebFetchError>>>>,
16}
17
18impl FakeHttpClient {
19 pub fn with_response(self, url: &str, response: HttpResponse) -> Self {
21 self.with_result(url, Ok(response))
22 }
23
24 pub fn with_html(self, url: &str, html: &str) -> Self {
26 self.with_response(
27 url,
28 HttpResponse {
29 final_url: url.to_string(),
30 status_code: 200,
31 body: html.to_string(),
32 content_type: Some("text/html".to_string()),
33 },
34 )
35 }
36
37 pub fn with_timeout(self, url: &str, timeout_ms: u64) -> Self {
39 self.with_result(url, Err(WebFetchError::Timeout(timeout_ms)))
40 }
41
42 pub fn with_request_failed(self, url: &str, message: &str) -> Self {
44 self.with_result(url, Err(WebFetchError::RequestFailed(message.to_string())))
45 }
46
47 fn with_result(self, url: &str, result: Result<HttpResponse, WebFetchError>) -> Self {
48 self.responses.lock().unwrap().insert(url.to_string(), result);
49 self
50 }
51}
52
53impl HttpClient for FakeHttpClient {
54 fn fetch<'a>(&'a self, url: &'a str, _timeout: Duration) -> BoxFuture<'a, Result<HttpResponse, WebFetchError>> {
55 let response = self.responses.lock().unwrap().get(url).cloned();
56 Box::pin(std::future::ready(response.unwrap_or_else(|| {
57 Err(WebFetchError::RequestFailed(format!("No fake response configured for URL: {url}")))
58 })))
59 }
60}