1use chrono::NaiveDate;
2
3use crate::client::Executor;
4
5const TV_PATH: &str = "/tv/changes";
6const MOVIE_PATH: &str = "/movie/changes";
7const PERSON_PATH: &str = "/person/changes";
8
9#[derive(Clone, Debug, Default, Serialize)]
10pub struct Params {
11 #[serde(skip_serializing_if = "Option::is_none")]
13 pub start_date: Option<NaiveDate>,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub end_date: Option<NaiveDate>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub page: Option<u32>,
20}
21
22impl Params {
23 pub fn set_start_date(&mut self, value: NaiveDate) {
24 self.start_date = Some(value);
25 }
26
27 pub fn with_start_date(mut self, value: NaiveDate) -> Self {
28 self.set_start_date(value);
29 self
30 }
31
32 pub fn set_end_date(&mut self, value: NaiveDate) {
33 self.end_date = Some(value);
34 }
35
36 pub fn with_end_date(mut self, value: NaiveDate) -> Self {
37 self.set_end_date(value);
38 self
39 }
40
41 pub fn set_page(&mut self, value: u32) {
42 self.page = Some(value);
43 }
44
45 pub fn with_page(mut self, value: u32) -> Self {
46 self.set_page(value);
47 self
48 }
49}
50
51impl<E: Executor> crate::Client<E> {
52 pub async fn list_movie_changes(
69 &self,
70 params: &Params,
71 ) -> crate::Result<crate::common::PaginatedResult<super::Change>> {
72 self.execute(MOVIE_PATH, params).await
73 }
74
75 pub async fn list_person_changes(
92 &self,
93 params: &Params,
94 ) -> crate::Result<crate::common::PaginatedResult<super::Change>> {
95 self.execute(PERSON_PATH, params).await
96 }
97
98 pub async fn list_tvshow_changes(
115 &self,
116 params: &Params,
117 ) -> crate::Result<crate::common::PaginatedResult<super::Change>> {
118 self.execute(TV_PATH, params).await
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use chrono::NaiveDate;
125 use mockito::Matcher;
126
127 use crate::changes::list::Params;
128 use crate::client::Client;
129 use crate::client::reqwest::Client as ReqwestClient;
130
131 #[tokio::test]
132 async fn tv_works() {
133 let mut server = mockito::Server::new_async().await;
134 let m = server
135 .mock("GET", super::TV_PATH)
136 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
137 .with_status(200)
138 .with_header("content-type", "application/json")
139 .with_body(include_str!("../../assets/tv-all-changes.json"))
140 .create_async()
141 .await;
142
143 let client = Client::<ReqwestClient>::builder()
144 .with_api_key("secret".into())
145 .with_base_url(server.url())
146 .build()
147 .unwrap();
148 let result = client
149 .list_tvshow_changes(&Default::default())
150 .await
151 .unwrap();
152 assert_eq!(result.page, 1);
153
154 m.assert_async().await;
155 }
156
157 #[tokio::test]
158 async fn tv_works_with_args() {
159 let mut server = mockito::Server::new_async().await;
160 let m = server
161 .mock("GET", super::TV_PATH)
162 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
163 .match_query(Matcher::AllOf(vec![
164 Matcher::UrlEncoded("api_key".into(), "secret".into()),
165 Matcher::UrlEncoded("start_date".into(), "2015-03-14".into()),
166 Matcher::UrlEncoded("end_date".into(), "2019-03-14".into()),
167 Matcher::UrlEncoded("page".into(), "2".into()),
168 ]))
169 .with_status(200)
170 .with_header("content-type", "application/json")
171 .with_body(include_str!("../../assets/tv-all-changes.json"))
172 .create_async()
173 .await;
174
175 let client = Client::<ReqwestClient>::builder()
176 .with_api_key("secret".into())
177 .with_base_url(server.url())
178 .build()
179 .unwrap();
180 let result = client
181 .list_tvshow_changes(
182 &Params::default()
183 .with_start_date(NaiveDate::from_ymd_opt(2015, 3, 14).unwrap())
184 .with_end_date(NaiveDate::from_ymd_opt(2019, 3, 14).unwrap())
185 .with_page(2),
186 )
187 .await
188 .unwrap();
189 assert_eq!(result.page, 1);
190 m.assert_async().await;
191 }
192
193 #[tokio::test]
194 async fn movie_works() {
195 let mut server = mockito::Server::new_async().await;
196 let m = server
197 .mock("GET", super::MOVIE_PATH)
198 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
199 .with_status(200)
200 .with_header("content-type", "application/json")
201 .with_body(include_str!("../../assets/movie-all-changes.json"))
202 .create_async()
203 .await;
204
205 let client = Client::<ReqwestClient>::builder()
206 .with_api_key("secret".into())
207 .with_base_url(server.url())
208 .build()
209 .unwrap();
210 let result = client
211 .list_movie_changes(&Default::default())
212 .await
213 .unwrap();
214 assert_eq!(result.page, 1);
215
216 m.assert_async().await;
217 }
218
219 #[tokio::test]
220 async fn person_works() {
221 let mut server = mockito::Server::new_async().await;
222 let m = server
223 .mock("GET", super::PERSON_PATH)
224 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
225 .with_status(200)
226 .with_header("content-type", "application/json")
227 .with_body(include_str!("../../assets/movie-all-changes.json"))
228 .create_async()
229 .await;
230
231 let client = Client::<ReqwestClient>::builder()
232 .with_api_key("secret".into())
233 .with_base_url(server.url())
234 .build()
235 .unwrap();
236 let result = client
237 .list_person_changes(&Default::default())
238 .await
239 .unwrap();
240 assert_eq!(result.page, 1);
241
242 m.assert_async().await;
243 }
244
245 #[tokio::test]
246 async fn invalid_api_key() {
247 let mut server = mockito::Server::new_async().await;
248 let m = server
249 .mock("GET", super::TV_PATH)
250 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
251 .with_status(401)
252 .with_header("content-type", "application/json")
253 .with_body(include_str!("../../assets/invalid-api-key.json"))
254 .create_async()
255 .await;
256
257 let client = Client::<ReqwestClient>::builder()
258 .with_api_key("secret".into())
259 .with_base_url(server.url())
260 .build()
261 .unwrap();
262 let err = client
263 .list_tvshow_changes(&Default::default())
264 .await
265 .unwrap_err();
266 let server_err = err.as_server_error().unwrap();
267 assert_eq!(server_err.status_code, 7);
268
269 m.assert_async().await;
270 }
271
272 #[tokio::test]
273 async fn resource_not_found() {
274 let mut server = mockito::Server::new_async().await;
275 let m = server
276 .mock("GET", super::TV_PATH)
277 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
278 .with_status(404)
279 .with_header("content-type", "application/json")
280 .with_body(include_str!("../../assets/resource-not-found.json"))
281 .create_async()
282 .await;
283
284 let client = Client::<ReqwestClient>::builder()
285 .with_api_key("secret".into())
286 .with_base_url(server.url())
287 .build()
288 .unwrap();
289 let err = client
290 .list_tvshow_changes(&Default::default())
291 .await
292 .unwrap_err();
293 let server_err = err.as_server_error().unwrap();
294 assert_eq!(server_err.status_code, 34);
295
296 m.assert_async().await;
297 }
298}
299
300#[cfg(all(test, feature = "integration"))]
301mod integration_tests {
302 use crate::client::Client;
303 use crate::client::reqwest::Client as ReqwestClient;
304
305 #[tokio::test]
306 async fn execute_tv() {
307 let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
308 let client = Client::<ReqwestClient>::new(secret);
309
310 let result = client
311 .list_tvshow_changes(&Default::default())
312 .await
313 .unwrap();
314 assert_eq!(result.page, 1);
315 }
316
317 #[tokio::test]
318 async fn execute_movie() {
319 let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
320 let client = Client::<ReqwestClient>::new(secret);
321
322 let result = client
323 .list_movie_changes(&Default::default())
324 .await
325 .unwrap();
326 assert_eq!(result.page, 1);
327 }
328
329 #[tokio::test]
330 async fn execute_person() {
331 let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
332 let client = Client::<ReqwestClient>::new(secret);
333
334 let result = client
335 .list_person_changes(&Default::default())
336 .await
337 .unwrap();
338 assert_eq!(result.page, 1);
339 }
340}