polar_rs/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use regex::Regex;
4use std::error::Error;
5use std::fmt::Display;
6use std::sync::LazyLock;
7
8use reqwest::{IntoUrl, StatusCode};
9use serde::de::DeserializeOwned;
10use serde::{Deserialize, Serialize};
11use serde_json::{Value, json};
12use uuid::Uuid;
13
14mod enums;
15mod models;
16
17pub use enums::*;
18pub use models::*;
19
20static REGEX_QUERY_ARRAY: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\[[0-9]+\]").unwrap());
21
22#[derive(Debug, Deserialize)]
23pub enum PolarError {
24    NotFound,
25    Request(String),
26    Unauthorized,
27    Unknown(String),
28    Validation(String),
29}
30
31impl Display for PolarError {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            PolarError::Request(msg) => write!(f, "Request error: {msg}"),
35            PolarError::NotFound => write!(f, "Not found"),
36            PolarError::Unauthorized => write!(f, "Unauthorized"),
37            PolarError::Unknown(msg) => write!(f, "Unknown error: {msg}"),
38            PolarError::Validation(msg) => write!(f, "Validation error: {msg}"),
39        }
40    }
41}
42
43impl From<serde_json::Error> for PolarError {
44    fn from(err: serde_json::Error) -> Self {
45        PolarError::Request(err.to_string())
46    }
47}
48
49impl From<reqwest::Error> for PolarError {
50    fn from(err: reqwest::Error) -> Self {
51        PolarError::Request(err.to_string())
52    }
53}
54
55impl From<url::ParseError> for PolarError {
56    fn from(err: url::ParseError) -> Self {
57        PolarError::Request(err.to_string())
58    }
59}
60
61impl Error for PolarError {}
62
63pub type PolarResult<T> = Result<T, PolarError>;
64
65pub struct Polar {
66    base_url: reqwest::Url,
67    access_token: String,
68}
69
70impl Polar {
71    pub fn new<U: IntoUrl, T: Display>(base_url: U, access_token: T) -> PolarResult<Self> {
72        if access_token.to_string().is_empty() {
73            return Err(PolarError::Request("access_token cannot be empty".to_owned()));
74        }
75
76        let base_url = if let Ok(mut url) = base_url.into_url() {
77            if !url.path().ends_with('/') {
78                url.set_path(&format!("{}/", url.path()))
79            }
80
81            url
82        } else {
83            return Err(PolarError::Request("base_url is not a valid URL".to_owned()));
84        };
85
86        Ok(Self {
87            base_url,
88            access_token: access_token.to_string(),
89        })
90    }
91
92    pub async fn delete<T>(&self, path: &str) -> PolarResult<T>
93    where
94        T: DeserializeOwned,
95    {
96        let response = reqwest::Client::new()
97            .delete(self.base_url.join(path)?)
98            .bearer_auth(&self.access_token)
99            .send()
100            .await?;
101
102        match response.status() {
103            StatusCode::OK => Ok(response.json().await.unwrap()),
104            StatusCode::NOT_FOUND => Err(PolarError::NotFound),
105            StatusCode::UNPROCESSABLE_ENTITY => Err(PolarError::Validation(response.text().await?)),
106            StatusCode::UNAUTHORIZED => Err(PolarError::Unauthorized),
107            _ => Err(PolarError::Unknown(response.text().await?)),
108        }
109    }
110
111    pub async fn get<T>(&self, path: &str) -> PolarResult<T>
112    where
113        T: DeserializeOwned,
114    {
115        self.get_with_params(path, &()).await
116    }
117
118    pub async fn get_with_params<P, T>(&self, path: &str, params: &P) -> PolarResult<T>
119    where
120        P: Serialize,
121        T: DeserializeOwned,
122    {
123        let mut url = self.base_url.join(path)?;
124
125        if let Ok(query) = serde_qs::to_string(params) {
126            let query = REGEX_QUERY_ARRAY.replace_all(&query, "");
127
128            url.set_query(Some(&query));
129        }
130
131        let response = reqwest::Client::new()
132            .get(url)
133            .bearer_auth(&self.access_token)
134            .send()
135            .await?;
136
137        match response.status() {
138            StatusCode::OK => Ok(response.json().await.unwrap()),
139            StatusCode::NOT_FOUND => Err(PolarError::NotFound),
140            StatusCode::UNPROCESSABLE_ENTITY => Err(PolarError::Validation(response.text().await?)),
141            StatusCode::UNAUTHORIZED => Err(PolarError::Unauthorized),
142            _ => Err(PolarError::Unknown(response.text().await?)),
143        }
144    }
145
146    pub async fn patch<P, T>(&self, path: &str, params: &P) -> PolarResult<T>
147    where
148        P: Serialize,
149        T: DeserializeOwned,
150    {
151        let response = reqwest::Client::new()
152            .patch(self.base_url.join(path)?)
153            .bearer_auth(&self.access_token)
154            .json(params)
155            .send()
156            .await?;
157
158        match response.status() {
159            StatusCode::OK => Ok(response.json().await.unwrap()),
160            StatusCode::NOT_FOUND => Err(PolarError::NotFound),
161            StatusCode::UNPROCESSABLE_ENTITY => Err(PolarError::Validation(response.text().await?)),
162            StatusCode::UNAUTHORIZED => Err(PolarError::Unauthorized),
163            _ => Err(PolarError::Unknown(response.text().await?)),
164        }
165    }
166
167    pub async fn post<P, T>(&self, path: &str, params: &P) -> PolarResult<T>
168    where
169        P: Serialize,
170        T: DeserializeOwned,
171    {
172        let response = reqwest::Client::new()
173            .post(self.base_url.join(path)?)
174            .bearer_auth(&self.access_token)
175            .json(params)
176            .send()
177            .await?;
178
179        match response.status() {
180            StatusCode::CREATED => Ok(response.json().await.unwrap()),
181            StatusCode::UNPROCESSABLE_ENTITY => Err(PolarError::Validation(response.text().await?)),
182            StatusCode::UNAUTHORIZED => Err(PolarError::Unauthorized),
183            _ => Err(PolarError::Unknown(response.text().await?)),
184        }
185    }
186
187    /// **Create a checkout session.**
188    ///
189    /// Scopes: `checkouts:write`
190    ///
191    /// Reference: <https://docs.polar.sh/api-reference/checkouts/create-session>
192    pub async fn create_checkout_session(&self, params: &CheckoutSessionParams) -> PolarResult<CheckoutSession> {
193        self.post("checkouts", params).await
194    }
195
196    /// **Get a checkout session by ID.**
197    ///
198    /// Scopes: `checkouts:read` `checkouts:write`
199    ///
200    /// Reference: <https://docs.polar.sh/api-reference/checkouts/get-session>
201    pub async fn get_checkout_session(&self, id: Uuid) -> PolarResult<CheckoutSession> {
202        self.get(&format!("checkouts/{id}")).await
203    }
204
205    /// **List checkout sessions.**
206    ///
207    /// Scopes: `checkouts:read` `checkouts:write`
208    ///
209    /// Reference: <https://docs.polar.sh/api-reference/checkouts/list-sessions>
210    pub async fn list_checkout_sessions(
211        &self,
212        params: &ListCheckoutSessionsParams,
213    ) -> PolarResult<Page<CheckoutSession>> {
214        self.get_with_params("checkouts", params).await
215    }
216
217    /// **Get a subscription by ID.**
218    ///
219    /// Scopes: `subscriptions:read` `subscriptions:write`
220    ///
221    /// Reference: <https://docs.polar.sh/api-reference/subscriptions/get>
222    pub async fn get_subscription(&self, id: Uuid) -> PolarResult<Subscription> {
223        self.get(&format!("subscriptions/{id}")).await
224    }
225
226    /// **List subscriptions.**
227    ///
228    /// Scopes: `subscriptions:read` `subscriptions:write`
229    ///
230    /// Reference: <https://docs.polar.sh/api-reference/subscriptions/list>
231    pub async fn list_subscriptions(&self, params: &ListSubscriptionsParams) -> PolarResult<Page<Subscription>> {
232        self.get_with_params("subscriptions", params).await
233    }
234
235    /// **Update a subscription.**
236    ///
237    /// Scopes: `subscriptions:write`
238    ///
239    /// Reference: <https://docs.polar.sh/api-reference/subscriptions/update>
240    pub async fn update_subscription(&self, id: Uuid, params: &SubscriptionParams) -> PolarResult<Subscription> {
241        self.patch(&format!("subscriptions/{id}"), params).await
242    }
243
244    /// **Revoke a subscription, i.e cancel immediately.**
245    ///
246    /// Scopes: `subscriptions:write`
247    ///
248    /// Reference: <https://docs.polar.sh/api-reference/subscriptions/revoke>
249    pub async fn revoke_subscription(&self, id: Uuid) -> PolarResult<Subscription> {
250        self.delete(&format!("subscriptions/{id}")).await
251    }
252
253    /// **Get a product by ID.**
254    ///
255    /// Scopes: `products:read` `products:write`
256    ///
257    /// Reference: <https://docs.polar.sh/api-reference/products/get>
258    pub async fn get_product(&self, id: Uuid) -> PolarResult<Product> {
259        self.get(&format!("products/{id}")).await
260    }
261
262    /// **List products.**
263    ///
264    /// Scopes: `products:read` `products:write`
265    ///
266    /// Reference: <https://docs.polar.sh/api-reference/products/list>
267    pub async fn list_products(&self, params: &ListProductsParams) -> PolarResult<Page<Product>> {
268        self.get_with_params("products", params).await
269    }
270
271    /// **Create a product.**
272    ///
273    /// Scopes: `products:write`
274    ///
275    /// Reference: <https://docs.polar.sh/api-reference/products/create>
276    pub async fn create_product(&self, params: &ProductParams) -> PolarResult<Product> {
277        self.post("products", params).await
278    }
279
280    /// **Update a product.**
281    ///
282    /// Scopes: `products:write`
283    ///
284    /// Reference: <https://docs.polar.sh/api-reference/products/update>
285    pub async fn update_product(&self, id: Uuid, params: &UpdateProductParams) -> PolarResult<Product> {
286        self.patch(&format!("products/{id}"), params).await
287    }
288
289    /// **Update benefits granted by a product..**
290    ///
291    /// Scopes: `products:write`
292    ///
293    /// Reference: <https://docs.polar.sh/api-reference/products/update-benefits>
294    pub async fn update_product_benefits(&self, id: Uuid, benefits: Vec<Uuid>) -> PolarResult<Product> {
295        self.patch(&format!("products/{id}/benefits"), &json!({ "benefits": benefits }))
296            .await
297    }
298
299    /// **Ingest batch of events.**
300    ///
301    /// Scopes: `events:write`
302    ///
303    /// Reference: <https://docs.polar.sh/api-reference/events/ingest>
304    pub async fn ingest_events(&self, events: Vec<EventParams>) -> PolarResult<i64> {
305        self.post("events/ingest", &json!({ "events": events }))
306            .await
307            .map(|resp: Value| resp["inserted"].as_i64().unwrap())
308    }
309
310    // **Get an event by ID.**
311    //
312    // Scopes: `events:read` `events:write`
313    //
314    // Reference: <https://docs.polar.sh/api-reference/events/get>
315    pub async fn get_event(&self, id: Uuid) -> PolarResult<Event> {
316        self.get(&format!("events/{id}")).await
317    }
318
319    /// **Create a meter.**
320    ///
321    /// Scopes: `meters:write`
322    ///
323    /// Reference: <https://docs.polar.sh/api-reference/meters/create>
324    pub async fn create_meter(&self, params: &MeterParams) -> PolarResult<Meter> {
325        self.post("meters", params).await
326    }
327
328    /// **Get a meter by ID.**
329    ///
330    /// Scopes: `meters:read` `meters:write`
331    ///
332    /// Reference: <https://docs.polar.sh/api-reference/meters/get>
333    pub async fn get_meter(&self, id: Uuid) -> PolarResult<Meter> {
334        self.get(&format!("meters/{id}")).await
335    }
336
337    /// **List meters.**
338    ///
339    /// Scopes: `meters:read` `meters:write`
340    ///
341    /// Reference: <https://docs.polar.sh/api-reference/meters/list>
342    pub async fn list_meters(&self, params: &ListMetersParams) -> PolarResult<Page<Meter>> {
343        self.get_with_params("meters", params).await
344    }
345
346    /// **Update a meter.**
347    ///
348    /// Scopes: `meters:write`
349    ///
350    /// Reference: <https://docs.polar.sh/api-reference/meters/update>
351    pub async fn update_meter(&self, id: Uuid, params: &UpdateMeterParams) -> PolarResult<Meter> {
352        self.patch(&format!("meters/{id}"), params).await
353    }
354
355    /// **Get quantities of a meter over a time period.**
356    ///
357    /// Scopes: `meters:read` `meters:write`
358    ///
359    /// Reference: <https://docs.polar.sh/api-reference/meters/get-quantities>
360    pub async fn get_meter_quantities(&self, id: Uuid, params: &MeterQuantitiesParams) -> PolarResult<MeterQuantities> {
361        self.get_with_params(&format!("meters/{id}/quantities"), params).await
362    }
363}
364
365#[cfg(test)]
366mod tests {
367    use std::{fs::File, io::BufReader};
368
369    use serde_json::from_reader;
370    use wiremock::{Mock, MockServer, ResponseTemplate, matchers};
371
372    use super::*;
373
374    fn get_fixture<T: DeserializeOwned>(name: &str) -> T {
375        let file = File::open(format!("fixtures/{name}.json")).unwrap();
376        let reader = BufReader::new(file);
377
378        from_reader(reader).unwrap()
379    }
380
381    async fn get_mock<B: Serialize>(method: &str, path: &str, status_code: u16, body: B) -> MockServer {
382        let mock_server = MockServer::start().await;
383
384        Mock::given(matchers::method(method))
385            .and(matchers::path(path))
386            .respond_with(ResponseTemplate::new(status_code).set_body_json(body))
387            .mount(&mock_server)
388            .await;
389
390        mock_server
391    }
392
393    fn get_test_polar(base_url: String) -> Polar {
394        Polar::new(base_url, "123").ok().unwrap()
395    }
396
397    #[test]
398    fn should_get_polar_when_arguments_are_valid() {
399        let result = Polar::new("https://sandbox-api.polar.sh/v1/", "123");
400
401        assert!(result.is_ok());
402    }
403
404    #[test]
405    fn should_not_get_polar_when_base_url_is_invalid() {
406        let result = Polar::new("/v1/", "123");
407
408        assert!(result.is_err());
409    }
410
411    #[test]
412    fn should_not_get_polar_when_access_token_is_empty() {
413        let result = Polar::new("https://sandbox-api.polar.sh/v1/", "");
414
415        assert!(result.is_err());
416    }
417
418    #[tokio::test]
419    async fn should_create_checkout_session() {
420        let mock = get_mock("POST", "/checkouts", 201, get_fixture::<Value>("checkout_session")).await;
421
422        let polar = get_test_polar(mock.uri());
423
424        let params = get_fixture("checkout_session_params");
425
426        let result = polar.create_checkout_session(&params).await;
427
428        assert!(result.is_ok());
429    }
430
431    #[tokio::test]
432    async fn should_not_create_checkout_session() {
433        let mock = get_mock("POST", "/checkouts", 422, get_fixture::<Value>("unprocessable_entity")).await;
434
435        let polar = get_test_polar(mock.uri());
436
437        let params = get_fixture("checkout_session_params");
438
439        let result = polar.create_checkout_session(&params).await;
440
441        assert!(result.is_err());
442    }
443
444    #[tokio::test]
445    async fn should_get_checkout_session() {
446        let checkout_id = Uuid::new_v4();
447        let mock = get_mock(
448            "GET",
449            &format!("/checkouts/{}", checkout_id),
450            200,
451            get_fixture::<Value>("checkout_session"),
452        )
453        .await;
454
455        let polar = get_test_polar(mock.uri());
456
457        let result = polar.get_checkout_session(checkout_id).await;
458
459        assert!(result.is_ok());
460    }
461
462    #[tokio::test]
463    async fn should_not_get_checkout_session() {
464        let checkout_id = Uuid::new_v4();
465        let mock = get_mock(
466            "GET",
467            &format!("/checkouts/{}", checkout_id),
468            404,
469            get_fixture::<Value>("not_found"),
470        )
471        .await;
472
473        let polar = get_test_polar(mock.uri());
474
475        let result = polar.get_checkout_session(checkout_id).await;
476
477        assert!(result.is_err());
478    }
479
480    #[tokio::test]
481    async fn should_list_checkout_sessions() {
482        let mock = get_mock("GET", "/checkouts", 200, get_fixture::<Value>("checkout_sessions_list")).await;
483
484        let polar = get_test_polar(mock.uri());
485
486        let result = polar
487            .list_checkout_sessions(&ListCheckoutSessionsParams::default())
488            .await;
489
490        assert!(result.is_ok());
491    }
492
493    #[tokio::test]
494    async fn should_get_subscription() {
495        let subscription_id = Uuid::new_v4();
496        let mock = get_mock(
497            "GET",
498            &format!("/subscriptions/{}", subscription_id),
499            200,
500            get_fixture::<Value>("subscription"),
501        )
502        .await;
503
504        let polar = get_test_polar(mock.uri());
505
506        let result = polar.get_subscription(subscription_id).await;
507
508        assert!(result.is_ok());
509    }
510
511    #[tokio::test]
512    async fn should_not_get_subscription() {
513        let subscription_id = Uuid::new_v4();
514        let mock = get_mock(
515            "GET",
516            &format!("/subscriptions/{}", subscription_id),
517            404,
518            get_fixture::<Value>("not_found"),
519        )
520        .await;
521
522        let polar = get_test_polar(mock.uri());
523
524        let result = polar.get_subscription(subscription_id).await;
525
526        assert!(result.is_err());
527    }
528
529    #[tokio::test]
530    async fn should_list_subscriptions() {
531        let mock = get_mock("GET", "/subscriptions", 200, get_fixture::<Value>("subscriptions_list")).await;
532
533        let polar = get_test_polar(mock.uri());
534
535        let result = polar.list_subscriptions(&ListSubscriptionsParams::default()).await;
536
537        assert!(result.is_ok());
538    }
539
540    #[tokio::test]
541    async fn should_update_subscription() {
542        let subscription_id = Uuid::new_v4();
543        let mock = get_mock(
544            "PATCH",
545            &format!("/subscriptions/{}", subscription_id),
546            200,
547            get_fixture::<Value>("subscription"),
548        )
549        .await;
550
551        let polar = get_test_polar(mock.uri());
552
553        let params = get_fixture("subscription_params");
554
555        let result = polar.update_subscription(subscription_id, &params).await;
556
557        assert!(result.is_ok());
558    }
559
560    #[tokio::test]
561    async fn should_not_update_subscription() {
562        let subscription_id = Uuid::new_v4();
563        let mock = get_mock(
564            "PATCH",
565            &format!("/subscriptions/{}", subscription_id),
566            422,
567            get_fixture::<Value>("unprocessable_entity"),
568        )
569        .await;
570
571        let polar = get_test_polar(mock.uri());
572
573        let params = get_fixture("subscription_params");
574
575        let result = polar.update_subscription(subscription_id, &params).await;
576
577        assert!(result.is_err());
578    }
579
580    #[tokio::test]
581    async fn should_revoke_subscription() {
582        let subscription_id = Uuid::new_v4();
583        let mock = get_mock(
584            "DELETE",
585            &format!("/subscriptions/{}", subscription_id),
586            200,
587            get_fixture::<Value>("subscription"),
588        )
589        .await;
590
591        let polar = get_test_polar(mock.uri());
592
593        let result = polar.revoke_subscription(subscription_id).await;
594
595        assert!(result.is_ok());
596    }
597
598    #[tokio::test]
599    async fn should_not_revoke_subscription() {
600        let subscription_id = Uuid::new_v4();
601        let mock = get_mock(
602            "DELETE",
603            &format!("/subscriptions/{}", subscription_id),
604            422,
605            get_fixture::<Value>("unprocessable_entity"),
606        )
607        .await;
608
609        let polar = get_test_polar(mock.uri());
610
611        let result = polar.revoke_subscription(subscription_id).await;
612
613        assert!(result.is_err());
614    }
615
616    #[tokio::test]
617    async fn should_get_product() {
618        let product_id = Uuid::new_v4();
619        let mock = get_mock(
620            "GET",
621            &format!("/products/{}", product_id),
622            200,
623            get_fixture::<Value>("product"),
624        )
625        .await;
626
627        let polar = get_test_polar(mock.uri());
628
629        let result = polar.get_product(product_id).await;
630
631        assert!(result.is_ok());
632    }
633
634    #[tokio::test]
635    async fn should_not_get_product() {
636        let product_id = Uuid::new_v4();
637        let mock = get_mock(
638            "GET",
639            &format!("/products/{}", product_id),
640            404,
641            get_fixture::<Value>("not_found"),
642        )
643        .await;
644
645        let polar = get_test_polar(mock.uri());
646
647        let result = polar.get_product(product_id).await;
648
649        assert!(result.is_err());
650    }
651
652    #[tokio::test]
653    async fn should_list_products() {
654        let mock = get_mock("GET", "/products", 200, get_fixture::<Value>("products_list")).await;
655
656        let polar = get_test_polar(mock.uri());
657
658        let result = polar.list_products(&ListProductsParams::default()).await;
659
660        assert!(result.is_ok());
661    }
662
663    #[tokio::test]
664    async fn should_create_product() {
665        let mock = get_mock("POST", "/products", 201, get_fixture::<Value>("product")).await;
666
667        let polar = get_test_polar(mock.uri());
668
669        let params = get_fixture("product_params");
670
671        let result = polar.create_product(&params).await;
672
673        assert!(result.is_ok());
674    }
675
676    #[tokio::test]
677    async fn should_not_create_product() {
678        let mock = get_mock("POST", "/products", 422, get_fixture::<Value>("unprocessable_entity")).await;
679
680        let polar = get_test_polar(mock.uri());
681
682        let params = get_fixture("product_params");
683
684        let result = polar.create_product(&params).await;
685
686        assert!(result.is_err());
687    }
688
689    #[tokio::test]
690    async fn should_update_product() {
691        let product_id = Uuid::new_v4();
692        let mock = get_mock(
693            "PATCH",
694            &format!("/products/{}", product_id),
695            200,
696            get_fixture::<Value>("product"),
697        )
698        .await;
699
700        let polar = get_test_polar(mock.uri());
701
702        let params = get_fixture("update_product_params");
703
704        let result = polar.update_product(product_id, &params).await;
705
706        assert!(result.is_ok());
707    }
708
709    #[tokio::test]
710    async fn should_not_update_product() {
711        let product_id = Uuid::new_v4();
712        let mock = get_mock(
713            "PATCH",
714            &format!("/products/{}", product_id),
715            422,
716            get_fixture::<Value>("unprocessable_entity"),
717        )
718        .await;
719
720        let polar = get_test_polar(mock.uri());
721
722        let params = get_fixture("update_product_params");
723
724        let result = polar.update_product(product_id, &params).await;
725
726        assert!(result.is_err());
727    }
728
729    #[tokio::test]
730    async fn should_update_product_benefits() {
731        let product_id = Uuid::new_v4();
732        let mock = get_mock(
733            "PATCH",
734            &format!("/products/{}/benefits", product_id),
735            200,
736            get_fixture::<Value>("product"),
737        )
738        .await;
739
740        let polar = get_test_polar(mock.uri());
741
742        let result = polar.update_product_benefits(product_id, vec![Uuid::new_v4()]).await;
743
744        assert!(result.is_ok());
745    }
746
747    #[tokio::test]
748    async fn should_not_update_product_benefits() {
749        let product_id = Uuid::new_v4();
750        let mock = get_mock(
751            "PATCH",
752            &format!("/products/{}/benefits", product_id),
753            422,
754            get_fixture::<Value>("unprocessable_entity"),
755        )
756        .await;
757
758        let polar = get_test_polar(mock.uri());
759
760        let result = polar.update_product_benefits(product_id, vec![Uuid::new_v4()]).await;
761
762        assert!(result.is_err());
763    }
764
765    #[tokio::test]
766    async fn should_ingest_events() {
767        let mock = get_mock("POST", "/events/ingest", 201, get_fixture::<Value>("events_inserted")).await;
768        let polar = get_test_polar(mock.uri());
769
770        let result = polar.ingest_events(vec![]).await;
771
772        assert!(result.is_ok());
773    }
774
775    #[tokio::test]
776    async fn should_not_ingest_events() {
777        let mock = get_mock(
778            "POST",
779            "/events/ingest",
780            422,
781            get_fixture::<Value>("unprocessable_entity"),
782        )
783        .await;
784        let polar = get_test_polar(mock.uri());
785
786        let result = polar.ingest_events(vec![]).await;
787
788        assert!(result.is_err());
789    }
790
791    #[tokio::test]
792    async fn should_get_event() {
793        let event_id = Uuid::new_v4();
794        let mock = get_mock(
795            "GET",
796            &format!("/events/{}", event_id),
797            200,
798            get_fixture::<Value>("event"),
799        )
800        .await;
801
802        let polar = get_test_polar(mock.uri());
803
804        let result = polar.get_event(event_id).await;
805
806        assert!(result.is_ok());
807    }
808
809    #[tokio::test]
810    async fn should_not_get_event() {
811        let event_id = Uuid::new_v4();
812        let mock = get_mock(
813            "GET",
814            &format!("/events/{}", event_id),
815            404,
816            get_fixture::<Value>("not_found"),
817        )
818        .await;
819
820        let polar = get_test_polar(mock.uri());
821
822        let result = polar.get_event(event_id).await;
823
824        assert!(result.is_err());
825    }
826
827    #[tokio::test]
828    async fn should_create_meter() {
829        let mock = get_mock("POST", "/meters", 201, get_fixture::<Value>("meter")).await;
830
831        let polar = get_test_polar(mock.uri());
832
833        let params = get_fixture("meter_params");
834
835        let result = polar.create_meter(&params).await;
836
837        assert!(result.is_ok());
838    }
839
840    #[tokio::test]
841    async fn should_not_create_meter() {
842        let mock = get_mock("POST", "/meters", 422, get_fixture::<Value>("unprocessable_entity")).await;
843
844        let polar = get_test_polar(mock.uri());
845
846        let params = get_fixture("meter_params");
847
848        let result = polar.create_meter(&params).await;
849
850        assert!(result.is_err());
851    }
852
853    #[tokio::test]
854    async fn should_get_meter() {
855        let meter_id = Uuid::new_v4();
856        let mock = get_mock(
857            "GET",
858            &format!("/meters/{}", meter_id),
859            200,
860            get_fixture::<Value>("meter"),
861        )
862        .await;
863
864        let polar = get_test_polar(mock.uri());
865
866        let result = polar.get_meter(meter_id).await;
867
868        assert!(result.is_ok());
869    }
870
871    #[tokio::test]
872    async fn should_not_get_meter() {
873        let meter_id = Uuid::new_v4();
874        let mock = get_mock(
875            "GET",
876            &format!("/meters/{}", meter_id),
877            404,
878            get_fixture::<Value>("not_found"),
879        )
880        .await;
881
882        let polar = get_test_polar(mock.uri());
883
884        let result = polar.get_meter(meter_id).await;
885
886        assert!(result.is_err());
887    }
888
889    #[tokio::test]
890    async fn should_list_meters() {
891        let mock = get_mock("GET", "/meters", 200, get_fixture::<Value>("meters_list")).await;
892
893        let polar = get_test_polar(mock.uri());
894
895        let result = polar.list_meters(&ListMetersParams::default()).await;
896
897        assert!(result.is_ok());
898    }
899
900    #[tokio::test]
901    async fn should_update_meter() {
902        let meter_id = Uuid::new_v4();
903        let mock = get_mock(
904            "PATCH",
905            &format!("/meters/{}", meter_id),
906            200,
907            get_fixture::<Value>("meter"),
908        )
909        .await;
910
911        let polar = get_test_polar(mock.uri());
912
913        let params = get_fixture("update_meter_params");
914
915        let result = polar.update_meter(meter_id, &params).await;
916
917        assert!(result.is_ok());
918    }
919
920    #[tokio::test]
921    async fn should_not_update_meter() {
922        let meter_id = Uuid::new_v4();
923        let mock = get_mock(
924            "PATCH",
925            &format!("/meters/{}", meter_id),
926            422,
927            get_fixture::<Value>("unprocessable_entity"),
928        )
929        .await;
930
931        let polar = get_test_polar(mock.uri());
932
933        let params = get_fixture("update_meter_params");
934
935        let result = polar.update_meter(meter_id, &params).await;
936
937        assert!(result.is_err());
938    }
939
940    #[tokio::test]
941    async fn should_get_meter_quantities() {
942        let meter_id = Uuid::new_v4();
943        let mock = get_mock(
944            "GET",
945            &format!("/meters/{}/quantities", meter_id),
946            200,
947            get_fixture::<Value>("meter_quantities"),
948        )
949        .await;
950
951        let polar = get_test_polar(mock.uri());
952
953        let result = polar
954            .get_meter_quantities(meter_id, &MeterQuantitiesParams::default())
955            .await;
956
957        assert!(result.is_ok());
958    }
959
960    #[tokio::test]
961    async fn should_not_get_meter_quantities() {
962        let meter_id = Uuid::new_v4();
963        let mock = get_mock(
964            "GET",
965            &format!("/meters/{}/quantities", meter_id),
966            404,
967            get_fixture::<Value>("not_found"),
968        )
969        .await;
970
971        let polar = get_test_polar(mock.uri());
972
973        let result = polar
974            .get_meter_quantities(meter_id, &MeterQuantitiesParams::default())
975            .await;
976
977        assert!(result.is_err());
978    }
979}