Merge branch 'codex/wechat-nickname'
This commit is contained in:
@@ -2584,7 +2584,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wechat_miniprogram_login_returns_system_token_and_marks_session_source() {
|
||||
async fn wechat_miniprogram_login_returns_system_token_and_marks_session_label() {
|
||||
let config = AppConfig {
|
||||
wechat_auth_enabled: true,
|
||||
..AppConfig::default()
|
||||
@@ -2606,7 +2606,8 @@ mod tests {
|
||||
.header("x-mini-program-env", "develop")
|
||||
.body(Body::from(
|
||||
serde_json::json!({
|
||||
"code": "wx-mini-code-001"
|
||||
"code": "wx-mini-code-001",
|
||||
"displayName": "微信旅人"
|
||||
})
|
||||
.to_string(),
|
||||
))
|
||||
@@ -2643,6 +2644,14 @@ mod tests {
|
||||
login_payload["user"]["loginMethod"],
|
||||
Value::String("wechat".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
login_payload["user"]["wechatDisplayName"],
|
||||
Value::String("微信旅人".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
login_payload["user"]["wechatAccount"],
|
||||
Value::String("wx-mini-code-001".to_string())
|
||||
);
|
||||
assert!(refresh_cookie.contains("genarrative_refresh_session="));
|
||||
|
||||
let sessions_response = app
|
||||
@@ -2667,16 +2676,23 @@ mod tests {
|
||||
let sessions_payload: Value =
|
||||
serde_json::from_slice(&sessions_body).expect("sessions payload should be json");
|
||||
assert_eq!(
|
||||
sessions_payload["sessions"][0]["clientType"],
|
||||
Value::String("mini_program".to_string())
|
||||
sessions_payload["sessions"][0]["clientLabel"],
|
||||
Value::String("微信小程序 / iPhone".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
sessions_payload["sessions"][0]["clientRuntime"],
|
||||
Value::String("wechat_mini_program".to_string())
|
||||
sessions_payload["sessions"][0]["sessionCount"],
|
||||
Value::Number(1.into())
|
||||
);
|
||||
assert_eq!(
|
||||
sessions_payload["sessions"][0]["miniProgramAppId"],
|
||||
Value::String("wx-mini-test".to_string())
|
||||
sessions_payload["sessions"][0]["isCurrent"],
|
||||
Value::Bool(true)
|
||||
);
|
||||
assert_eq!(
|
||||
sessions_payload["sessions"][0]["sessionIds"]
|
||||
.as_array()
|
||||
.expect("session ids should exist")
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2703,7 +2719,8 @@ mod tests {
|
||||
.header("x-mini-program-env", "develop")
|
||||
.body(Body::from(
|
||||
serde_json::json!({
|
||||
"code": "wx-mini-code-bind-001"
|
||||
"code": "wx-mini-code-bind-001",
|
||||
"displayName": "微信旅人"
|
||||
})
|
||||
.to_string(),
|
||||
))
|
||||
@@ -2745,7 +2762,8 @@ mod tests {
|
||||
.header("x-mini-program-env", "develop")
|
||||
.body(Body::from(
|
||||
serde_json::json!({
|
||||
"wechatPhoneCode": "13800138000"
|
||||
"wechatPhoneCode": "13800138000",
|
||||
"displayName": "微信旅人"
|
||||
})
|
||||
.to_string(),
|
||||
))
|
||||
@@ -2996,7 +3014,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn auth_sessions_returns_multi_device_session_fields() {
|
||||
async fn auth_sessions_returns_multi_device_session_summaries() {
|
||||
let state = AppState::new(AppConfig::default()).expect("state should build");
|
||||
seed_phone_user_with_password(&state, "13800138013", TEST_PASSWORD).await;
|
||||
let app = build_router(state);
|
||||
@@ -3096,23 +3114,19 @@ mod tests {
|
||||
|
||||
assert_eq!(sessions.len(), 2);
|
||||
assert!(sessions.iter().any(|session| {
|
||||
session["clientType"] == Value::String("web_browser".to_string())
|
||||
&& session["clientRuntime"] == Value::String("chrome".to_string())
|
||||
&& session["clientPlatform"] == Value::String("windows".to_string())
|
||||
session["clientLabel"] == Value::String("Windows / Chrome".to_string())
|
||||
&& session["sessionCount"] == Value::Number(1.into())
|
||||
&& session["sessionIds"]
|
||||
.as_array()
|
||||
.is_some_and(|ids| ids.len() == 1)
|
||||
&& session["deviceDisplayName"] == Value::String("Windows / Chrome".to_string())
|
||||
&& session["isCurrent"] == Value::Bool(true)
|
||||
}));
|
||||
assert!(sessions.iter().any(|session| {
|
||||
session["clientType"] == Value::String("mini_program".to_string())
|
||||
&& session["clientRuntime"] == Value::String("wechat_mini_program".to_string())
|
||||
session["clientLabel"] == Value::String("微信小程序 / Android".to_string())
|
||||
&& session["sessionCount"] == Value::Number(1.into())
|
||||
&& session["miniProgramAppId"] == Value::String("wx-session-test".to_string())
|
||||
&& session["miniProgramEnv"] == Value::String("release".to_string())
|
||||
&& session["deviceDisplayName"] == Value::String("微信小程序 / Android".to_string())
|
||||
&& session["sessionIds"]
|
||||
.as_array()
|
||||
.is_some_and(|ids| ids.len() == 1)
|
||||
&& session["isCurrent"] == Value::Bool(false)
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use shared_contracts::auth::{
|
||||
WechatMiniProgramLoginRequest, WechatMiniProgramLoginResponse, WechatStartQuery,
|
||||
WechatStartResponse,
|
||||
};
|
||||
use shared_kernel::normalize_optional_string;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
|
||||
@@ -208,6 +209,7 @@ pub async fn bind_wechat_phone(
|
||||
.bind_wechat_verified_phone(BindWechatVerifiedPhoneInput {
|
||||
user_id: authenticated.claims().user_id().to_string(),
|
||||
phone_number: phone_profile.phone_number,
|
||||
wechat_display_name: payload.display_name.clone(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_wechat_bind_phone_error)?
|
||||
@@ -235,6 +237,7 @@ pub async fn bind_wechat_phone(
|
||||
user_id: authenticated.claims().user_id().to_string(),
|
||||
phone_number: phone.to_string(),
|
||||
verify_code: code.to_string(),
|
||||
wechat_display_name: payload.display_name.clone(),
|
||||
},
|
||||
OffsetDateTime::now_utc(),
|
||||
)
|
||||
@@ -313,7 +316,7 @@ pub async fn login_wechat_mini_program(
|
||||
let result = state
|
||||
.wechat_auth_service()
|
||||
.resolve_login(module_auth::ResolveWechatLoginInput {
|
||||
profile: map_wechat_profile_to_domain(profile),
|
||||
profile: map_wechat_profile_to_domain_with_display_name(profile, payload.display_name),
|
||||
})
|
||||
.await
|
||||
.map_err(map_wechat_auth_error)?;
|
||||
@@ -389,6 +392,17 @@ fn map_wechat_profile_to_domain(
|
||||
}
|
||||
}
|
||||
|
||||
fn map_wechat_profile_to_domain_with_display_name(
|
||||
profile: platform_auth::WechatIdentityProfile,
|
||||
display_name: Option<String>,
|
||||
) -> module_auth::WechatIdentityProfile {
|
||||
let mut profile = map_wechat_profile_to_domain(profile);
|
||||
if let Some(display_name) = normalize_optional_string(display_name) {
|
||||
profile.display_name = Some(display_name);
|
||||
}
|
||||
profile
|
||||
}
|
||||
|
||||
fn normalize_redirect_path(raw_value: Option<&str>, fallback: &str) -> String {
|
||||
let Some(raw_value) = raw_value.map(str::trim).filter(|value| !value.is_empty()) else {
|
||||
return fallback.to_string();
|
||||
|
||||
@@ -65,12 +65,14 @@ pub struct BindWechatPhoneInput {
|
||||
pub user_id: String,
|
||||
pub phone_number: String,
|
||||
pub verify_code: String,
|
||||
pub wechat_display_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct BindWechatVerifiedPhoneInput {
|
||||
pub user_id: String,
|
||||
pub phone_number: String,
|
||||
pub wechat_display_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
||||
@@ -111,11 +111,7 @@ fn hydrate_private_auth_fields(
|
||||
.find(|identity| identity.user_id == hydrated.user.id);
|
||||
if hydrated.user.wechat_display_name.is_none() {
|
||||
hydrated.user.wechat_display_name = hydrated_wechat_identity
|
||||
.and_then(|identity| identity.display_name.clone())
|
||||
.or_else(|| {
|
||||
(hydrated.user.login_method == AuthLoginMethod::Wechat)
|
||||
.then(|| hydrated.user.display_name.clone())
|
||||
});
|
||||
.and_then(|identity| normalize_optional_string(identity.display_name.clone()));
|
||||
}
|
||||
if hydrated.user.wechat_account.is_none() {
|
||||
hydrated.user.wechat_account =
|
||||
@@ -655,9 +651,11 @@ impl PhoneAuthService {
|
||||
return Err(PhoneAuthError::UserStateMismatch);
|
||||
}
|
||||
|
||||
let (merged_user, activated_new_user) = self
|
||||
.store
|
||||
.bind_wechat_phone_to_user(&input.user_id, normalized_phone)?;
|
||||
let (merged_user, activated_new_user) = self.store.bind_wechat_phone_to_user(
|
||||
&input.user_id,
|
||||
normalized_phone,
|
||||
input.wechat_display_name,
|
||||
)?;
|
||||
|
||||
Ok(BindWechatPhoneResult {
|
||||
user: merged_user,
|
||||
@@ -711,9 +709,11 @@ impl PhoneAuthService {
|
||||
return Err(PhoneAuthError::UserStateMismatch);
|
||||
}
|
||||
|
||||
let (merged_user, activated_new_user) = self
|
||||
.store
|
||||
.bind_wechat_phone_to_user(&input.user_id, normalized_phone)?;
|
||||
let (merged_user, activated_new_user) = self.store.bind_wechat_phone_to_user(
|
||||
&input.user_id,
|
||||
normalized_phone,
|
||||
input.wechat_display_name,
|
||||
)?;
|
||||
|
||||
Ok(BindWechatPhoneResult {
|
||||
user: merged_user,
|
||||
@@ -1365,8 +1365,7 @@ impl InMemoryAuthStore {
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or("微信旅人")
|
||||
.to_string();
|
||||
let wechat_display_name = normalize_optional_string(profile.display_name.clone())
|
||||
.or_else(|| Some(display_name.clone()));
|
||||
let wechat_display_name = normalize_optional_string(profile.display_name.clone());
|
||||
let username = build_wechat_username(&display_name, &profile.provider_uid);
|
||||
let provider_uid = normalize_required_string(&profile.provider_uid).unwrap_or_default();
|
||||
let user = AuthUser {
|
||||
@@ -1758,11 +1757,13 @@ impl InMemoryAuthStore {
|
||||
&self,
|
||||
pending_user_id: &str,
|
||||
phone_number: PhoneNumberSnapshot,
|
||||
wechat_display_name: Option<String>,
|
||||
) -> Result<(AuthUser, bool), PhoneAuthError> {
|
||||
let mut state = self
|
||||
.inner
|
||||
.lock()
|
||||
.map_err(|_| PhoneAuthError::Store("用户仓储锁已中毒".to_string()))?;
|
||||
let submitted_wechat_display_name = normalize_optional_string(wechat_display_name);
|
||||
|
||||
let existing_phone_user_id =
|
||||
Self::resolve_phone_user_locked(&mut state, &phone_number.e164)
|
||||
@@ -1777,20 +1778,24 @@ impl InMemoryAuthStore {
|
||||
.cloned()
|
||||
.ok_or(PhoneAuthError::UserStateMismatch)?;
|
||||
let pending_wechat_account = pending_wechat_identity.provider_uid.clone();
|
||||
let pending_wechat_display_name = pending_wechat_identity.display_name.clone();
|
||||
|
||||
let pending_username = state
|
||||
let pending_user = state
|
||||
.users_by_username
|
||||
.values()
|
||||
.find(|stored| stored.user.id == pending_user_id)
|
||||
.map(|stored| stored.user.username.clone())
|
||||
.cloned()
|
||||
.ok_or(PhoneAuthError::UserNotFound)?;
|
||||
let pending_username = pending_user.user.username.clone();
|
||||
let pending_wechat_display_name = submitted_wechat_display_name
|
||||
.clone()
|
||||
.or_else(|| normalize_optional_string(pending_wechat_identity.display_name.clone()))
|
||||
.or_else(|| normalize_optional_string(pending_user.user.wechat_display_name));
|
||||
state.users_by_username.remove(&pending_username);
|
||||
|
||||
state.wechat_identity_by_provider_uid.insert(
|
||||
pending_wechat_identity.provider_uid.clone(),
|
||||
StoredWechatIdentity {
|
||||
user_id: target_user_id.clone(),
|
||||
display_name: pending_wechat_display_name.clone(),
|
||||
..pending_wechat_identity.clone()
|
||||
},
|
||||
);
|
||||
@@ -1825,11 +1830,31 @@ impl InMemoryAuthStore {
|
||||
.values()
|
||||
.find(|identity| identity.user_id == pending_user_id)
|
||||
.map(|identity| identity.provider_uid.clone());
|
||||
let bound_wechat_display_name = state
|
||||
.wechat_identity_by_provider_uid
|
||||
.values()
|
||||
.find(|identity| identity.user_id == pending_user_id)
|
||||
.and_then(|identity| identity.display_name.clone());
|
||||
let bound_wechat_display_name = submitted_wechat_display_name.clone().or_else(|| {
|
||||
state
|
||||
.wechat_identity_by_provider_uid
|
||||
.values()
|
||||
.find(|identity| identity.user_id == pending_user_id)
|
||||
.and_then(|identity| normalize_optional_string(identity.display_name.clone()))
|
||||
.or_else(|| {
|
||||
state
|
||||
.users_by_username
|
||||
.values()
|
||||
.find(|stored| stored.user.id == pending_user_id)
|
||||
.and_then(|stored| {
|
||||
normalize_optional_string(stored.user.wechat_display_name.clone())
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(display_name) = bound_wechat_display_name.clone()
|
||||
&& let Some(identity) = state
|
||||
.wechat_identity_by_provider_uid
|
||||
.values_mut()
|
||||
.find(|identity| identity.user_id == pending_user_id)
|
||||
{
|
||||
identity.display_name = Some(display_name);
|
||||
}
|
||||
|
||||
let stored_user = state
|
||||
.users_by_username
|
||||
@@ -3584,6 +3609,7 @@ mod tests {
|
||||
user_id: wechat_user.id.clone(),
|
||||
phone_number: "13800138000".to_string(),
|
||||
verify_code: "123456".to_string(),
|
||||
wechat_display_name: None,
|
||||
},
|
||||
now + Duration::seconds(3),
|
||||
)
|
||||
@@ -3619,4 +3645,97 @@ mod tests {
|
||||
Some("已归并微信用户")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bind_wechat_phone_keeps_account_marker_when_identity_has_no_display_name() {
|
||||
let store = build_store();
|
||||
let phone_service = build_phone_service(store.clone());
|
||||
let wechat_service = WechatAuthService::new(store.clone());
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
phone_service
|
||||
.send_code(
|
||||
SendPhoneCodeInput {
|
||||
phone_number: "13800138031".to_string(),
|
||||
scene: PhoneAuthScene::Login,
|
||||
},
|
||||
now,
|
||||
)
|
||||
.await
|
||||
.expect("phone login code should send");
|
||||
let phone_user = phone_service
|
||||
.login(
|
||||
PhoneLoginInput {
|
||||
phone_number: "13800138031".to_string(),
|
||||
verify_code: "123456".to_string(),
|
||||
},
|
||||
now + Duration::seconds(1),
|
||||
)
|
||||
.await
|
||||
.expect("phone login should succeed")
|
||||
.user;
|
||||
|
||||
let wechat_user = wechat_service
|
||||
.resolve_login(ResolveWechatLoginInput {
|
||||
profile: WechatIdentityProfile {
|
||||
provider_uid: "wx-openid-mini-bind".to_string(),
|
||||
provider_union_id: Some("wx-union-mini-bind".to_string()),
|
||||
display_name: None,
|
||||
avatar_url: None,
|
||||
session_key: Some("mini-session-key".to_string()),
|
||||
},
|
||||
})
|
||||
.await
|
||||
.expect("mini program wechat login should succeed")
|
||||
.user;
|
||||
|
||||
assert_eq!(wechat_user.wechat_display_name, None);
|
||||
assert_eq!(
|
||||
wechat_user.wechat_account.as_deref(),
|
||||
Some("wx-openid-mini-bind")
|
||||
);
|
||||
assert_ne!(wechat_user.id, phone_user.id);
|
||||
|
||||
phone_service
|
||||
.send_code(
|
||||
SendPhoneCodeInput {
|
||||
phone_number: "13800138031".to_string(),
|
||||
scene: PhoneAuthScene::BindPhone,
|
||||
},
|
||||
now + Duration::seconds(2),
|
||||
)
|
||||
.await
|
||||
.expect("bind phone code should send");
|
||||
let merged = phone_service
|
||||
.bind_wechat_phone(
|
||||
BindWechatPhoneInput {
|
||||
user_id: wechat_user.id.clone(),
|
||||
phone_number: "13800138031".to_string(),
|
||||
verify_code: "123456".to_string(),
|
||||
wechat_display_name: None,
|
||||
},
|
||||
now + Duration::seconds(3),
|
||||
)
|
||||
.await
|
||||
.expect("bind phone should succeed");
|
||||
|
||||
assert_eq!(merged.user.id, phone_user.id);
|
||||
assert!(merged.user.wechat_bound);
|
||||
assert_eq!(merged.user.wechat_display_name, None);
|
||||
assert_eq!(
|
||||
merged.user.wechat_account.as_deref(),
|
||||
Some("wx-openid-mini-bind")
|
||||
);
|
||||
|
||||
let restored_user = build_password_service(store)
|
||||
.get_user_by_id(&phone_user.id)
|
||||
.expect("user lookup should succeed")
|
||||
.expect("merged user should exist")
|
||||
.user;
|
||||
assert_eq!(restored_user.wechat_display_name, None);
|
||||
assert_eq!(
|
||||
restored_user.wechat_account.as_deref(),
|
||||
Some("wx-openid-mini-bind")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,7 +796,7 @@ impl WechatProvider {
|
||||
) -> Result<WechatIdentityProfile, WechatProviderError> {
|
||||
match self {
|
||||
Self::Disabled => Err(WechatProviderError::Disabled),
|
||||
Self::Mock(provider) => Ok(provider.resolve_callback_profile(code)),
|
||||
Self::Mock(provider) => Ok(provider.resolve_mini_program_login_profile(code)),
|
||||
Self::Real(provider) => provider.resolve_mini_program_login_profile(code).await,
|
||||
}
|
||||
}
|
||||
@@ -839,6 +839,21 @@ impl MockWechatProvider {
|
||||
session_key: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_mini_program_login_profile(&self, mock_code: Option<&str>) -> WechatIdentityProfile {
|
||||
let provider_uid = mock_code
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or(self.mock_user_id.as_str())
|
||||
.to_string();
|
||||
WechatIdentityProfile {
|
||||
provider_uid: provider_uid.clone(),
|
||||
provider_union_id: self.mock_union_id.clone(),
|
||||
display_name: None,
|
||||
avatar_url: None,
|
||||
session_key: Some(format!("mock-session-key-{provider_uid}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RealWechatProvider {
|
||||
@@ -2274,6 +2289,42 @@ mod tests {
|
||||
assert_eq!(profile.display_name.as_deref(), Some("微信测试用户"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mock_wechat_provider_resolves_mini_program_profile_without_nickname() {
|
||||
let provider = WechatProvider::new(WechatAuthConfig::new(
|
||||
true,
|
||||
"mock".to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
DEFAULT_WECHAT_AUTHORIZE_ENDPOINT.to_string(),
|
||||
DEFAULT_WECHAT_ACCESS_TOKEN_ENDPOINT.to_string(),
|
||||
DEFAULT_WECHAT_USER_INFO_ENDPOINT.to_string(),
|
||||
DEFAULT_WECHAT_JS_CODE_SESSION_ENDPOINT.to_string(),
|
||||
DEFAULT_WECHAT_STABLE_ACCESS_TOKEN_ENDPOINT.to_string(),
|
||||
DEFAULT_WECHAT_PHONE_NUMBER_ENDPOINT.to_string(),
|
||||
"wx-user-001".to_string(),
|
||||
Some("wx-union-001".to_string()),
|
||||
"微信测试用户".to_string(),
|
||||
Some("https://example.test/avatar.png".to_string()),
|
||||
));
|
||||
|
||||
let profile = provider
|
||||
.resolve_mini_program_login_profile(Some("wx-mini-code-001"))
|
||||
.await
|
||||
.expect("mock mini program profile should resolve");
|
||||
|
||||
assert_eq!(profile.provider_uid, "wx-mini-code-001");
|
||||
assert_eq!(profile.provider_union_id.as_deref(), Some("wx-union-001"));
|
||||
assert_eq!(profile.display_name, None);
|
||||
assert_eq!(profile.avatar_url, None);
|
||||
assert_eq!(
|
||||
profile.session_key.as_deref(),
|
||||
Some("mock-session-key-wx-mini-code-001")
|
||||
);
|
||||
}
|
||||
|
||||
fn build_jwt_config() -> JwtConfig {
|
||||
JwtConfig::new(
|
||||
"https://auth.genarrative.local".to_string(),
|
||||
|
||||
@@ -228,6 +228,8 @@ pub struct WechatBindPhoneRequest {
|
||||
pub code: Option<String>,
|
||||
#[serde(default)]
|
||||
pub wechat_phone_code: Option<String>,
|
||||
#[serde(default)]
|
||||
pub display_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -241,6 +243,8 @@ pub struct WechatBindPhoneResponse {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WechatMiniProgramLoginRequest {
|
||||
pub code: String,
|
||||
#[serde(default)]
|
||||
pub display_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -354,6 +358,7 @@ mod tests {
|
||||
phone: None,
|
||||
code: None,
|
||||
wechat_phone_code: Some("wx-phone-code-001".to_string()),
|
||||
display_name: Some("陶泥儿玩家".to_string()),
|
||||
})
|
||||
.expect("payload should serialize");
|
||||
|
||||
@@ -362,7 +367,25 @@ mod tests {
|
||||
json!({
|
||||
"phone": null,
|
||||
"code": null,
|
||||
"wechatPhoneCode": "wx-phone-code-001"
|
||||
"wechatPhoneCode": "wx-phone-code-001",
|
||||
"displayName": "陶泥儿玩家"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wechat_mini_program_login_request_accepts_native_nickname() {
|
||||
let payload = serde_json::to_value(WechatMiniProgramLoginRequest {
|
||||
code: "wx-mini-code-001".to_string(),
|
||||
display_name: Some("陶泥儿玩家".to_string()),
|
||||
})
|
||||
.expect("payload should serialize");
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!({
|
||||
"code": "wx-mini-code-001",
|
||||
"displayName": "陶泥儿玩家"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user