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