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#[serde(rename_all = "snake_case")]
8pub enum SendAccessTokenInvalidRequestError {
10 #[allow(missing_docs)]
11 SendIdRequired,
12
13 #[allow(missing_docs)]
14 PasswordHashB64Required,
15
16 #[allow(missing_docs)]
17 EmailRequired,
18
19 #[allow(missing_docs)]
20 EmailAndOtpRequiredOtpSent,
21
22 #[serde(other)]
24 Unknown,
25}
26
27#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
28#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
29#[serde(rename_all = "snake_case")]
30pub enum SendAccessTokenInvalidGrantError {
32 #[allow(missing_docs)]
33 SendIdInvalid,
34
35 #[allow(missing_docs)]
36 PasswordHashB64Invalid,
37
38 #[allow(missing_docs)]
39 EmailInvalid,
40
41 #[allow(missing_docs)]
42 OtpInvalid,
43
44 #[allow(missing_docs)]
45 OtpGenerationFailed,
46
47 #[serde(other)]
49 Unknown,
50}
51
52#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
53#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
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::EmailAndOtpRequiredOtpSent,
158 "\"email_and_otp_required_otp_sent\"",
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::EmailInvalid,
328 "\"email_invalid\"",
329 ),
330 (
331 SendAccessTokenInvalidGrantError::OtpInvalid,
332 "\"otp_invalid\"",
333 ),
334 (
335 SendAccessTokenInvalidGrantError::OtpGenerationFailed,
336 "\"otp_generation_failed\"",
337 ),
338 ];
339
340 for (expected_variant, send_access_error_type_json) in cases {
341 let error_from_send_access_error_type: SendAccessTokenInvalidGrantError =
343 from_str(send_access_error_type_json).unwrap();
344 assert_eq!(
345 &error_from_send_access_error_type, expected_variant,
346 "send_access_error_type should map to the expected variant"
347 );
348
349 let json_from_variant = to_string(expected_variant).unwrap();
351 assert_eq!(
352 json_from_variant, *send_access_error_type_json,
353 "serialization should emit the send_access_error_type_json"
354 );
355
356 let value_from_variant = to_value(expected_variant).unwrap();
358 assert_eq!(
359 value_from_variant,
360 Value::String(send_access_error_type_json.trim_matches('"').to_string()),
361 "serialization as value should match json generated from enum"
362 );
363
364 let round_tripped_code = to_string(&error_from_send_access_error_type).unwrap();
366 assert_eq!(
367 round_tripped_code, *send_access_error_type_json,
368 "round-trip should preserve the send_access_error_type_json"
369 );
370 }
371 }
372
373 #[test]
374 fn invalid_grant_full_payload_with_both_fields_parses() {
375 let payload = json!({
376 "error": "invalid_grant",
377 "error_description": "password_hash_b64 is invalid.",
378 "send_access_error_type": "password_hash_b64_invalid"
379 })
380 .to_string();
381
382 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
383 match parsed {
384 SendAccessTokenApiErrorResponse::InvalidGrant {
385 error_description,
386 send_access_error_type,
387 } => {
388 assert_eq!(
389 error_description.as_deref(),
390 Some("password_hash_b64 is invalid.")
391 );
392 assert_eq!(
393 send_access_error_type,
394 Some(SendAccessTokenInvalidGrantError::PasswordHashB64Invalid)
395 );
396 }
397 _ => panic!("expected invalid_grant"),
398 }
399 }
400
401 #[test]
402 fn invalid_grant_payload_without_description_is_allowed() {
403 let payload = r#"
404 {
405 "error": "invalid_grant",
406 "send_access_error_type": "otp_invalid"
407 }"#;
408
409 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
410 match parsed {
411 SendAccessTokenApiErrorResponse::InvalidGrant {
412 error_description,
413 send_access_error_type,
414 } => {
415 assert!(error_description.is_none());
416 assert_eq!(
417 send_access_error_type,
418 Some(SendAccessTokenInvalidGrantError::OtpInvalid)
419 );
420 }
421 _ => panic!("expected invalid_grant"),
422 }
423 }
424
425 #[test]
426 fn invalid_grant_unknown_code_maps_to_unknown() {
427 let payload = r#"
428 {
429 "error": "invalid_grant",
430 "error_description": "new server-side reason",
431 "send_access_error_type": "brand_new_grant_code"
432 }"#;
433
434 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
435 match parsed {
436 SendAccessTokenApiErrorResponse::InvalidGrant {
437 error_description,
438 send_access_error_type,
439 } => {
440 assert_eq!(error_description.as_deref(), Some("new server-side reason"));
441 assert_eq!(
442 send_access_error_type,
443 Some(SendAccessTokenInvalidGrantError::Unknown)
444 );
445 }
446 _ => panic!("expected invalid_grant"),
447 }
448 }
449
450 #[test]
451 fn invalid_grant_minimal_payload_is_allowed() {
452 let payload = r#"{ "error": "invalid_grant" }"#;
453 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
454 match parsed {
455 SendAccessTokenApiErrorResponse::InvalidGrant {
456 error_description,
457 send_access_error_type,
458 } => {
459 assert!(error_description.is_none());
460 assert!(send_access_error_type.is_none());
461 }
462 _ => panic!("expected invalid_grant"),
463 }
464 }
465
466 #[test]
467 fn invalid_grant_null_fields_become_none() {
468 let payload = r#"
469 {
470 "error": "invalid_grant",
471 "error_description": null,
472 "send_access_error_type": null
473 }"#;
474
475 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
476 match parsed {
477 SendAccessTokenApiErrorResponse::InvalidGrant {
478 error_description,
479 send_access_error_type,
480 } => {
481 assert!(error_description.is_none());
482 assert!(send_access_error_type.is_none());
483 }
484 _ => panic!("expected invalid_grant"),
485 }
486 }
487 }
488
489 mod send_access_token_invalid_client_error_tests {
490 use serde_json::{from_str, json, to_value};
491
492 use super::*;
493
494 #[test]
495 fn invalid_client_full_payload_with_description_parses() {
496 let payload = json!({
497 "error": "invalid_client",
498 "error_description": "Invalid client credentials."
499 })
500 .to_string();
501
502 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
503 match parsed {
504 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
505 assert_eq!(
506 error_description.as_deref(),
507 Some("Invalid client credentials.")
508 );
509 }
510 _ => panic!("expected invalid_client"),
511 }
512 }
513
514 #[test]
515 fn invalid_client_without_description_is_allowed() {
516 let payload = r#"{ "error": "invalid_client" }"#;
517
518 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
519 match parsed {
520 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
521 assert!(error_description.is_none());
522 }
523 _ => panic!("expected invalid_client"),
524 }
525 }
526
527 #[test]
528 fn invalid_client_serializes_back() {
529 let value = SendAccessTokenApiErrorResponse::InvalidClient {
530 error_description: Some("Invalid client credentials.".into()),
531 };
532 let j = to_value(value).unwrap();
533 assert_eq!(
534 j,
535 json!({
536 "error": "invalid_client",
537 "error_description": "Invalid client credentials."
538 })
539 );
540 }
541
542 #[test]
543 fn invalid_client_minimal_payload_is_allowed() {
544 let payload = r#"{ "error": "invalid_client" }"#;
545 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
546 match parsed {
547 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
548 assert!(error_description.is_none());
549 }
550 _ => panic!("expected invalid_client"),
551 }
552 }
553
554 #[test]
555 fn invalid_client_null_description_becomes_none() {
556 let payload = r#"
557 {
558 "error": "invalid_client",
559 "error_description": null
560 }"#;
561
562 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
563 match parsed {
564 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
565 assert!(error_description.is_none());
566 }
567 _ => panic!("expected invalid_client"),
568 }
569 }
570
571 #[test]
572 fn invalid_client_ignores_send_access_error_type_and_extra_fields() {
573 let payload = r#"
574 {
575 "error": "invalid_client",
576 "send_access_error_type": "should_be_ignored",
577 "extra_field": 123,
578 "error_description": "desc"
579 }"#;
580
581 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
582 match parsed {
583 SendAccessTokenApiErrorResponse::InvalidClient { error_description } => {
584 assert_eq!(error_description.as_deref(), Some("desc"));
585 }
586 _ => panic!("expected invalid_client"),
587 }
588 }
589 }
590
591 mod send_access_token_unauthorized_client_error_tests {
592 use serde_json::{from_str, json, to_value};
593
594 use super::*;
595
596 #[test]
597 fn unauthorized_client_full_payload_with_description_parses() {
598 let payload = json!({
599 "error": "unauthorized_client",
600 "error_description": "Client not permitted to use this grant."
601 })
602 .to_string();
603
604 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
605 match parsed {
606 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
607 assert_eq!(
608 error_description.as_deref(),
609 Some("Client not permitted to use this grant.")
610 );
611 }
612 _ => panic!("expected unauthorized_client"),
613 }
614 }
615
616 #[test]
617 fn unauthorized_client_without_description_is_allowed() {
618 let payload = r#"{ "error": "unauthorized_client" }"#;
619
620 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
621 match parsed {
622 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
623 assert!(error_description.is_none());
624 }
625 _ => panic!("expected unauthorized_client"),
626 }
627 }
628
629 #[test]
630 fn unauthorized_client_serializes_back() {
631 let value = SendAccessTokenApiErrorResponse::UnauthorizedClient {
632 error_description: None,
633 };
634 let j = to_value(value).unwrap();
635 assert_eq!(j, json!({ "error": "unauthorized_client" }));
636 }
637
638 #[test]
639 fn unauthorized_client_minimal_payload_is_allowed() {
640 let payload = r#"{ "error": "unauthorized_client" }"#;
641 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
642 match parsed {
643 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
644 assert!(error_description.is_none());
645 }
646 _ => panic!("expected unauthorized_client"),
647 }
648 }
649
650 #[test]
651 fn unauthorized_client_null_description_becomes_none() {
652 let payload = r#"
653 {
654 "error": "unauthorized_client",
655 "error_description": null
656 }"#;
657
658 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
659 match parsed {
660 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
661 assert!(error_description.is_none());
662 }
663 _ => panic!("expected unauthorized_client"),
664 }
665 }
666
667 #[test]
668 fn unauthorized_client_ignores_send_access_error_type_and_extra_fields() {
669 let payload = r#"
670 {
671 "error": "unauthorized_client",
672 "send_access_error_type": "should_be_ignored",
673 "extra_field": true
674 }"#;
675
676 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
677 match parsed {
678 SendAccessTokenApiErrorResponse::UnauthorizedClient { error_description } => {
679 assert!(error_description.is_none());
680 }
681 _ => panic!("expected unauthorized_client"),
682 }
683 }
684 }
685
686 mod send_access_token_unsupported_grant_type_error_tests {
687 use serde_json::{from_str, json, to_value};
688
689 use super::*;
690
691 #[test]
692 fn unsupported_grant_type_full_payload_with_description_parses() {
693 let payload = json!({
694 "error": "unsupported_grant_type",
695 "error_description": "This grant type is not enabled."
696 })
697 .to_string();
698
699 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
700 match parsed {
701 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
702 assert_eq!(
703 error_description.as_deref(),
704 Some("This grant type is not enabled.")
705 );
706 }
707 _ => panic!("expected unsupported_grant_type"),
708 }
709 }
710
711 #[test]
712 fn unsupported_grant_type_without_description_is_allowed() {
713 let payload = r#"{ "error": "unsupported_grant_type" }"#;
714
715 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
716 match parsed {
717 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
718 assert!(error_description.is_none());
719 }
720 _ => panic!("expected unsupported_grant_type"),
721 }
722 }
723
724 #[test]
725 fn unsupported_grant_type_serializes_back() {
726 let value = SendAccessTokenApiErrorResponse::UnsupportedGrantType {
727 error_description: Some("Disabled by feature flag".into()),
728 };
729 let j = to_value(value).unwrap();
730 assert_eq!(
731 j,
732 json!({
733 "error": "unsupported_grant_type",
734 "error_description": "Disabled by feature flag"
735 })
736 );
737 }
738
739 #[test]
740 fn unsupported_grant_type_minimal_payload_is_allowed() {
741 let payload = r#"{ "error": "unsupported_grant_type" }"#;
742 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
743 match parsed {
744 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
745 assert!(error_description.is_none());
746 }
747 _ => panic!("expected unsupported_grant_type"),
748 }
749 }
750
751 #[test]
752 fn unsupported_grant_type_null_description_becomes_none() {
753 let payload = r#"
754 {
755 "error": "unsupported_grant_type",
756 "error_description": null
757 }"#;
758
759 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
760 match parsed {
761 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
762 assert!(error_description.is_none());
763 }
764 _ => panic!("expected unsupported_grant_type"),
765 }
766 }
767
768 #[test]
769 fn unsupported_grant_type_ignores_send_access_error_type_and_extra_fields() {
770 let payload = r#"
771 {
772 "error": "unsupported_grant_type",
773 "send_access_error_type": "should_be_ignored",
774 "extra_field": "noise"
775 }"#;
776
777 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
778 match parsed {
779 SendAccessTokenApiErrorResponse::UnsupportedGrantType { error_description } => {
780 assert!(error_description.is_none());
781 }
782 _ => panic!("expected unsupported_grant_type"),
783 }
784 }
785 }
786
787 mod send_access_token_invalid_scope_error_tests {
788 use serde_json::{from_str, json, to_value};
789
790 use super::*;
791
792 #[test]
793 fn invalid_scope_full_payload_with_description_parses() {
794 let payload = json!({
795 "error": "invalid_scope",
796 "error_description": "Requested scope is not allowed."
797 })
798 .to_string();
799
800 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
801 match parsed {
802 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
803 assert_eq!(
804 error_description.as_deref(),
805 Some("Requested scope is not allowed.")
806 );
807 }
808 _ => panic!("expected invalid_scope"),
809 }
810 }
811
812 #[test]
813 fn invalid_scope_without_description_is_allowed() {
814 let payload = r#"{ "error": "invalid_scope" }"#;
815
816 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
817 match parsed {
818 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
819 assert!(error_description.is_none());
820 }
821 _ => panic!("expected invalid_scope"),
822 }
823 }
824
825 #[test]
826 fn invalid_scope_serializes_back() {
827 let value = SendAccessTokenApiErrorResponse::InvalidScope {
828 error_description: None,
829 };
830 let j = to_value(value).unwrap();
831 assert_eq!(j, json!({ "error": "invalid_scope" }));
832 }
833
834 #[test]
835 fn invalid_scope_minimal_payload_is_allowed() {
836 let payload = r#"{ "error": "invalid_scope" }"#;
837 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
838 match parsed {
839 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
840 assert!(error_description.is_none());
841 }
842 _ => panic!("expected invalid_scope"),
843 }
844 }
845
846 #[test]
847 fn invalid_scope_null_description_becomes_none() {
848 let payload = r#"
849 {
850 "error": "invalid_scope",
851 "error_description": null
852 }"#;
853
854 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
855 match parsed {
856 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
857 assert!(error_description.is_none());
858 }
859 _ => panic!("expected invalid_scope"),
860 }
861 }
862
863 #[test]
864 fn invalid_scope_ignores_send_access_error_type_and_extra_fields() {
865 let payload = r#"
866 {
867 "error": "invalid_scope",
868 "send_access_error_type": "should_be_ignored",
869 "extra_field": [1,2,3]
870 }"#;
871
872 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
873 match parsed {
874 SendAccessTokenApiErrorResponse::InvalidScope { error_description } => {
875 assert!(error_description.is_none());
876 }
877 _ => panic!("expected invalid_scope"),
878 }
879 }
880 }
881
882 mod send_access_token_invalid_target_error_tests {
883 use serde_json::{from_str, json, to_value};
884
885 use super::*;
886
887 #[test]
888 fn invalid_target_full_payload_with_description_parses() {
889 let payload = json!({
890 "error": "invalid_target",
891 "error_description": "Unknown or disallowed resource indicator."
892 })
893 .to_string();
894
895 let parsed: SendAccessTokenApiErrorResponse = from_str(&payload).unwrap();
896 match parsed {
897 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
898 assert_eq!(
899 error_description.as_deref(),
900 Some("Unknown or disallowed resource indicator.")
901 );
902 }
903 _ => panic!("expected invalid_target"),
904 }
905 }
906
907 #[test]
908 fn invalid_target_without_description_is_allowed() {
909 let payload = r#"{ "error": "invalid_target" }"#;
910
911 let parsed: SendAccessTokenApiErrorResponse = serde_json::from_str(payload).unwrap();
912 match parsed {
913 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
914 assert!(error_description.is_none());
915 }
916 _ => panic!("expected invalid_target"),
917 }
918 }
919
920 #[test]
921 fn invalid_target_serializes_back() {
922 let value = SendAccessTokenApiErrorResponse::InvalidTarget {
923 error_description: Some("Bad resource parameter".into()),
924 };
925 let j = to_value(value).unwrap();
926 assert_eq!(
927 j,
928 json!({
929 "error": "invalid_target",
930 "error_description": "Bad resource parameter"
931 })
932 );
933 }
934
935 #[test]
936 fn invalid_target_minimal_payload_is_allowed() {
937 let payload = r#"{ "error": "invalid_target" }"#;
938 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
939 match parsed {
940 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
941 assert!(error_description.is_none());
942 }
943 _ => panic!("expected invalid_target"),
944 }
945 }
946
947 #[test]
948 fn invalid_target_null_description_becomes_none() {
949 let payload = r#"
950 {
951 "error": "invalid_target",
952 "error_description": null
953 }"#;
954
955 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
956 match parsed {
957 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
958 assert!(error_description.is_none());
959 }
960 _ => panic!("expected invalid_target"),
961 }
962 }
963
964 #[test]
965 fn invalid_target_ignores_send_access_error_type_and_extra_fields() {
966 let payload = r#"
967 {
968 "error": "invalid_target",
969 "send_access_error_type": "should_be_ignored",
970 "extra_field": {"k":"v"}
971 }"#;
972
973 let parsed: SendAccessTokenApiErrorResponse = from_str(payload).unwrap();
974 match parsed {
975 SendAccessTokenApiErrorResponse::InvalidTarget { error_description } => {
976 assert!(error_description.is_none());
977 }
978 _ => panic!("expected invalid_target"),
979 }
980 }
981 }
982
983 #[test]
984 fn unknown_top_level_error_rejects() {
985 let payload = r#"{ "error": "totally_new_error" }"#;
986 let err = serde_json::from_str::<SendAccessTokenApiErrorResponse>(payload).unwrap_err();
987 let _ = err;
988 }
989}