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