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 pub async fn create_checkout_session(&self, params: &CheckoutSessionParams) -> PolarResult<CheckoutSession> {
193 self.post("checkouts", params).await
194 }
195
196 pub async fn get_checkout_session(&self, id: Uuid) -> PolarResult<CheckoutSession> {
202 self.get(&format!("checkouts/{id}")).await
203 }
204
205 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 pub async fn get_subscription(&self, id: Uuid) -> PolarResult<Subscription> {
223 self.get(&format!("subscriptions/{id}")).await
224 }
225
226 pub async fn list_subscriptions(&self, params: &ListSubscriptionsParams) -> PolarResult<Page<Subscription>> {
232 self.get_with_params("subscriptions", params).await
233 }
234
235 pub async fn update_subscription(&self, id: Uuid, params: &SubscriptionParams) -> PolarResult<Subscription> {
241 self.patch(&format!("subscriptions/{id}"), params).await
242 }
243
244 pub async fn revoke_subscription(&self, id: Uuid) -> PolarResult<Subscription> {
250 self.delete(&format!("subscriptions/{id}")).await
251 }
252
253 pub async fn get_product(&self, id: Uuid) -> PolarResult<Product> {
259 self.get(&format!("products/{id}")).await
260 }
261
262 pub async fn list_products(&self, params: &ListProductsParams) -> PolarResult<Page<Product>> {
268 self.get_with_params("products", params).await
269 }
270
271 pub async fn create_product(&self, params: &ProductParams) -> PolarResult<Product> {
277 self.post("products", params).await
278 }
279
280 pub async fn update_product(&self, id: Uuid, params: &UpdateProductParams) -> PolarResult<Product> {
286 self.patch(&format!("products/{id}"), params).await
287 }
288
289 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 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 pub async fn get_event(&self, id: Uuid) -> PolarResult<Event> {
316 self.get(&format!("events/{id}")).await
317 }
318
319 pub async fn create_meter(&self, params: &MeterParams) -> PolarResult<Meter> {
325 self.post("meters", params).await
326 }
327
328 pub async fn get_meter(&self, id: Uuid) -> PolarResult<Meter> {
334 self.get(&format!("meters/{id}")).await
335 }
336
337 pub async fn list_meters(&self, params: &ListMetersParams) -> PolarResult<Page<Meter>> {
343 self.get_with_params("meters", params).await
344 }
345
346 pub async fn update_meter(&self, id: Uuid, params: &UpdateMeterParams) -> PolarResult<Meter> {
352 self.patch(&format!("meters/{id}"), params).await
353 }
354
355 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(¶ms).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(¶ms).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, ¶ms).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, ¶ms).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(¶ms).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(¶ms).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, ¶ms).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, ¶ms).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(¶ms).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(¶ms).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, ¶ms).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, ¶ms).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}