use derive_builder::Builder;
use mockito::{Mock, Server};
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::line_login::post_oauth2_v2_1_verify::Address;
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Default)]
#[builder(setter(into))]
#[builder(default)]
#[builder(field(public))]
pub struct MockParams {
pub id_token: String,
pub client_id: String,
pub nonce: Option<String>,
pub user_id: Option<String>,
pub status_code: usize,
pub iss: String,
pub sub: String,
pub aud: String,
pub exp: u64,
pub iat: u64,
pub auth_time: Option<u64>,
pub response_nonce: Option<String>,
pub amr: Vec<String>,
pub name: Option<String>,
pub picture: Option<String>,
pub email: Option<String>,
pub given_name: Option<String>,
pub given_name_pronunciation: Option<String>,
pub middle_name: Option<String>,
pub family_name: Option<String>,
pub family_name_pronunciation: Option<String>,
pub gender: Option<String>,
pub birthdate: Option<String>,
pub phone_number: Option<String>,
pub address: Option<Address>,
pub error_message: Option<String>,
}
pub async fn make_mock(server: &mut Server, builder: Option<MockParamsBuilder>) -> Mock {
let mut builder = builder.unwrap_or_default();
if builder.id_token.is_none() {
builder.id_token("test_id_token".to_string());
}
if builder.client_id.is_none() {
builder.client_id("1234567890".to_string());
}
if builder.status_code.is_none() {
builder.status_code(200usize);
}
if builder.iss.is_none() {
builder.iss("https://access.line.me".to_string());
}
if builder.sub.is_none() {
builder.sub("U123456".to_string());
}
if builder.aud.is_none() {
builder.aud("1234567890".to_string());
}
if builder.exp.is_none() {
builder.exp(1700000000u64);
}
if builder.iat.is_none() {
builder.iat(1699996400u64);
}
if builder.amr.is_none() {
builder.amr(vec!["linesso".to_string()]);
}
if builder.error_message.is_none() {
builder.error_message("error occurred".to_string());
}
let params = builder.build().unwrap();
let body_json = if params.status_code == 200 {
let mut json = json!({
"iss": params.iss,
"sub": params.sub,
"aud": params.aud,
"exp": params.exp,
"iat": params.iat,
"amr": params.amr,
});
if params.auth_time.is_some() {
json["auth_time"] = params.auth_time.into();
}
if params.response_nonce.is_some() {
json["nonce"] = params.response_nonce.clone().into();
}
if params.name.is_some() {
json["name"] = params.name.clone().into();
}
if params.picture.is_some() {
json["picture"] = params.picture.clone().into();
}
if params.email.is_some() {
json["email"] = params.email.clone().into();
}
if params.given_name.is_some() {
json["given_name"] = params.given_name.clone().into();
}
if params.given_name_pronunciation.is_some() {
json["given_name_pronunciation"] = params.given_name_pronunciation.clone().into();
}
if params.middle_name.is_some() {
json["middle_name"] = params.middle_name.clone().into();
}
if params.family_name.is_some() {
json["family_name"] = params.family_name.clone().into();
}
if params.family_name_pronunciation.is_some() {
json["family_name_pronunciation"] = params.family_name_pronunciation.clone().into();
}
if params.gender.is_some() {
json["gender"] = params.gender.clone().into();
}
if params.birthdate.is_some() {
json["birthdate"] = params.birthdate.clone().into();
}
if params.phone_number.is_some() {
json["phone_number"] = params.phone_number.clone().into();
}
if let Some(address) = ¶ms.address {
json["address"] = serde_json::to_value(address).unwrap();
}
json
} else {
json!({
"message": params.error_message
})
};
let mut matchers = vec![
mockito::Matcher::UrlEncoded("id_token".into(), params.id_token.clone()),
mockito::Matcher::UrlEncoded("client_id".into(), params.client_id.clone()),
];
if let Some(nonce) = params.nonce {
matchers.push(mockito::Matcher::UrlEncoded("nonce".into(), nonce));
}
if let Some(user_id) = params.user_id {
matchers.push(mockito::Matcher::UrlEncoded("user_id".into(), user_id));
}
server
.mock("POST", "/oauth2/v2.1/verify")
.match_body(mockito::Matcher::AllOf(matchers))
.with_status(params.status_code)
.with_header("content-type", "application/json")
.with_body(body_json.to_string())
.create_async()
.await
}
#[cfg(test)]
mod tests {
use crate::{error::Error, line_login::post_oauth2_v2_1_verify, option::LineOptions};
use super::*;
#[tokio::test]
async fn test_make_mock_post_oauth2_v2_1_verify_success() {
let mut server = Server::new_async().await;
let mut builder = MockParamsBuilder::default();
builder.response_nonce(Some("test_nonce".to_string()));
builder.auth_time(Some(1699996000u64));
builder.name(Some("Test User".to_string()));
builder.picture(Some("https://example.com/picture.jpg".to_string()));
builder.email(Some("test@example.com".to_string()));
builder.given_name(Some("Taro".to_string()));
builder.given_name_pronunciation(Some("タロウ".to_string()));
builder.middle_name(Some("M".to_string()));
builder.family_name(Some("Yamada".to_string()));
builder.family_name_pronunciation(Some("ヤマダ".to_string()));
builder.gender(Some("male".to_string()));
builder.birthdate(Some("1990-01-01".to_string()));
builder.phone_number(Some("+819012345678".to_string()));
builder.address(Some(Address {
country: Some("JP".to_string()),
postal_code: Some("150-0002".to_string()),
region: Some("Tokyo".to_string()),
locality: Some("Shibuya".to_string()),
street_address: Some("1-2-3".to_string()),
extra: std::collections::HashMap::new(),
}));
let mock = make_mock(&mut server, Some(builder)).await;
let request_body = post_oauth2_v2_1_verify::RequestBody {
id_token: "test_id_token".to_string(),
client_id: "1234567890".to_string(),
nonce: Some("test_nonce".to_string()),
user_id: None,
};
let res = post_oauth2_v2_1_verify::execute(
&request_body,
&LineOptions::builder().with_prefix_url(server.url()).build(),
)
.await
.unwrap();
assert_eq!(res.0.iss, "https://access.line.me");
assert_eq!(res.0.sub, "U123456");
assert_eq!(res.0.aud, "1234567890");
assert_eq!(res.0.exp, 1700000000);
assert_eq!(res.0.iat, 1699996400);
assert_eq!(res.0.auth_time, Some(1699996000));
assert_eq!(res.0.nonce, Some("test_nonce".to_string()));
assert_eq!(res.0.amr, vec!["linesso"]);
assert_eq!(res.0.name, Some("Test User".to_string()));
assert_eq!(
res.0.picture,
Some("https://example.com/picture.jpg".to_string())
);
assert_eq!(res.0.email, Some("test@example.com".to_string()));
assert_eq!(res.0.given_name, Some("Taro".to_string()));
assert_eq!(res.0.given_name_pronunciation, Some("タロウ".to_string()));
assert_eq!(res.0.middle_name, Some("M".to_string()));
assert_eq!(res.0.family_name, Some("Yamada".to_string()));
assert_eq!(res.0.family_name_pronunciation, Some("ヤマダ".to_string()));
assert_eq!(res.0.gender, Some("male".to_string()));
assert_eq!(res.0.birthdate, Some("1990-01-01".to_string()));
assert_eq!(res.0.phone_number, Some("+819012345678".to_string()));
let address = res.0.address.unwrap();
assert_eq!(address.country, Some("JP".to_string()));
assert_eq!(address.postal_code, Some("150-0002".to_string()));
assert_eq!(address.region, Some("Tokyo".to_string()));
assert_eq!(address.locality, Some("Shibuya".to_string()));
assert_eq!(address.street_address, Some("1-2-3".to_string()));
mock.assert_async().await;
}
#[tokio::test]
async fn test_make_mock_post_oauth2_v2_1_verify_minimal() {
let mut server = Server::new_async().await;
let builder = MockParamsBuilder::default();
let mock = make_mock(&mut server, Some(builder)).await;
let request_body = post_oauth2_v2_1_verify::RequestBody {
id_token: "test_id_token".to_string(),
client_id: "1234567890".to_string(),
nonce: None,
user_id: None,
};
let res = post_oauth2_v2_1_verify::execute(
&request_body,
&LineOptions::builder().with_prefix_url(server.url()).build(),
)
.await
.unwrap();
assert_eq!(res.0.iss, "https://access.line.me");
assert_eq!(res.0.sub, "U123456");
assert_eq!(res.0.aud, "1234567890");
assert!(res.0.nonce.is_none());
assert!(res.0.name.is_none());
assert!(res.0.picture.is_none());
assert!(res.0.email.is_none());
assert!(res.0.given_name.is_none());
assert!(res.0.family_name.is_none());
assert!(res.0.gender.is_none());
assert!(res.0.birthdate.is_none());
assert!(res.0.phone_number.is_none());
assert!(res.0.address.is_none());
mock.assert_async().await;
}
#[tokio::test]
async fn test_make_mock_post_oauth2_v2_1_verify_failure() {
let mut server = Server::new_async().await;
let mut builder = MockParamsBuilder::default();
builder.status_code(400usize);
let mock = make_mock(&mut server, Some(builder)).await;
let request_body = post_oauth2_v2_1_verify::RequestBody {
id_token: "test_id_token".to_string(),
client_id: "1234567890".to_string(),
nonce: None,
user_id: None,
};
let res = post_oauth2_v2_1_verify::execute(
&request_body,
&LineOptions::builder().with_prefix_url(server.url()).build(),
)
.await;
match res {
Err(e) => match *e {
Error::Line(response, status_code, _header) => {
assert_eq!(status_code, 400);
assert_eq!(response.message, "error occurred");
}
_ => panic!("Unexpected error"),
},
_ => panic!("Unexpected response"),
}
mock.assert_async().await;
}
}