From 68a30ea0fea7a375e215d2511ab33828a37fbb9d Mon Sep 17 00:00:00 2001 From: Glenn Griffin Date: Thu, 14 Nov 2019 14:07:11 -0800 Subject: [PATCH] Tidy up tests. --- src/device.rs | 147 +++++++++++++++++++++-------------------- src/helper.rs | 10 +++ src/installed.rs | 119 +++++++++++++++++---------------- src/refresh.rs | 46 ++++--------- src/service_account.rs | 139 ++++++++++++++------------------------ 5 files changed, 214 insertions(+), 247 deletions(-) diff --git a/src/device.rs b/src/device.rs index 90ec9f3..6815963 100644 --- a/src/device.rs +++ b/src/device.rs @@ -264,16 +264,12 @@ impl DeviceFlow { #[cfg(test)] mod tests { - use hyper; use hyper_rustls::HttpsConnector; - use mockito; - use tokio; use super::*; - use crate::helper::parse_application_secret; - #[test] - fn test_device_end2end() { + #[tokio::test] + async fn test_device_end2end() { #[derive(Clone)] struct FD; impl FlowDelegate for FD { @@ -283,9 +279,15 @@ mod tests { } let server_url = mockito::server_url(); - let app_secret = r#"{"installed":{"client_id":"902216714886-k2v9uei3p1dk6h686jbsn9mo96tnbvto.apps.googleusercontent.com","project_id":"yup-test-243420","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"iuMPN6Ne1PD7cos29Tk9rlqH","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}"#; - let mut app_secret = parse_application_secret(app_secret).unwrap(); - app_secret.token_uri = format!("{}/token", server_url); + let app_secret: ApplicationSecret = crate::parse_json!({ + "client_id": "902216714886-k2v9uei3p1dk6h686jbsn9mo96tnbvto.apps.googleusercontent.com", + "project_id": "yup-test-243420", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": format!("{}/token", server_url), + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_secret": "iuMPN6Ne1PD7cos29Tk9rlqH", + "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob","http://localhost"], + }); let device_code_url = format!("{}/code", server_url); let https = HttpsConnector::new(); @@ -300,118 +302,123 @@ mod tests { grant_type: GOOGLE_GRANT_TYPE.into(), }; - let rt = tokio::runtime::Builder::new() - .core_threads(1) - .panic_handler(|e| std::panic::resume_unwind(e)) - .build() - .unwrap(); - // Successful path { - let code_response = r#"{"device_code": "devicecode", "user_code": "usercode", "verification_url": "https://example.com/verify", "expires_in": 1234567, "interval": 1}"#; + let code_response = serde_json::json!({ + "device_code": "devicecode", + "user_code": "usercode", + "verification_url": "https://example.com/verify", + "expires_in": 1234567, + "interval": 1 + }); let _m = mockito::mock("POST", "/code") .match_body(mockito::Matcher::Regex( ".*client_id=902216714886.*".to_string(), )) .with_status(200) - .with_body(code_response) + .with_body(code_response.to_string()) .create(); - let token_response = r#"{"access_token": "accesstoken", "refresh_token": "refreshtoken", "token_type": "Bearer", "expires_in": 1234567}"#; + let token_response = serde_json::json!({ + "access_token": "accesstoken", + "refresh_token": "refreshtoken", + "token_type": "Bearer", + "expires_in": 1234567 + }); let _m = mockito::mock("POST", "/token") .match_body(mockito::Matcher::Regex( ".*client_secret=iuMPN6Ne1PD7cos29Tk9rlqH&code=devicecode.*".to_string(), )) .with_status(200) - .with_body(token_response) + .with_body(token_response.to_string()) .create(); - let fut = async { - let token = flow - .token( - &client, - &app_secret, - &["https://www.googleapis.com/scope/1"], - ) - .await - .unwrap(); - assert_eq!("accesstoken", token.access_token); - Ok(()) as Result<(), ()> - }; - rt.block_on(fut).expect("block_on"); - + let token = flow + .token( + &client, + &app_secret, + &["https://www.googleapis.com/scope/1"], + ) + .await + .expect("token failed"); + assert_eq!("accesstoken", token.access_token); _m.assert(); } + // Code is not delivered. { - let code_response = - r#"{"error": "invalid_client_id", "error_description": "description"}"#; + let code_response = serde_json::json!({ + "error": "invalid_client_id", + "error_description": "description" + }); let _m = mockito::mock("POST", "/code") .match_body(mockito::Matcher::Regex( ".*client_id=902216714886.*".to_string(), )) .with_status(400) - .with_body(code_response) + .with_body(code_response.to_string()) .create(); - let token_response = r#"{"access_token": "accesstoken", "refresh_token": "refreshtoken", "token_type": "Bearer", "expires_in": 1234567}"#; + let token_response = serde_json::json!({ + "access_token": "accesstoken", + "refresh_token": "refreshtoken", + "token_type": "Bearer", + "expires_in": 1234567 + }); let _m = mockito::mock("POST", "/token") .match_body(mockito::Matcher::Regex( ".*client_secret=iuMPN6Ne1PD7cos29Tk9rlqH&code=devicecode.*".to_string(), )) .with_status(200) - .with_body(token_response) + .with_body(token_response.to_string()) .expect(0) // Never called! .create(); - let fut = async { - let res = flow - .token( - &client, - &app_secret, - &["https://www.googleapis.com/scope/1"], - ) - .await; - assert!(res.is_err()); - assert!(format!("{}", res.unwrap_err()).contains("invalid_client_id")); - Ok(()) as Result<(), ()> - }; - rt.block_on(fut).expect("block_on"); - + let res = flow + .token( + &client, + &app_secret, + &["https://www.googleapis.com/scope/1"], + ) + .await; + assert!(res.is_err()); + assert!(format!("{}", res.unwrap_err()).contains("invalid_client_id")); _m.assert(); } + // Token is not delivered. { - let code_response = r#"{"device_code": "devicecode", "user_code": "usercode", "verification_url": "https://example.com/verify", "expires_in": 1234567, "interval": 1}"#; + let code_response = serde_json::json!({ + "device_code": "devicecode", + "user_code": "usercode", + "verification_url": "https://example.com/verify", + "expires_in": 1234567, + "interval": 1 + }); let _m = mockito::mock("POST", "/code") .match_body(mockito::Matcher::Regex( ".*client_id=902216714886.*".to_string(), )) .with_status(200) - .with_body(code_response) + .with_body(code_response.to_string()) .create(); - let token_response = r#"{"error": "access_denied"}"#; + let token_response = serde_json::json!({"error": "access_denied"}); let _m = mockito::mock("POST", "/token") .match_body(mockito::Matcher::Regex( ".*client_secret=iuMPN6Ne1PD7cos29Tk9rlqH&code=devicecode.*".to_string(), )) .with_status(400) - .with_body(token_response) + .with_body(token_response.to_string()) .expect(1) .create(); - let fut = async { - let res = flow - .token( - &client, - &app_secret, - &["https://www.googleapis.com/scope/1"], - ) - .await; - assert!(res.is_err()); - assert!(format!("{}", res.unwrap_err()).contains("Access denied by user")); - Ok(()) as Result<(), ()> - }; - rt.block_on(fut).expect("block_on"); - + let res = flow + .token( + &client, + &app_secret, + &["https://www.googleapis.com/scope/1"], + ) + .await; + assert!(res.is_err()); + assert!(format!("{}", res.unwrap_err()).contains("Access denied by user")); _m.assert(); } } diff --git a/src/helper.rs b/src/helper.rs index e5465ac..18cced0 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -65,3 +65,13 @@ where debug_assert_eq!(size, result.len()); result } + +#[cfg(test)] +#[macro_export] +/// Utility function for parsing json. Useful in unit tests. Simply wrap the +/// json! macro in a from_value to deserialize the contents to arbitrary structs. +macro_rules! parse_json { + ($($json:tt)+) => { + ::serde_json::from_value(::serde_json::json!($($json)+)).expect("failed to deserialize") + } +} diff --git a/src/installed.rs b/src/installed.rs index 8fd64ae..d0805d0 100644 --- a/src/installed.rs +++ b/src/installed.rs @@ -397,15 +397,13 @@ mod tests { use hyper::client::connect::HttpConnector; use hyper::Uri; use hyper_rustls::HttpsConnector; - use mockito::{self, mock}; - use tokio; + use mockito::mock; use super::*; use crate::authenticator_delegate::FlowDelegate; - use crate::helper::*; - #[test] - fn test_end2end() { + #[tokio::test] + async fn test_end2end() { #[derive(Clone)] struct FD( String, @@ -455,9 +453,15 @@ mod tests { } let server_url = mockito::server_url(); - let app_secret = r#"{"installed":{"client_id":"902216714886-k2v9uei3p1dk6h686jbsn9mo96tnbvto.apps.googleusercontent.com","project_id":"yup-test-243420","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"iuMPN6Ne1PD7cos29Tk9rlqH","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}"#; - let mut app_secret = parse_application_secret(app_secret).unwrap(); - app_secret.token_uri = format!("{}/token", server_url); + let app_secret: ApplicationSecret = crate::parse_json!({ + "client_id": "902216714886-k2v9uei3p1dk6h686jbsn9mo96tnbvto.apps.googleusercontent.com", + "project_id": "yup-test-243420", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": format!("{}/token", server_url), + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_secret": "iuMPN6Ne1PD7cos29Tk9rlqH", + "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob","http://localhost"] + }); let https = HttpsConnector::new(); let client = hyper::Client::builder() @@ -470,35 +474,34 @@ mod tests { flow_delegate: Box::new(fd), }; - let rt = tokio::runtime::Builder::new() - .core_threads(1) - .panic_handler(|e| std::panic::resume_unwind(e)) - .build() - .unwrap(); - // Successful path. { let _m = mock("POST", "/token") - .match_body(mockito::Matcher::Regex(".*code=authorizationcode.*client_id=9022167.*".to_string())) - .with_body(r#"{"access_token": "accesstoken", "refresh_token": "refreshtoken", "token_type": "Bearer", "expires_in": 12345678}"#) - .expect(1) - .create(); + .match_body(mockito::Matcher::Regex( + ".*code=authorizationcode.*client_id=9022167.*".to_string(), + )) + .with_body( + serde_json::json!({ + "access_token": "accesstoken", + "refresh_token": "refreshtoken", + "token_type": "Bearer", + "expires_in": 12345678 + }) + .to_string(), + ) + .expect(1) + .create(); - let fut = || { - async { - let tok = inf - .token(&client, &app_secret, &["https://googleapis.com/some/scope"]) - .await - .map_err(|_| ())?; - assert_eq!("accesstoken", tok.access_token); - assert_eq!("refreshtoken", tok.refresh_token.unwrap()); - assert_eq!("Bearer", tok.token_type); - Ok(()) as Result<(), ()> - } - }; - rt.block_on(fut()).expect("block on"); + let tok = inf + .token(&client, &app_secret, &["https://googleapis.com/some/scope"]) + .await + .expect("failed to get token"); + assert_eq!("accesstoken", tok.access_token); + assert_eq!("refreshtoken", tok.refresh_token.unwrap()); + assert_eq!("Bearer", tok.token_type); _m.assert(); } + // Successful path with HTTP redirect. { let inf = InstalledFlow { @@ -509,24 +512,31 @@ mod tests { )), }; let _m = mock("POST", "/token") - .match_body(mockito::Matcher::Regex(".*code=authorizationcodefromlocalserver.*client_id=9022167.*".to_string())) - .with_body(r#"{"access_token": "accesstoken", "refresh_token": "refreshtoken", "token_type": "Bearer", "expires_in": 12345678}"#) - .expect(1) - .create(); + .match_body(mockito::Matcher::Regex( + ".*code=authorizationcodefromlocalserver.*client_id=9022167.*".to_string(), + )) + .with_body( + serde_json::json!({ + "access_token": "accesstoken", + "refresh_token": "refreshtoken", + "token_type": "Bearer", + "expires_in": 12345678 + }) + .to_string(), + ) + .expect(1) + .create(); - let fut = async { - let tok = inf - .token(&client, &app_secret, &["https://googleapis.com/some/scope"]) - .await - .map_err(|_| ())?; - assert_eq!("accesstoken", tok.access_token); - assert_eq!("refreshtoken", tok.refresh_token.unwrap()); - assert_eq!("Bearer", tok.token_type); - Ok(()) as Result<(), ()> - }; - rt.block_on(fut).expect("block on"); + let tok = inf + .token(&client, &app_secret, &["https://googleapis.com/some/scope"]) + .await + .expect("failed to get token"); + assert_eq!("accesstoken", tok.access_token); + assert_eq!("refreshtoken", tok.refresh_token.unwrap()); + assert_eq!("Bearer", tok.token_type); _m.assert(); } + // Error from server. { let _m = mock("POST", "/token") @@ -534,22 +544,17 @@ mod tests { ".*code=authorizationcode.*client_id=9022167.*".to_string(), )) .with_status(400) - .with_body(r#"{"error": "invalid_code"}"#) + .with_body(serde_json::json!({"error": "invalid_code"}).to_string()) .expect(1) .create(); - let fut = async { - let tokr = inf - .token(&client, &app_secret, &["https://googleapis.com/some/scope"]) - .await; - assert!(tokr.is_err()); - assert!(format!("{}", tokr.unwrap_err()).contains("invalid_code")); - Ok(()) as Result<(), ()> - }; - rt.block_on(fut).expect("block on"); + let tokr = inf + .token(&client, &app_secret, &["https://googleapis.com/some/scope"]) + .await; + assert!(tokr.is_err()); + assert!(format!("{}", tokr.unwrap_err()).contains("invalid_code")); _m.assert(); } - rt.shutdown_on_idle(); } #[test] diff --git a/src/refresh.rs b/src/refresh.rs index 0a4a225..970cfe1 100644 --- a/src/refresh.rs +++ b/src/refresh.rs @@ -79,13 +79,10 @@ mod tests { use super::*; use crate::helper; - use hyper; use hyper_rustls::HttpsConnector; - use mockito; - use tokio; - #[test] - fn test_refresh_end2end() { + #[tokio::test] + async fn test_refresh_end2end() { let server_url = mockito::server_url(); let app_secret = r#"{"installed":{"client_id":"902216714886-k2v9uei3p1dk6h686jbsn9mo96tnbvto.apps.googleusercontent.com","project_id":"yup-test-243420","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"iuMPN6Ne1PD7cos29Tk9rlqH","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}"#; @@ -98,12 +95,6 @@ mod tests { .keep_alive(false) .build::<_, hyper::Body>(https); - let rt = tokio::runtime::Builder::new() - .core_threads(1) - .panic_handler(|e| std::panic::resume_unwind(e)) - .build() - .unwrap(); - // Success { let _m = mockito::mock("POST", "/token") @@ -112,18 +103,14 @@ mod tests { .with_status(200) .with_body(r#"{"access_token": "new-access-token", "token_type": "Bearer", "expires_in": 1234567}"#) .create(); - let fut = async { - let token = RefreshFlow::refresh_token(&client, &app_secret, refresh_token) - .await - .unwrap(); - assert_eq!("new-access-token", token.access_token); - assert_eq!("Bearer", token.token_type); - Ok(()) as Result<(), ()> - }; - - rt.block_on(fut).expect("block_on"); + let token = RefreshFlow::refresh_token(&client, &app_secret, refresh_token) + .await + .expect("token failed"); + assert_eq!("new-access-token", token.access_token); + assert_eq!("Bearer", token.token_type); _m.assert(); } + // Refresh error. { let _m = mockito::mock("POST", "/token") @@ -133,18 +120,13 @@ mod tests { .with_body(r#"{"error": "invalid_token"}"#) .create(); - let fut = async { - let rr = RefreshFlow::refresh_token(&client, &app_secret, refresh_token).await; - match rr { - Err(RefreshError::ServerError(e, None)) => { - assert_eq!(e, "invalid_token"); - } - _ => panic!(format!("unexpected RefreshResult {:?}", rr)), + let rr = RefreshFlow::refresh_token(&client, &app_secret, refresh_token).await; + match rr { + Err(RefreshError::ServerError(e, None)) => { + assert_eq!(e, "invalid_token"); } - Ok(()) as Result<(), ()> - }; - - rt.block_on(fut).expect("block_on"); + _ => panic!(format!("unexpected RefreshResult {:?}", rr)), + } _m.assert(); } } diff --git a/src/service_account.rs b/src/service_account.rs index b88d5c8..e88022a 100644 --- a/src/service_account.rs +++ b/src/service_account.rs @@ -348,69 +348,54 @@ where mod tests { use super::*; use crate::helper::service_account_key_from_file; + use crate::parse_json; - use hyper; - use hyper_rustls::HttpsConnector; - use mockito::{self, mock}; - use tokio; + use mockito::mock; - #[test] - fn test_mocked_http() { + #[tokio::test] + async fn test_mocked_http() { env_logger::try_init().unwrap(); let server_url = &mockito::server_url(); - let client_secret = r#"{ - "type": "service_account", - "project_id": "yup-test-243420", - "private_key_id": "26de294916614a5ebdf7a065307ed3ea9941902b", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDemmylrvp1KcOn\n9yTAVVKPpnpYznvBvcAU8Qjwr2fSKylpn7FQI54wCk5VJVom0jHpAmhxDmNiP8yv\nHaqsef+87Oc0n1yZ71/IbeRcHZc2OBB33/LCFqf272kThyJo3qspEqhuAw0e8neg\nLQb4jpm9PsqR8IjOoAtXQSu3j0zkXemMYFy93PWHjVpPEUX16NGfsWH7oxspBHOk\n9JPGJL8VJdbiAoDSDgF0y9RjJY5I52UeHNhMsAkTYs6mIG4kKXt2+T9tAyHw8aho\nwmuytQAfydTflTfTG8abRtliF3nil2taAc5VB07dP1b4dVYy/9r6M8Z0z4XM7aP+\nNdn2TKm3AgMBAAECggEAWi54nqTlXcr2M5l535uRb5Xz0f+Q/pv3ceR2iT+ekXQf\n+mUSShOr9e1u76rKu5iDVNE/a7H3DGopa7ZamzZvp2PYhSacttZV2RbAIZtxU6th\n7JajPAM+t9klGh6wj4jKEcE30B3XVnbHhPJI9TCcUyFZoscuPXt0LLy/z8Uz0v4B\nd5JARwyxDMb53VXwukQ8nNY2jP7WtUig6zwE5lWBPFMbi8GwGkeGZOruAK5sPPwY\nGBAlfofKANI7xKx9UXhRwisB4+/XI1L0Q6xJySv9P+IAhDUI6z6kxR+WkyT/YpG3\nX9gSZJc7qEaxTIuDjtep9GTaoEqiGntjaFBRKoe+VQKBgQDzM1+Ii+REQqrGlUJo\nx7KiVNAIY/zggu866VyziU6h5wjpsoW+2Npv6Dv7nWvsvFodrwe50Y3IzKtquIal\nVd8aa50E72JNImtK/o5Nx6xK0VySjHX6cyKENxHRDnBmNfbALRM+vbD9zMD0lz2q\nmns/RwRGq3/98EqxP+nHgHSr9QKBgQDqUYsFAAfvfT4I75Glc9svRv8IsaemOm07\nW1LCwPnj1MWOhsTxpNF23YmCBupZGZPSBFQobgmHVjQ3AIo6I2ioV6A+G2Xq/JCF\nmzfbvZfqtbbd+nVgF9Jr1Ic5T4thQhAvDHGUN77BpjEqZCQLAnUWJx9x7e2xvuBl\n1A6XDwH/ewKBgQDv4hVyNyIR3nxaYjFd7tQZYHTOQenVffEAd9wzTtVbxuo4sRlR\nNM7JIRXBSvaATQzKSLHjLHqgvJi8LITLIlds1QbNLl4U3UVddJbiy3f7WGTqPFfG\nkLhUF4mgXpCpkMLxrcRU14Bz5vnQiDmQRM4ajS7/kfwue00BZpxuZxst3QKBgQCI\nRI3FhaQXyc0m4zPfdYYVc4NjqfVmfXoC1/REYHey4I1XetbT9Nb/+ow6ew0UbgSC\nUZQjwwJ1m1NYXU8FyovVwsfk9ogJ5YGiwYb1msfbbnv/keVq0c/Ed9+AG9th30qM\nIf93hAfClITpMz2mzXIMRQpLdmQSR4A2l+E4RjkSOwKBgQCB78AyIdIHSkDAnCxz\nupJjhxEhtQ88uoADxRoEga7H/2OFmmPsqfytU4+TWIdal4K+nBCBWRvAX1cU47vH\nJOlSOZI0gRKe0O4bRBQc8GXJn/ubhYSxI02IgkdGrIKpOb5GG10m85ZvqsXw3bKn\nRVHMD0ObF5iORjZUqD0yRitAdg==\n-----END PRIVATE KEY-----\n", - "client_email": "yup-test-sa-1@yup-test-243420.iam.gserviceaccount.com", - "client_id": "102851967901799660408", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/yup-test-sa-1%40yup-test-243420.iam.gserviceaccount.com" -}"#; - let mut key: ServiceAccountKey = serde_json::from_str(client_secret).unwrap(); - key.token_uri = format!("{}/token", server_url); + let key: ServiceAccountKey = parse_json!({ + "type": "service_account", + "project_id": "yup-test-243420", + "private_key_id": "26de294916614a5ebdf7a065307ed3ea9941902b", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDemmylrvp1KcOn\n9yTAVVKPpnpYznvBvcAU8Qjwr2fSKylpn7FQI54wCk5VJVom0jHpAmhxDmNiP8yv\nHaqsef+87Oc0n1yZ71/IbeRcHZc2OBB33/LCFqf272kThyJo3qspEqhuAw0e8neg\nLQb4jpm9PsqR8IjOoAtXQSu3j0zkXemMYFy93PWHjVpPEUX16NGfsWH7oxspBHOk\n9JPGJL8VJdbiAoDSDgF0y9RjJY5I52UeHNhMsAkTYs6mIG4kKXt2+T9tAyHw8aho\nwmuytQAfydTflTfTG8abRtliF3nil2taAc5VB07dP1b4dVYy/9r6M8Z0z4XM7aP+\nNdn2TKm3AgMBAAECggEAWi54nqTlXcr2M5l535uRb5Xz0f+Q/pv3ceR2iT+ekXQf\n+mUSShOr9e1u76rKu5iDVNE/a7H3DGopa7ZamzZvp2PYhSacttZV2RbAIZtxU6th\n7JajPAM+t9klGh6wj4jKEcE30B3XVnbHhPJI9TCcUyFZoscuPXt0LLy/z8Uz0v4B\nd5JARwyxDMb53VXwukQ8nNY2jP7WtUig6zwE5lWBPFMbi8GwGkeGZOruAK5sPPwY\nGBAlfofKANI7xKx9UXhRwisB4+/XI1L0Q6xJySv9P+IAhDUI6z6kxR+WkyT/YpG3\nX9gSZJc7qEaxTIuDjtep9GTaoEqiGntjaFBRKoe+VQKBgQDzM1+Ii+REQqrGlUJo\nx7KiVNAIY/zggu866VyziU6h5wjpsoW+2Npv6Dv7nWvsvFodrwe50Y3IzKtquIal\nVd8aa50E72JNImtK/o5Nx6xK0VySjHX6cyKENxHRDnBmNfbALRM+vbD9zMD0lz2q\nmns/RwRGq3/98EqxP+nHgHSr9QKBgQDqUYsFAAfvfT4I75Glc9svRv8IsaemOm07\nW1LCwPnj1MWOhsTxpNF23YmCBupZGZPSBFQobgmHVjQ3AIo6I2ioV6A+G2Xq/JCF\nmzfbvZfqtbbd+nVgF9Jr1Ic5T4thQhAvDHGUN77BpjEqZCQLAnUWJx9x7e2xvuBl\n1A6XDwH/ewKBgQDv4hVyNyIR3nxaYjFd7tQZYHTOQenVffEAd9wzTtVbxuo4sRlR\nNM7JIRXBSvaATQzKSLHjLHqgvJi8LITLIlds1QbNLl4U3UVddJbiy3f7WGTqPFfG\nkLhUF4mgXpCpkMLxrcRU14Bz5vnQiDmQRM4ajS7/kfwue00BZpxuZxst3QKBgQCI\nRI3FhaQXyc0m4zPfdYYVc4NjqfVmfXoC1/REYHey4I1XetbT9Nb/+ow6ew0UbgSC\nUZQjwwJ1m1NYXU8FyovVwsfk9ogJ5YGiwYb1msfbbnv/keVq0c/Ed9+AG9th30qM\nIf93hAfClITpMz2mzXIMRQpLdmQSR4A2l+E4RjkSOwKBgQCB78AyIdIHSkDAnCxz\nupJjhxEhtQ88uoADxRoEga7H/2OFmmPsqfytU4+TWIdal4K+nBCBWRvAX1cU47vH\nJOlSOZI0gRKe0O4bRBQc8GXJn/ubhYSxI02IgkdGrIKpOb5GG10m85ZvqsXw3bKn\nRVHMD0ObF5iORjZUqD0yRitAdg==\n-----END PRIVATE KEY-----\n", + "client_email": "yup-test-sa-1@yup-test-243420.iam.gserviceaccount.com", + "client_id": "102851967901799660408", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": format!("{}/token", server_url), + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/yup-test-sa-1%40yup-test-243420.iam.gserviceaccount.com" + }); - let json_response = r#"{ - "access_token": "ya29.c.ElouBywiys0LyNaZoLPJcp1Fdi2KjFMxzvYKLXkTdvM-rDfqKlvEq6PiMhGoGHx97t5FAvz3eb_ahdwlBjSStxHtDVQB4ZPRJQ_EOi-iS7PnayahU2S9Jp8S6rk", - "expires_in": 3600, - "token_type": "Bearer" -}"#; - let bad_json_response = r#"{ - "access_token": "ya29.c.ElouBywiys0LyNaZoLPJcp1Fdi2KjFMxzvYKLXkTdvM-rDfqKlvEq6PiMhGoGHx97t5FAvz3eb_ahdwlBjSStxHtDVQB4ZPRJQ_EOi-iS7PnayahU2S9Jp8S6rk", - "token_type": "Bearer" -}"#; - - let https = HttpsConnector::new(); - let client = hyper::Client::builder() - .keep_alive(false) - .build::<_, hyper::Body>(https); - let rt = tokio::runtime::Builder::new() - .core_threads(1) - .panic_handler(|e| std::panic::resume_unwind(e)) - .build() - .unwrap(); + let json_response = serde_json::json!({ + "access_token": "ya29.c.ElouBywiys0LyNaZoLPJcp1Fdi2KjFMxzvYKLXkTdvM-rDfqKlvEq6PiMhGoGHx97t5FAvz3eb_ahdwlBjSStxHtDVQB4ZPRJQ_EOi-iS7PnayahU2S9Jp8S6rk", + "expires_in": 3600, + "token_type": "Bearer" + }); + let bad_json_response = serde_json::json!({ + "access_token": "ya29.c.ElouBywiys0LyNaZoLPJcp1Fdi2KjFMxzvYKLXkTdvM-rDfqKlvEq6PiMhGoGHx97t5FAvz3eb_ahdwlBjSStxHtDVQB4ZPRJQ_EOi-iS7PnayahU2S9Jp8S6rk", + "token_type": "Bearer" + }); // Successful path. { let _m = mock("POST", "/token") .with_status(200) .with_header("content-type", "text/json") - .with_body(json_response) + .with_body(json_response.to_string()) .expect(1) .create(); - let acc = ServiceAccountAccess::new(client.clone(), key.clone(), None).unwrap(); - let fut = async { - let tok = acc - .token(&["https://www.googleapis.com/auth/pubsub"]) - .await?; - assert!(tok.access_token.contains("ya29.c.ElouBywiys0Ly")); - assert_eq!(Some(3600), tok.expires_in); - Ok(()) as Result<(), Error> - }; - rt.block_on(fut).expect("block_on"); + let acc = ServiceAccountAuthenticator::builder(key.clone()) + .build() + .unwrap(); + let tok = acc + .token(&["https://www.googleapis.com/auth/pubsub"]) + .await + .expect("token failed"); + assert!(tok.access_token.contains("ya29.c.ElouBywiys0Ly")); + assert_eq!(Some(3600), tok.expires_in); assert!(acc .cache @@ -422,16 +407,12 @@ mod tests { ) .is_some()); // Test that token is in cache (otherwise mock will tell us) - let fut = async { - let tok = acc - .token(&["https://www.googleapis.com/auth/pubsub"]) - .await?; - assert!(tok.access_token.contains("ya29.c.ElouBywiys0Ly")); - assert_eq!(Some(3600), tok.expires_in); - Ok(()) as Result<(), Error> - }; - rt.block_on(fut).expect("block_on 2"); - + let tok = acc + .token(&["https://www.googleapis.com/auth/pubsub"]) + .await + .expect("token failed"); + assert!(tok.access_token.contains("ya29.c.ElouBywiys0Ly")); + assert_eq!(Some(3600), tok.expires_in); _m.assert(); } // Malformed response. @@ -439,48 +420,30 @@ mod tests { let _m = mock("POST", "/token") .with_status(200) .with_header("content-type", "text/json") - .with_body(bad_json_response) + .with_body(bad_json_response.to_string()) .create(); let acc = ServiceAccountAuthenticator::builder(key.clone()) - .hyper_client(client.clone()) .build() .unwrap(); - let fut = async { - let result = acc.token(&["https://www.googleapis.com/auth/pubsub"]).await; - assert!(result.is_err()); - Ok(()) as Result<(), ()> - }; - rt.block_on(fut).expect("block_on"); + let result = acc.token(&["https://www.googleapis.com/auth/pubsub"]).await; + assert!(result.is_err()); _m.assert(); } - rt.shutdown_on_idle(); } // Valid but deactivated key. const TEST_PRIVATE_KEY_PATH: &'static str = "examples/Sanguine-69411a0c0eea.json"; // Uncomment this test to verify that we can successfully obtain tokens. - //#[test] + //#[tokio::test] #[allow(dead_code)] - fn test_service_account_e2e() { + async fn test_service_account_e2e() { let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap(); - let https = HttpsConnector::new(); - let client = hyper::Client::builder().build(https); - let acc = ServiceAccountAuthenticator::builder(key) - .hyper_client(client) - .build() - .unwrap(); - let rt = tokio::runtime::Builder::new() - .core_threads(1) - .panic_handler(|e| std::panic::resume_unwind(e)) - .build() - .unwrap(); - rt.block_on(async { - println!( - "{:?}", - acc.token(&["https://www.googleapis.com/auth/pubsub"]).await - ); - }); + let acc = ServiceAccountAuthenticator::builder(key).build().unwrap(); + println!( + "{:?}", + acc.token(&["https://www.googleapis.com/auth/pubsub"]).await + ); } #[test]