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