mangadex_api/v5/captcha/solve/
post.rs1use derive_builder::Builder;
35use serde::Serialize;
36
37use crate::HttpClientRef;
38use mangadex_api_schema::NoData;
39
40#[cfg_attr(
44 feature = "deserializable-endpoint",
45 derive(serde::Deserialize, getset::Getters, getset::Setters)
46)]
47#[derive(Debug, Serialize, Clone, Builder)]
48#[serde(rename_all = "camelCase")]
49#[builder(
50 setter(into, strip_option),
51 build_fn(error = "mangadex_api_types::error::BuilderError")
52)]
53pub struct SolveCaptcha {
54 #[doc(hidden)]
56 #[serde(skip)]
57 #[builder(pattern = "immutable")]
58 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
59 pub http_client: HttpClientRef,
60
61 pub captcha_challenge: String,
62}
63
64endpoint! {
65 POST "/captcha/solve",
66 #[body] SolveCaptcha,
67 #[rate_limited] NoData,
68 SolveCaptchaBuilder
69}
70
71#[cfg(test)]
72mod tests {
73 use serde_json::json;
74 use url::Url;
75 use wiremock::matchers::{body_json, header, method, path};
76 use wiremock::{Mock, MockServer, ResponseTemplate};
77
78 use crate::{HttpClient, MangaDexClient};
79 use mangadex_api_types::error::Error;
80
81 #[tokio::test]
82 async fn solve_captcha_fires_a_request_to_base_url() -> anyhow::Result<()> {
83 let mock_server = MockServer::start().await;
84 let http_client: HttpClient = HttpClient::builder()
85 .base_url(Url::parse(&mock_server.uri())?)
86 .build()?;
87 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
88
89 let expected_body = json!({
90 "captchaChallenge": "solution",
91 });
92 let response_body = json!({"result": "ok"});
93
94 Mock::given(method("POST"))
95 .and(path(r"/captcha/solve"))
96 .and(header("Content-Type", "application/json"))
97 .and(body_json(expected_body))
98 .respond_with(
99 ResponseTemplate::new(200)
100 .insert_header("x-ratelimit-retry-after", "1698723860")
101 .insert_header("x-ratelimit-limit", "40")
102 .insert_header("x-ratelimit-remaining", "39")
103 .set_body_json(response_body),
104 )
105 .expect(1)
106 .mount(&mock_server)
107 .await;
108
109 mangadex_client
110 .captcha()
111 .solve()
112 .post()
113 .captcha_challenge("solution")
114 .send()
115 .await?;
116
117 Ok(())
118 }
119
120 #[tokio::test]
121 async fn solve_captcha_handles_400() -> anyhow::Result<()> {
122 let mock_server = MockServer::start().await;
123 let http_client: HttpClient = HttpClient::builder()
124 .base_url(Url::parse(&mock_server.uri())?)
125 .build()?;
126 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
127
128 let expected_body = json!({
129 "captchaChallenge": "solution",
130 });
131 let response_body = json!({
132 "result": "error",
133 "errors": []
134 });
135
136 Mock::given(method("POST"))
137 .and(path(r"/captcha/solve"))
138 .and(header("Content-Type", "application/json"))
139 .and(body_json(expected_body))
140 .respond_with(
141 ResponseTemplate::new(400)
142 .insert_header("x-ratelimit-retry-after", "1698723860")
143 .insert_header("x-ratelimit-limit", "40")
144 .insert_header("x-ratelimit-remaining", "39")
145 .set_body_json(response_body),
146 )
147 .expect(1)
148 .mount(&mock_server)
149 .await;
150
151 let res = mangadex_client
152 .captcha()
153 .solve()
154 .post()
155 .captcha_challenge("solution")
156 .send()
157 .await
158 .expect_err("expected error");
159
160 if let Error::Api(errors) = res {
161 assert_eq!(errors.errors.len(), 0);
162 }
163
164 Ok(())
165 }
166}