1use serde::{Deserialize, Serialize};
2#[cfg(feature = "wasm")]
3use tsify::Tsify;
4
5#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
6#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
7#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
8#[serde(rename_all = "snake_case")]
9pub enum SendAccessTokenInvalidRequestError {
11 #[allow(missing_docs)]
12 SendIdRequired,
13
14 #[allow(missing_docs)]
15 PasswordHashB64Required,
16
17 #[allow(missing_docs)]
18 EmailRequired,
19
20 #[allow(missing_docs)]
21 EmailAndOtpRequiredOtpSent,
22
23 #[serde(other)]
25 Unknown,
26}
27
28#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
29#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
30#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
31#[serde(rename_all = "snake_case")]
32pub enum SendAccessTokenInvalidGrantError {
34 #[allow(missing_docs)]
35 SendIdInvalid,
36
37 #[allow(missing_docs)]
38 PasswordHashB64Invalid,
39
40 #[allow(missing_docs)]
41 EmailInvalid,
42
43 #[allow(missing_docs)]
44 OtpInvalid,
45
46 #[allow(missing_docs)]
47 OtpGenerationFailed,
48
49 #[serde(other)]
51 Unknown,
52}
53
54#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
55#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
56#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
57#[serde(rename_all = "snake_case")]
58#[serde(tag = "error")]
59pub enum SendAccessTokenApiErrorResponse {
63 InvalidRequest {
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 #[cfg_attr(feature = "wasm", tsify(optional))]
68 error_description: Option<String>,
70
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 #[cfg_attr(feature = "wasm", tsify(optional))]
73 send_access_error_type: Option<SendAccessTokenInvalidRequestError>,
75 },
76
77 InvalidGrant {
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 #[cfg_attr(feature = "wasm", tsify(optional))]
81 error_description: Option<String>,
83
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 #[cfg_attr(feature = "wasm", tsify(optional))]
86 send_access_error_type: Option<SendAccessTokenInvalidGrantError>,
88 },
89
90 InvalidClient {
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 #[cfg_attr(feature = "wasm", tsify(optional))]
94 error_description: Option<String>,
96 },
97
98 UnauthorizedClient {
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 #[cfg_attr(feature = "wasm", tsify(optional))]
102 error_description: Option<String>,
104 },
105
106 UnsupportedGrantType {
110 #[serde(default, skip_serializing_if = "Option::is_none")]
111 #[cfg_attr(feature = "wasm", tsify(optional))]
112 error_description: Option<String>,
114 },
115
116 InvalidScope {
118 #[serde(default, skip_serializing_if = "Option::is_none")]
119 #[cfg_attr(feature = "wasm", tsify(optional))]
120 error_description: Option<String>,
122 },
123
124 InvalidTarget {
127 #[serde(default, skip_serializing_if = "Option::is_none")]
128 #[cfg_attr(feature = "wasm", tsify(optional))]
129 error_description: Option<String>,
131 },
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 mod send_access_token_invalid_request_error_tests {
139 use serde_json::{Value, from_str, json, to_string, to_value};
140
141 use super::*;
142
143 #[test]
144 fn invalid_request_variants_serde_tests() {
145 let cases: &[(SendAccessTokenInvalidRequestError, &str)] = &[
147 (
148 SendAccessTokenInvalidRequestError::SendIdRequired,
149 "\"send_id_required\"",
150 ),
151 (
152 SendAccessTokenInvalidRequestError::PasswordHashB64Required,
153 "\"password_hash_b64_required\"",
154 ),
155 (
156 SendAccessTokenInvalidRequestError::EmailRequired,
157 "\"email_required\"",
158 ),
159 (
160 SendAccessTokenInvalidRequestError::EmailAndOtpRequiredOtpSent,
161 "\"email_and_otp_required_otp_sent\"",
162 ),
163 ];
164
165 for (expected_variant, send_access_error_type_json) in cases {
166 let error_from_send_access_error_type: SendAccessTokenInvalidRequestError =
168 from_str(send_access_error_type_json).unwrap();
169 assert_eq!(
170 &error_from_send_access_error_type, expected_variant,
171 "send_access_error_type should map to the expected variant"
172 );
173
174 let json_from_variant = to_string(expected_variant).unwrap();
176 assert_eq!(
177 json_from_variant, *send_access_error_type_json,
178 "serialization should emit the send_access_error_type_json"
179 );
180
181 let value_from_variant = to_value(expected_variant).unwrap();
184 assert_eq!(
185 value_from_variant,
186 Value::String(send_access_error_type_json.trim_matches('"').to_string()),
187 "serialization as value should match json generated from enum"
188 );
189
190 let round_tripped_code = to_string(&error_from_send_access_error_type).unwrap();
192 assert_eq!(
193 round_tripped_code, *send_access_error_type_json,
194 "round-trip should preserve the send_access_error_type_json"
195 );
196 }
197 }
198
199 #[test]
200 fn invalid_request_full_payload_with_both_fields_parses() {
201 let payload = json!({
202 "error": "invalid_request",
203 "error_description": "send_id is required.",
204 "send_access_error_type": "send_id_required"
205 })
206 .to_string();
207
208 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
209 match parsed {
210 SendAccessTokenApiErrorResponse::InvalidRequest {
211 error_description,
212 send_access_error_type,
213 } => {
214 assert_eq!(error_description.as_deref(), Some("send_id is required."));
215 assert_eq!(
216 send_access_error_type,
217 Some(SendAccessTokenInvalidRequestError::SendIdRequired)
218 );
219 }
220 _ => panic!("expected invalid_request"),
221 }
222 }
223
224 #[test]
225 fn invalid_request_payload_without_description_is_allowed() {
226 let payload = r#"
227 {
228 "error": "invalid_request",
229 "send_access_error_type": "email_required"
230 }"#;
231
232 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
233 match parsed {
234 SendAccessTokenApiErrorResponse::InvalidRequest {
235 error_description,
236 send_access_error_type,
237 } => {
238 assert!(error_description.is_none());
239 assert_eq!(
240 send_access_error_type,
241 Some(SendAccessTokenInvalidRequestError::EmailRequired)
242 );
243 }
244 _ => panic!("expected invalid_request"),
245 }
246 }
247
248 #[test]
249 fn invalid_request_unknown_code_maps_to_unknown() {
250 let payload = r#"
251 {
252 "error": "invalid_request",
253 "error_description": "something new",
254 "send_access_error_type": "brand_new_code"
255 }"#;
256
257 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
258 match parsed {
259 SendAccessTokenApiErrorResponse::InvalidRequest {
260 error_description,
261 send_access_error_type,
262 } => {
263 assert_eq!(error_description.as_deref(), Some("something new"));
264 assert_eq!(
265 send_access_error_type,
266 Some(SendAccessTokenInvalidRequestError::Unknown)
267 );
268 }
269 _ => panic!("expected invalid_request"),
270 }
271 }
272
273 #[test]
274 fn invalid_request_minimal_payload_is_allowed() {
275 let payload = r#"{ "error": "invalid_request" }"#;
276 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
277 match parsed {
278 SendAccessTokenApiErrorResponse::InvalidRequest {
279 error_description,
280 send_access_error_type,
281 } => {
282 assert!(error_description.is_none());
283 assert!(send_access_error_type.is_none());
284 }
285 _ => panic!("expected invalid_request"),
286 }
287 }
288
289 #[test]
290 fn invalid_request_null_fields_become_none() {
291 let payload = r#"
292 {
293 "error": "invalid_request",
294 "error_description": null,
295 "send_access_error_type": null
296 }"#;
297
298 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
299 match parsed {
300 SendAccessTokenApiErrorResponse::InvalidRequest {
301 error_description,
302 send_access_error_type,
303 } => {
304 assert!(error_description.is_none());
305 assert!(send_access_error_type.is_none());
306 }
307 _ => panic!("expected invalid_request"),
308 }
309 }
310 }
311
312 mod send_access_token_invalid_grant_error_tests {
313 use serde_json::{Value, from_str, json, to_string, to_value};
314
315 use super::*;
316
317 #[test]
318 fn invalid_grant_variants_serde_tests() {
319 let cases: &[(SendAccessTokenInvalidGrantError, &str)] = &[
321 (
322 SendAccessTokenInvalidGrantError::SendIdInvalid,
323 "\"send_id_invalid\"",
324 ),
325 (
326 SendAccessTokenInvalidGrantError::PasswordHashB64Invalid,
327 "\"password_hash_b64_invalid\"",
328 ),
329 (
330 SendAccessTokenInvalidGrantError::EmailInvalid,
331 "\"email_invalid\"",
332 ),
333 (
334 SendAccessTokenInvalidGrantError::OtpInvalid,
335 "\"otp_invalid\"",
336 ),
337 (
338 SendAccessTokenInvalidGrantError::OtpGenerationFailed,
339 "\"otp_generation_failed\"",
340 ),
341 ];
342
343 for (expected_variant, send_access_error_type_json) in cases {
344 let error_from_send_access_error_type: SendAccessTokenInvalidGrantError =
346 from_str(send_access_error_type_json).unwrap();
347 assert_eq!(
348 &error_from_send_access_error_type, expected_variant,
349 "send_access_error_type should map to the expected variant"
350 );
351
352 let json_from_variant = to_string(expected_variant).unwrap();
354 assert_eq!(
355 json_from_variant, *send_access_error_type_json,
356 "serialization should emit the send_access_error_type_json"
357 );
358
359 let value_from_variant = to_value(expected_variant).unwrap();
361 assert_eq!(
362 value_from_variant,
363 Value::String(send_access_error_type_json.trim_matches('"').to_string()),
364 "serialization as value should match json generated from enum"
365 );
366
367 let round_tripped_code = to_string(&error_from_send_access_error_type).unwrap();
369 assert_eq!(
370 round_tripped_code, *send_access_error_type_json,
371 "round-trip should preserve the send_access_error_type_json"
372 );
373 }
374 }
375
376 #[test]
377 fn invalid_grant_full_payload_with_both_fields_parses() {
378 let payload = json!({
379 "error": "invalid_grant",
380 "error_description": "password_hash_b64 is invalid.",
381 "send_access_error_type": "password_hash_b64_invalid"
382 })
383 .to_string();
384
385 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
386 match parsed {
387 SendAccessTokenApiErrorResponse::InvalidGrant {
388 error_description,
389 send_access_error_type,
390 } => {
391 assert_eq!(
392 error_description.as_deref(),
393 Some("password_hash_b64 is invalid.")
394 );
395 assert_eq!(
396 send_access_error_type,
397 Some(SendAccessTokenInvalidGrantError::PasswordHashB64Invalid)
398 );
399 }
400 _ => panic!("expected invalid_grant"),
401 }
402 }
403
404 #[test]
405 fn invalid_grant_payload_without_description_is_allowed() {
406 let payload = r#"
407 {
408 "error": "invalid_grant",
409 "send_access_error_type": "otp_invalid"
410 }"#;
411
412 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
413 match parsed {
414 SendAccessTokenApiErrorResponse::InvalidGrant {
415 error_description,
416 send_access_error_type,
417 } => {
418 assert!(error_description.is_none());
419 assert_eq!(
420 send_access_error_type,
421 Some(SendAccessTokenInvalidGrantError::OtpInvalid)
422 );
423 }
424 _ => panic!("expected invalid_grant"),
425 }
426 }
427
428 #[test]
429 fn invalid_grant_unknown_code_maps_to_unknown() {
430 let payload = r#"
431 {
432 "error": "invalid_grant",
433 "error_description": "new server-side reason",
434 "send_access_error_type": "brand_new_grant_code"
435 }"#;
436
437 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
438 match parsed {
439 SendAccessTokenApiErrorResponse::InvalidGrant {
440 error_description,
441 send_access_error_type,
442 } => {
443 assert_eq!(error_description.as_deref(), Some("new server-side reason"));
444 assert_eq!(
445 send_access_error_type,
446 Some(SendAccessTokenInvalidGrantError::Unknown)
447 );
448 }
449 _ => panic!("expected invalid_grant"),
450 }
451 }
452
453 #[test]
454 fn invalid_grant_minimal_payload_is_allowed() {
455 let payload = r#"{ "error": "invalid_grant" }"#;
456 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
457 match parsed {
458 SendAccessTokenApiErrorResponse::InvalidGrant {
459 error_description,
460 send_access_error_type,
461 } => {
462 assert!(error_description.is_none());
463 assert!(send_access_error_type.is_none());
464 }
465 _ => panic!("expected invalid_grant"),
466 }
467 }
468
469 #[test]
470 fn invalid_grant_null_fields_become_none() {
471 let payload = r#"
472 {
473 "error": "invalid_grant",
474 "error_description": null,
475 "send_access_error_type": null
476 }"#;
477
478 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
479 match parsed {
480 SendAccessTokenApiErrorResponse::InvalidGrant {
481 error_description,
482 send_access_error_type,
483 } => {
484 assert!(error_description.is_none());
485 assert!(send_access_error_type.is_none());
486 }
487 _ => panic!("expected invalid_grant"),
488 }
489 }
490 }
491
492 mod send_access_token_invalid_client_error_tests {
493 use serde_json::{from_str, json, to_value};
494
495 use super::*;
496
497 #[test]
498 fn invalid_client_full_payload_with_description_parses() {
499 let payload = json!({
500 "error": "invalid_client",
501 "error_description": "Invalid client credentials."
502 })
503 .to_string();
504
505 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
506 match parsed {
507 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
508 assert_eq!(
509 error_description.as_deref(),
510 Some("Invalid client credentials.")
511 );
512 }
513 _ => panic!("expected invalid_client"),
514 }
515 }
516
517 #[test]
518 fn invalid_client_without_description_is_allowed() {
519 let payload = r#"{ "error": "invalid_client" }"#;
520
521 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
522 match parsed {
523 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
524 assert!(error_description.is_none());
525 }
526 _ => panic!("expected invalid_client"),
527 }
528 }
529
530 #[test]
531 fn invalid_client_serializes_back() {
532 let value = SendAccessTokenApiErrorResponse::InvalidClient {
533 error_description: Some("Invalid client credentials.".into()),
534 };
535 let j = to_value(value).unwrap();
536 assert_eq!(
537 j,
538 json!({
539 "error": "invalid_client",
540 "error_description": "Invalid client credentials."
541 })
542 );
543 }
544
545 #[test]
546 fn invalid_client_minimal_payload_is_allowed() {
547 let payload = r#"{ "error": "invalid_client" }"#;
548 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
549 match parsed {
550 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
551 assert!(error_description.is_none());
552 }
553 _ => panic!("expected invalid_client"),
554 }
555 }
556
557 #[test]
558 fn invalid_client_null_description_becomes_none() {
559 let payload = r#"
560 {
561 "error": "invalid_client",
562 "error_description": null
563 }"#;
564
565 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
566 match parsed {
567 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
568 assert!(error_description.is_none());
569 }
570 _ => panic!("expected invalid_client"),
571 }
572 }
573
574 #[test]
575 fn invalid_client_ignores_send_access_error_type_and_extra_fields() {
576 let payload = r#"
577 {
578 "error": "invalid_client",
579 "send_access_error_type": "should_be_ignored",
580 "extra_field": 123,
581 "error_description": "desc"
582 }"#;
583
584 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
585 match parsed {
586 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
587 assert_eq!(error_description.as_deref(), Some("desc"));
588 }
589 _ => panic!("expected invalid_client"),
590 }
591 }
592 }
593
594 mod send_access_token_unauthorized_client_error_tests {
595 use serde_json::{from_str, json, to_value};
596
597 use super::*;
598
599 #[test]
600 fn unauthorized_client_full_payload_with_description_parses() {
601 let payload = json!({
602 "error": "unauthorized_client",
603 "error_description": "Client not permitted to use this grant."
604 })
605 .to_string();
606
607 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
608 match parsed {
609 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
610 assert_eq!(
611 error_description.as_deref(),
612 Some("Client not permitted to use this grant.")
613 );
614 }
615 _ => panic!("expected unauthorized_client"),
616 }
617 }
618
619 #[test]
620 fn unauthorized_client_without_description_is_allowed() {
621 let payload = r#"{ "error": "unauthorized_client" }"#;
622
623 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
624 match parsed {
625 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
626 assert!(error_description.is_none());
627 }
628 _ => panic!("expected unauthorized_client"),
629 }
630 }
631
632 #[test]
633 fn unauthorized_client_serializes_back() {
634 let value = SendAccessTokenApiErrorResponse::UnauthorizedClient {
635 error_description: None,
636 };
637 let j = to_value(value).unwrap();
638 assert_eq!(j, json!({ "error": "unauthorized_client" }));
639 }
640
641 #[test]
642 fn unauthorized_client_minimal_payload_is_allowed() {
643 let payload = r#"{ "error": "unauthorized_client" }"#;
644 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
645 match parsed {
646 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
647 assert!(error_description.is_none());
648 }
649 _ => panic!("expected unauthorized_client"),
650 }
651 }
652
653 #[test]
654 fn unauthorized_client_null_description_becomes_none() {
655 let payload = r#"
656 {
657 "error": "unauthorized_client",
658 "error_description": null
659 }"#;
660
661 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
662 match parsed {
663 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
664 assert!(error_description.is_none());
665 }
666 _ => panic!("expected unauthorized_client"),
667 }
668 }
669
670 #[test]
671 fn unauthorized_client_ignores_send_access_error_type_and_extra_fields() {
672 let payload = r#"
673 {
674 "error": "unauthorized_client",
675 "send_access_error_type": "should_be_ignored",
676 "extra_field": true
677 }"#;
678
679 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
680 match parsed {
681 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
682 assert!(error_description.is_none());
683 }
684 _ => panic!("expected unauthorized_client"),
685 }
686 }
687 }
688
689 mod send_access_token_unsupported_grant_type_error_tests {
690 use serde_json::{from_str, json, to_value};
691
692 use super::*;
693
694 #[test]
695 fn unsupported_grant_type_full_payload_with_description_parses() {
696 let payload = json!({
697 "error": "unsupported_grant_type",
698 "error_description": "This grant type is not enabled."
699 })
700 .to_string();
701
702 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
703 match parsed {
704 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
705 assert_eq!(
706 error_description.as_deref(),
707 Some("This grant type is not enabled.")
708 );
709 }
710 _ => panic!("expected unsupported_grant_type"),
711 }
712 }
713
714 #[test]
715 fn unsupported_grant_type_without_description_is_allowed() {
716 let payload = r#"{ "error": "unsupported_grant_type" }"#;
717
718 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
719 match parsed {
720 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
721 assert!(error_description.is_none());
722 }
723 _ => panic!("expected unsupported_grant_type"),
724 }
725 }
726
727 #[test]
728 fn unsupported_grant_type_serializes_back() {
729 let value = SendAccessTokenApiErrorResponse::UnsupportedGrantType {
730 error_description: Some("Disabled by feature flag".into()),
731 };
732 let j = to_value(value).unwrap();
733 assert_eq!(
734 j,
735 json!({
736 "error": "unsupported_grant_type",
737 "error_description": "Disabled by feature flag"
738 })
739 );
740 }
741
742 #[test]
743 fn unsupported_grant_type_minimal_payload_is_allowed() {
744 let payload = r#"{ "error": "unsupported_grant_type" }"#;
745 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
746 match parsed {
747 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
748 assert!(error_description.is_none());
749 }
750 _ => panic!("expected unsupported_grant_type"),
751 }
752 }
753
754 #[test]
755 fn unsupported_grant_type_null_description_becomes_none() {
756 let payload = r#"
757 {
758 "error": "unsupported_grant_type",
759 "error_description": null
760 }"#;
761
762 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
763 match parsed {
764 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
765 assert!(error_description.is_none());
766 }
767 _ => panic!("expected unsupported_grant_type"),
768 }
769 }
770
771 #[test]
772 fn unsupported_grant_type_ignores_send_access_error_type_and_extra_fields() {
773 let payload = r#"
774 {
775 "error": "unsupported_grant_type",
776 "send_access_error_type": "should_be_ignored",
777 "extra_field": "noise"
778 }"#;
779
780 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
781 match parsed {
782 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
783 assert!(error_description.is_none());
784 }
785 _ => panic!("expected unsupported_grant_type"),
786 }
787 }
788 }
789
790 mod send_access_token_invalid_scope_error_tests {
791 use serde_json::{from_str, json, to_value};
792
793 use super::*;
794
795 #[test]
796 fn invalid_scope_full_payload_with_description_parses() {
797 let payload = json!({
798 "error": "invalid_scope",
799 "error_description": "Requested scope is not allowed."
800 })
801 .to_string();
802
803 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
804 match parsed {
805 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
806 assert_eq!(
807 error_description.as_deref(),
808 Some("Requested scope is not allowed.")
809 );
810 }
811 _ => panic!("expected invalid_scope"),
812 }
813 }
814
815 #[test]
816 fn invalid_scope_without_description_is_allowed() {
817 let payload = r#"{ "error": "invalid_scope" }"#;
818
819 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
820 match parsed {
821 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
822 assert!(error_description.is_none());
823 }
824 _ => panic!("expected invalid_scope"),
825 }
826 }
827
828 #[test]
829 fn invalid_scope_serializes_back() {
830 let value = SendAccessTokenApiErrorResponse::InvalidScope {
831 error_description: None,
832 };
833 let j = to_value(value).unwrap();
834 assert_eq!(j, json!({ "error": "invalid_scope" }));
835 }
836
837 #[test]
838 fn invalid_scope_minimal_payload_is_allowed() {
839 let payload = r#"{ "error": "invalid_scope" }"#;
840 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
841 match parsed {
842 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
843 assert!(error_description.is_none());
844 }
845 _ => panic!("expected invalid_scope"),
846 }
847 }
848
849 #[test]
850 fn invalid_scope_null_description_becomes_none() {
851 let payload = r#"
852 {
853 "error": "invalid_scope",
854 "error_description": null
855 }"#;
856
857 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
858 match parsed {
859 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
860 assert!(error_description.is_none());
861 }
862 _ => panic!("expected invalid_scope"),
863 }
864 }
865
866 #[test]
867 fn invalid_scope_ignores_send_access_error_type_and_extra_fields() {
868 let payload = r#"
869 {
870 "error": "invalid_scope",
871 "send_access_error_type": "should_be_ignored",
872 "extra_field": [1,2,3]
873 }"#;
874
875 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
876 match parsed {
877 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
878 assert!(error_description.is_none());
879 }
880 _ => panic!("expected invalid_scope"),
881 }
882 }
883 }
884
885 mod send_access_token_invalid_target_error_tests {
886 use serde_json::{from_str, json, to_value};
887
888 use super::*;
889
890 #[test]
891 fn invalid_target_full_payload_with_description_parses() {
892 let payload = json!({
893 "error": "invalid_target",
894 "error_description": "Unknown or disallowed resource indicator."
895 })
896 .to_string();
897
898 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
899 match parsed {
900 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
901 assert_eq!(
902 error_description.as_deref(),
903 Some("Unknown or disallowed resource indicator.")
904 );
905 }
906 _ => panic!("expected invalid_target"),
907 }
908 }
909
910 #[test]
911 fn invalid_target_without_description_is_allowed() {
912 let payload = r#"{ "error": "invalid_target" }"#;
913
914 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
915 match parsed {
916 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
917 assert!(error_description.is_none());
918 }
919 _ => panic!("expected invalid_target"),
920 }
921 }
922
923 #[test]
924 fn invalid_target_serializes_back() {
925 let value = SendAccessTokenApiErrorResponse::InvalidTarget {
926 error_description: Some("Bad resource parameter".into()),
927 };
928 let j = to_value(value).unwrap();
929 assert_eq!(
930 j,
931 json!({
932 "error": "invalid_target",
933 "error_description": "Bad resource parameter"
934 })
935 );
936 }
937
938 #[test]
939 fn invalid_target_minimal_payload_is_allowed() {
940 let payload = r#"{ "error": "invalid_target" }"#;
941 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
942 match parsed {
943 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
944 assert!(error_description.is_none());
945 }
946 _ => panic!("expected invalid_target"),
947 }
948 }
949
950 #[test]
951 fn invalid_target_null_description_becomes_none() {
952 let payload = r#"
953 {
954 "error": "invalid_target",
955 "error_description": null
956 }"#;
957
958 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
959 match parsed {
960 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
961 assert!(error_description.is_none());
962 }
963 _ => panic!("expected invalid_target"),
964 }
965 }
966
967 #[test]
968 fn invalid_target_ignores_send_access_error_type_and_extra_fields() {
969 let payload = r#"
970 {
971 "error": "invalid_target",
972 "send_access_error_type": "should_be_ignored",
973 "extra_field": {"k":"v"}
974 }"#;
975
976 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
977 match parsed {
978 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
979 assert!(error_description.is_none());
980 }
981 _ => panic!("expected invalid_target"),
982 }
983 }
984 }
985
986 #[test]
987 fn unknown_top_level_error_rejects() {
988 let payload = r#"{ "error": "totally_new_error" }"#;
989 let err = serde_json::from_str::<SendAccessTokenApiErrorResponse>(payload).unwrap_err();
990 let _ = err;
991 }
992}