tuitbot_core/x_api/
null_client.rs1use crate::error::XApiError;
7use crate::x_api::types::*;
8use crate::x_api::XApiClient;
9
10const NOT_CONFIGURED: &str = "X API client not configured. Run `tuitbot auth` to authenticate.";
11
12pub struct NullXApiClient;
17
18#[async_trait::async_trait]
19impl XApiClient for NullXApiClient {
20 async fn search_tweets(
21 &self,
22 _query: &str,
23 _max_results: u32,
24 _since_id: Option<&str>,
25 _pagination_token: Option<&str>,
26 ) -> Result<SearchResponse, XApiError> {
27 Err(not_configured())
28 }
29
30 async fn get_mentions(
31 &self,
32 _user_id: &str,
33 _since_id: Option<&str>,
34 _pagination_token: Option<&str>,
35 ) -> Result<MentionResponse, XApiError> {
36 Err(not_configured())
37 }
38
39 async fn post_tweet(&self, _text: &str) -> Result<PostedTweet, XApiError> {
40 Err(not_configured())
41 }
42
43 async fn reply_to_tweet(
44 &self,
45 _text: &str,
46 _in_reply_to_id: &str,
47 ) -> Result<PostedTweet, XApiError> {
48 Err(not_configured())
49 }
50
51 async fn get_tweet(&self, _tweet_id: &str) -> Result<Tweet, XApiError> {
52 Err(not_configured())
53 }
54
55 async fn get_me(&self) -> Result<User, XApiError> {
56 Err(not_configured())
57 }
58
59 async fn get_user_tweets(
60 &self,
61 _user_id: &str,
62 _max_results: u32,
63 _pagination_token: Option<&str>,
64 ) -> Result<SearchResponse, XApiError> {
65 Err(not_configured())
66 }
67
68 async fn get_user_by_username(&self, _username: &str) -> Result<User, XApiError> {
69 Err(not_configured())
70 }
71}
72
73fn not_configured() -> XApiError {
74 XApiError::ApiError {
75 status: 0,
76 message: NOT_CONFIGURED.to_string(),
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[tokio::test]
85 async fn null_client_search_tweets() {
86 let client = NullXApiClient;
87 let result = client.search_tweets("query", 10, None, None).await;
88 assert!(result.is_err());
89 let err = result.unwrap_err();
90 assert!(format!("{err}").contains("not configured"));
91 }
92
93 #[tokio::test]
94 async fn null_client_get_mentions() {
95 let client = NullXApiClient;
96 let result = client.get_mentions("u1", None, None).await;
97 assert!(result.is_err());
98 }
99
100 #[tokio::test]
101 async fn null_client_post_tweet() {
102 let client = NullXApiClient;
103 let result = client.post_tweet("text").await;
104 assert!(result.is_err());
105 }
106
107 #[tokio::test]
108 async fn null_client_reply_to_tweet() {
109 let client = NullXApiClient;
110 let result = client.reply_to_tweet("text", "123").await;
111 assert!(result.is_err());
112 }
113
114 #[tokio::test]
115 async fn null_client_get_tweet() {
116 let client = NullXApiClient;
117 let result = client.get_tweet("123").await;
118 assert!(result.is_err());
119 }
120
121 #[tokio::test]
122 async fn null_client_get_me() {
123 let client = NullXApiClient;
124 let result = client.get_me().await;
125 assert!(result.is_err());
126 }
127
128 #[tokio::test]
129 async fn null_client_get_user_tweets() {
130 let client = NullXApiClient;
131 let result = client.get_user_tweets("u1", 10, None).await;
132 assert!(result.is_err());
133 }
134
135 #[tokio::test]
136 async fn null_client_get_user_by_username() {
137 let client = NullXApiClient;
138 let result = client.get_user_by_username("testuser").await;
139 assert!(result.is_err());
140 }
141
142 #[test]
143 fn not_configured_error_message() {
144 let err = not_configured();
145 match err {
146 XApiError::ApiError { status, message } => {
147 assert_eq!(status, 0);
148 assert!(message.contains("not configured"));
149 assert!(message.contains("tuitbot auth"));
150 }
151 _ => panic!("expected ApiError"),
152 }
153 }
154
155 #[tokio::test]
158 async fn null_client_upload_media() {
159 let client = NullXApiClient;
160 let result = client.upload_media(b"data", MediaType::Gif).await;
161 assert!(result.is_err());
162 }
163
164 #[tokio::test]
165 async fn null_client_post_tweet_with_media() {
166 let client = NullXApiClient;
167 let result = client
168 .post_tweet_with_media("text", &["m1".to_string()])
169 .await;
170 assert!(result.is_err());
171 }
172
173 #[tokio::test]
174 async fn null_client_reply_with_media() {
175 let client = NullXApiClient;
176 let result = client
177 .reply_to_tweet_with_media("text", "123", &["m1".to_string()])
178 .await;
179 assert!(result.is_err());
180 }
181
182 #[tokio::test]
183 async fn null_client_quote_tweet() {
184 let client = NullXApiClient;
185 let result = client.quote_tweet("text", "123").await;
186 assert!(result.is_err());
187 }
188
189 #[tokio::test]
190 async fn null_client_like_tweet() {
191 let client = NullXApiClient;
192 let result = client.like_tweet("u1", "t1").await;
193 assert!(result.is_err());
194 }
195
196 #[tokio::test]
197 async fn null_client_follow_user() {
198 let client = NullXApiClient;
199 let result = client.follow_user("u1", "u2").await;
200 assert!(result.is_err());
201 }
202
203 #[tokio::test]
204 async fn null_client_unfollow_user() {
205 let client = NullXApiClient;
206 let result = client.unfollow_user("u1", "u2").await;
207 assert!(result.is_err());
208 }
209
210 #[tokio::test]
211 async fn null_client_retweet() {
212 let client = NullXApiClient;
213 let result = client.retweet("u1", "t1").await;
214 assert!(result.is_err());
215 }
216
217 #[tokio::test]
218 async fn null_client_unretweet() {
219 let client = NullXApiClient;
220 let result = client.unretweet("u1", "t1").await;
221 assert!(result.is_err());
222 }
223
224 #[tokio::test]
225 async fn null_client_delete_tweet() {
226 let client = NullXApiClient;
227 let result = client.delete_tweet("t1").await;
228 assert!(result.is_err());
229 }
230
231 #[tokio::test]
232 async fn null_client_get_home_timeline() {
233 let client = NullXApiClient;
234 let result = client.get_home_timeline("u1", 10, None).await;
235 assert!(result.is_err());
236 }
237
238 #[tokio::test]
239 async fn null_client_unlike_tweet() {
240 let client = NullXApiClient;
241 let result = client.unlike_tweet("u1", "t1").await;
242 assert!(result.is_err());
243 }
244
245 #[tokio::test]
246 async fn null_client_get_followers() {
247 let client = NullXApiClient;
248 let result = client.get_followers("u1", 10, None).await;
249 assert!(result.is_err());
250 }
251
252 #[tokio::test]
253 async fn null_client_get_following() {
254 let client = NullXApiClient;
255 let result = client.get_following("u1", 10, None).await;
256 assert!(result.is_err());
257 }
258
259 #[tokio::test]
260 async fn null_client_get_user_by_id() {
261 let client = NullXApiClient;
262 let result = client.get_user_by_id("u1").await;
263 assert!(result.is_err());
264 }
265
266 #[tokio::test]
267 async fn null_client_get_liked_tweets() {
268 let client = NullXApiClient;
269 let result = client.get_liked_tweets("u1", 10, None).await;
270 assert!(result.is_err());
271 }
272
273 #[tokio::test]
274 async fn null_client_get_bookmarks() {
275 let client = NullXApiClient;
276 let result = client.get_bookmarks("u1", 10, None).await;
277 assert!(result.is_err());
278 }
279
280 #[tokio::test]
281 async fn null_client_bookmark_tweet() {
282 let client = NullXApiClient;
283 let result = client.bookmark_tweet("u1", "t1").await;
284 assert!(result.is_err());
285 }
286
287 #[tokio::test]
288 async fn null_client_unbookmark_tweet() {
289 let client = NullXApiClient;
290 let result = client.unbookmark_tweet("u1", "t1").await;
291 assert!(result.is_err());
292 }
293
294 #[tokio::test]
295 async fn null_client_get_users_by_ids() {
296 let client = NullXApiClient;
297 let result = client.get_users_by_ids(&["u1", "u2"]).await;
298 assert!(result.is_err());
299 }
300
301 #[tokio::test]
302 async fn null_client_get_tweet_liking_users() {
303 let client = NullXApiClient;
304 let result = client.get_tweet_liking_users("t1", 10, None).await;
305 assert!(result.is_err());
306 }
307
308 #[tokio::test]
309 async fn null_client_raw_request() {
310 let client = NullXApiClient;
311 let result = client.raw_request("GET", "/test", None, None, None).await;
312 assert!(result.is_err());
313 }
314
315 #[tokio::test]
318 async fn all_null_client_errors_contain_auth_hint() {
319 let client = NullXApiClient;
320 let err_msg = format!("{}", client.post_tweet("test").await.unwrap_err());
321 assert!(
322 err_msg.contains("tuitbot auth"),
323 "error should contain auth hint"
324 );
325 }
326}