tmdb_api/movie/
changes.rs1use std::borrow::Cow;
2
3use chrono::NaiveDate;
4
5#[derive(Clone, Debug, Default)]
25pub struct MovieChanges {
26 pub movie_id: u64,
28 pub start_date: Option<NaiveDate>,
30 pub end_date: Option<NaiveDate>,
32 pub page: Option<u32>,
34}
35
36impl MovieChanges {
37 pub fn new(movie_id: u64) -> Self {
38 Self {
39 movie_id,
40 start_date: None,
41 end_date: None,
42 page: None,
43 }
44 }
45
46 pub fn with_start_date(mut self, value: Option<NaiveDate>) -> Self {
47 self.start_date = value;
48 self
49 }
50
51 pub fn with_end_date(mut self, value: Option<NaiveDate>) -> Self {
52 self.end_date = value;
53 self
54 }
55
56 pub fn with_page(mut self, value: Option<u32>) -> Self {
57 self.page = value;
58 self
59 }
60}
61
62#[derive(Debug, Deserialize, Serialize)]
63pub struct MovieChange {
64 pub key: String,
65 pub items: Vec<MovieChangeItem>,
66}
67
68#[derive(Debug, Deserialize, Serialize)]
69pub struct MovieChangeItem {
70 pub id: String,
71 pub action: String,
72 pub time: chrono::DateTime<chrono::Utc>,
73 pub iso_639_1: String,
74 pub iso_3166_1: String,
75 }
79
80#[derive(Debug, Deserialize, Serialize)]
81pub struct MovieChangesResult {
82 pub changes: Vec<MovieChange>,
83}
84
85impl crate::prelude::Command for MovieChanges {
86 type Output = MovieChangesResult;
87
88 fn path(&self) -> Cow<'static, str> {
89 Cow::Owned(format!("/movie/{}/changes", self.movie_id))
90 }
91
92 fn params(&self) -> Vec<(&'static str, Cow<'_, str>)> {
93 Vec::new()
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::MovieChanges;
100 use crate::client::Client;
101 use crate::client::reqwest::ReqwestExecutor;
102 use crate::prelude::Command;
103 use mockito::Matcher;
104
105 #[tokio::test]
106 async fn it_works() {
107 let mut server = mockito::Server::new_async().await;
108 let client = Client::<ReqwestExecutor>::builder()
109 .with_api_key("secret".into())
110 .with_base_url(server.url())
111 .build()
112 .unwrap();
113
114 let _m = server
115 .mock("GET", "/movie/3/changes")
116 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
117 .with_status(200)
118 .with_header("content-type", "application/json")
119 .with_body(include_str!("../../assets/movie-single-changes.json"))
120 .create_async()
121 .await;
122
123 let result = MovieChanges::new(3).execute(&client).await.unwrap();
124 assert_eq!(result.changes.len(), 1);
125 }
126
127 #[tokio::test]
128 async fn invalid_api_key() {
129 let mut server = mockito::Server::new_async().await;
130 let client = Client::<ReqwestExecutor>::builder()
131 .with_api_key("secret".into())
132 .with_base_url(server.url())
133 .build()
134 .unwrap();
135
136 let _m = server
137 .mock("GET", "/movie/1/changes")
138 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
139 .with_status(401)
140 .with_header("content-type", "application/json")
141 .with_body(include_str!("../../assets/invalid-api-key.json"))
142 .create_async()
143 .await;
144
145 let err = MovieChanges::new(1).execute(&client).await.unwrap_err();
146 let server_err = err.as_server_error().unwrap();
147 assert_eq!(server_err.status_code, 7);
148 }
149
150 #[tokio::test]
151 async fn resource_not_found() {
152 let mut server = mockito::Server::new_async().await;
153 let client = Client::<ReqwestExecutor>::builder()
154 .with_api_key("secret".into())
155 .with_base_url(server.url())
156 .build()
157 .unwrap();
158
159 let _m = server
160 .mock("GET", "/movie/1/changes")
161 .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
162 .with_status(404)
163 .with_header("content-type", "application/json")
164 .with_body(include_str!("../../assets/resource-not-found.json"))
165 .create_async()
166 .await;
167
168 let err = MovieChanges::new(1).execute(&client).await.unwrap_err();
169 let server_err = err.as_server_error().unwrap();
170 assert_eq!(server_err.status_code, 34);
171 }
172}
173
174#[cfg(all(test, feature = "integration"))]
175mod integration_tests {
176 use super::MovieChanges;
177 use crate::client::Client;
178 use crate::client::reqwest::ReqwestExecutor;
179 use crate::prelude::Command;
180
181 #[tokio::test]
182 async fn execute() {
183 let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
184 let client = Client::<ReqwestExecutor>::new(secret);
185
186 let result = MovieChanges::new(1)
187 .with_start_date(Some(chrono::NaiveDate::from_ymd_opt(2015, 3, 14).unwrap()))
188 .with_end_date(Some(chrono::NaiveDate::from_ymd_opt(2019, 3, 14).unwrap()))
189 .execute(&client)
190 .await
191 .unwrap();
192 assert!(result.changes.is_empty());
193 }
194}