use std::error; use apple_app_store_receipts::{ endpoints::verify_receipt::{ReceiptData, RetryReason, VerifyReceipt, VerifyReceiptError}, objects::response_body::ResponseBody, types::status::Status, }; use http_api_client_endpoint::{ http::{Method, StatusCode, Version}, Response, RetryableEndpoint, RetryableEndpointRetry, }; #[test] fn render_request() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let req = verify_receipt.render_request(None)?; assert_eq!(req.method(), Method::POST); assert_eq!(req.uri(), "https://buy.itunes.apple.com/verifyReceipt"); assert_eq!(req.version(), Version::HTTP_11); let headers = req.headers(); assert_eq!( headers.get("User-Agent").map(|x| x.to_str().unwrap()), Some("curl/7.80.0") ); assert_eq!( headers.get("Content-Type").map(|x| x.to_str().unwrap()), Some("application/json") ); assert_eq!( headers.get("Accept").map(|x| x.to_str().unwrap()), Some("application/json") ); assert_eq!( req.body(), &br#"{"receipt-data":"foo","password":"pw"}"#.to_vec() ); Ok(()) } #[test] fn parse_response_with_http_503() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::SERVICE_UNAVAILABLE) .body(b"HTML".to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Ok(Err(reason)) => { assert_eq!(reason, RetryReason::ServiceUnavailable) } _ => panic!(), } Ok(()) } #[test] fn parse_response_with_http_400() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::BAD_REQUEST) .body(b"HTML".to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Err(VerifyReceiptError::StatusMismatch(status)) => { assert_eq!(status, StatusCode::BAD_REQUEST); } _ => panic!(), } Ok(()) } #[test] fn parse_response_with_21007() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21007}"#.to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Ok(Err(reason)) => { assert_eq!(reason, RetryReason::GotoSandbox) } _ => panic!(), } Ok(()) } #[test] #[should_panic(expected = "double goto sandbox")] fn parse_response_with_double_21007() { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21007}"#.to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Ok(Err(reason)) => { assert_eq!(reason, RetryReason::GotoSandbox) } _ => panic!(), } // again let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21007}"#.to_vec()) .unwrap(); #[allow(clippy::match_single_binding)] match verify_receipt.parse_response( res, Some(&RetryableEndpointRetry::new(1, RetryReason::GotoSandbox)), ) { _ => panic!(), } } #[test] fn parse_response_with_21002() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21002}"#.to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Ok(Err(reason)) => { assert_eq!(reason, RetryReason::TryAgainWithStatus(Status::Error21002)) } _ => panic!(), } Ok(()) } #[test] fn parse_response_with_many_times_21002() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21002}"#.to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Ok(Err(reason)) => { assert_eq!(reason, RetryReason::TryAgainWithStatus(Status::Error21002)) } _ => panic!(), } let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21002}"#.to_vec()) .unwrap(); match verify_receipt.parse_response( res, Some(&RetryableEndpointRetry::new( 1, RetryReason::TryAgainWithStatus(Status::Error21002), )), ) { Ok(Err(reason)) => { assert_eq!(reason, RetryReason::TryAgainWithStatus(Status::Error21002)) } _ => panic!(), } let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21002}"#.to_vec()) .unwrap(); match verify_receipt.parse_response( res, Some(&RetryableEndpointRetry::new( 2, RetryReason::TryAgainWithStatus(Status::Error21002), )), ) { Ok(Err(reason)) => { assert_eq!(reason, RetryReason::TryAgainWithStatus(Status::Error21002)) } _ => panic!(), } let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21002}"#.to_vec()) .unwrap(); match verify_receipt.parse_response( res, Some(&RetryableEndpointRetry::new( 3, RetryReason::TryAgainWithStatus(Status::Error21002), )), ) { Ok(Ok(ResponseBody::Error(body))) => { assert_eq!(body.status, Status::Error21002) } _ => { panic!() } } Ok(()) } #[test] fn parse_response_with_21104_and_retryable() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21104,"is_retryable":true}"#.to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Ok(Err(reason)) => assert_eq!( reason, RetryReason::TryAgainWithStatus(Status::InternalDataAccessError(21104)) ), _ => panic!(), } Ok(()) } #[test] fn parse_response_with_21104_and_not_retryable() -> Result<(), Box> { let verify_receipt = VerifyReceipt::new( "pw".to_owned(), ReceiptData::Base64String("foo".to_owned()), None, ); let res = Response::builder() .status(StatusCode::OK) .body(br#"{"status":21104,"is_retryable":false}"#.to_vec()) .unwrap(); match verify_receipt.parse_response(res, None) { Ok(Ok(ResponseBody::Error(body))) => { assert_eq!(body.status, Status::InternalDataAccessError(21104)) } _ => panic!(), } Ok(()) }