From fd711be0bea75b8a5c80e0154b58deb02fe32f45 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sun, 6 Sep 2026 18:16:09 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(auth):=20=E6=96=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E6=98=B5=E7=A7=B0=E6=94=B9=E4=B8=BA"?= =?UTF-8?q?=E5=B0=8F=E8=99=BE=E5=90=8C=E5=AD=A6"=EF=BC=8C=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=98=B5=E7=A7=B0=E4=B8=8D=E5=86=8D=E4=BD=9C=E4=B8=BA?= =?UTF-8?q?=E6=98=B5=E7=A7=B0=E6=9D=A5=E6=BA=90=20(update=2076d04e3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/application/auth/wechat_sync_use_case.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/application/auth/wechat_sync_use_case.py b/packages/application/auth/wechat_sync_use_case.py index 76d04e326..eae3b17e1 100644 --- a/packages/application/auth/wechat_sync_use_case.py +++ b/packages/application/auth/wechat_sync_use_case.py @@ -34,7 +34,7 @@ class WechatSyncRequest: ): self.openid = openid.strip() self.unionid = unionid.strip() if unionid else "" - self.nickname = nickname or "微信用户" + self.nickname = nickname or "小虾同学" # 微信新规拿不到真实昵称,新用户默认昵称 self.avatar_url = avatar_url or "" self.source = source @@ -227,7 +227,7 @@ class WechatSyncUseCase: id=user_id, email=email, username=username, - display_name=request.nickname or "微信用户", + display_name=request.nickname or "小虾同学", password_hash=password_hash, email_verified=True, # 微信登录视为已验证 wechat_openid=request.openid, -- 2.54.0 From 25ca33dbaa6eaecc8480e6002f14c3c29191f36d Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sun, 6 Sep 2026 18:16:21 +0800 Subject: [PATCH 2/3] =?UTF-8?q?test(auth):=20=E6=96=B0=E5=A2=9E=E6=98=B5?= =?UTF-8?q?=E7=A7=B0=E8=A7=84=E5=88=99=E5=8D=95=E6=B5=8B=E2=80=94=E2=80=94?= =?UTF-8?q?=E5=AD=98=E9=87=8F=E7=94=A8=E6=88=B7=E6=98=B5=E7=A7=B0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E4=B8=8D=E8=A6=86=E7=9B=96=E3=80=81=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=98=B5=E7=A7=B0=E3=80=81=E4=B8=AD=E6=96=87=E6=98=B5=E7=A7=B0?= =?UTF-8?q?=E5=85=A8=E9=93=BE=E8=B7=AF=E6=97=A0=E4=B9=B1=E7=A0=81=20(updat?= =?UTF-8?q?e=20b19d212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit/test_wechat_sync_use_case.py | 84 ++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_wechat_sync_use_case.py b/tests/unit/test_wechat_sync_use_case.py index b19d212c3..9776faf9b 100755 --- a/tests/unit/test_wechat_sync_use_case.py +++ b/tests/unit/test_wechat_sync_use_case.py @@ -60,7 +60,8 @@ class TestWechatSyncRequest: assert WechatSyncRequest(openid="o1", unionid=" unionid_456 ").unionid == "unionid_456" def test_default_nickname(self): - assert WechatSyncRequest(openid="o1").nickname == "微信用户" + # 微信新规拿不到真实昵称,新用户统一默认"小虾同学" + assert WechatSyncRequest(openid="o1").nickname == "小虾同学" def test_default_source(self): assert WechatSyncRequest(openid="o1").source == "miniapp" @@ -319,3 +320,84 @@ class TestWechatSyncSession: assert kw["user_id"] == "user_001" assert "wechat_miniapp" in kw["device_info"] assert kw["expires_in_seconds"] == 30 * 24 * 3600 + + +class TestWechatSyncNicknameRules: + """昵称规则:默认昵称、登录不覆盖存量昵称、中文昵称全链路无损。""" + + def test_existing_user_display_name_never_overwritten_on_login( + self, mock_user_repo, mock_session_store + ): + """守护规则:已存在用户登录时,即使请求带了别的 nickname, + 其 display_name 也绝不能被覆盖(只有建号时写入)。""" + user = User( + id="user_custom", + email="c@wechat.local", + username="wx_custom", + display_name="我的专属昵称", + password_hash="h", + email_verified=True, + wechat_openid="openid_123", + wechat_unionid="unionid_456", + ) + mock_user_repo.find_by_wechat_openid.return_value = user + mock_user_repo.find_by_wechat_unionid.return_value = user + + resp, err = make_use_case(mock_user_repo, mock_session_store).execute( + WechatSyncRequest( + openid="openid_123", + unionid="unionid_456", + nickname="小虾同学", # 请求里带的任何昵称都不准覆盖 + ) + ) + assert err is None + assert resp.is_new_user is False + # 数据层用户昵称不变 + assert user.display_name == "我的专属昵称" + # 返回给调用方的昵称也是存量昵称 + assert resp.nickname == "我的专属昵称" + # save 的都是同一个用户对象,且 display_name 未被改写 + for call in mock_user_repo.save.call_args_list: + assert call.args[0].display_name == "我的专属昵称" + + def test_new_user_gets_default_nickname_when_nickname_empty( + self, mock_user_repo, mock_session_store + ): + """新用户且请求未带昵称(微信新规场景)→ 默认昵称小虾同学。""" + saved = {} + mock_user_repo.save.side_effect = lambda u: saved.update({u.id: u}) + resp, err = make_use_case(mock_user_repo, mock_session_store).execute( + WechatSyncRequest(openid="new_oid_default", unionid="new_uid_default", nickname="") + ) + assert err is None + assert resp.is_new_user is True + u = saved[resp.user_id] + assert u.display_name == "小虾同学" + assert resp.nickname == "小虾同学" + + def test_chinese_nickname_full_chain_no_mojibake( + self, mock_user_repo, mock_session_store + ): + """中文昵称全链路无损:建号写入 → 仓储对象 → 响应返回, + 不允许出现 latin-1/utf-8 双重编码乱码(如 \u00e8\u0080\u0081 一类)。""" + chinese_name = "老字号🏮肩腰腿🏮御众堂永康店" + saved = {} + mock_user_repo.save.side_effect = lambda u: saved.update({u.id: u}) + + resp, err = make_use_case(mock_user_repo, mock_session_store).execute( + WechatSyncRequest( + openid="new_oid_cn", + unionid="new_uid_cn", + nickname=chinese_name, + ) + ) + assert err is None + u = saved[resp.user_id] + # 写入仓储对象的昵称必须与原文完全一致 + assert u.display_name == chinese_name + assert resp.nickname == chinese_name + assert resp.to_dict()["user"]["display_name"] == chinese_name + assert resp.to_dict()["user_info"]["display_name"] == chinese_name + # 乱码特征:双重编码后必然出现Ã/\u00c2 拼音字母或控制字符 + assert "\u00c3" not in u.display_name and "\u00c2" not in u.display_name + assert "\u00e8" not in u.display_name -- 2.54.0 From da9d719e83e2061e0638c2406ae98be33288ee2c Mon Sep 17 00:00:00 2001 From: CI Bot Date: Sun, 6 Sep 2026 10:19:10 +0000 Subject: [PATCH 3/3] style: auto-format with black + isort + prettier [skip ci-format-check] --- tests/unit/test_wechat_sync_use_case.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/unit/test_wechat_sync_use_case.py b/tests/unit/test_wechat_sync_use_case.py index 9776faf9b..eb12e4598 100755 --- a/tests/unit/test_wechat_sync_use_case.py +++ b/tests/unit/test_wechat_sync_use_case.py @@ -325,9 +325,7 @@ class TestWechatSyncSession: class TestWechatSyncNicknameRules: """昵称规则:默认昵称、登录不覆盖存量昵称、中文昵称全链路无损。""" - def test_existing_user_display_name_never_overwritten_on_login( - self, mock_user_repo, mock_session_store - ): + def test_existing_user_display_name_never_overwritten_on_login(self, mock_user_repo, mock_session_store): """守护规则:已存在用户登录时,即使请求带了别的 nickname, 其 display_name 也绝不能被覆盖(只有建号时写入)。""" user = User( @@ -360,9 +358,7 @@ class TestWechatSyncNicknameRules: for call in mock_user_repo.save.call_args_list: assert call.args[0].display_name == "我的专属昵称" - def test_new_user_gets_default_nickname_when_nickname_empty( - self, mock_user_repo, mock_session_store - ): + def test_new_user_gets_default_nickname_when_nickname_empty(self, mock_user_repo, mock_session_store): """新用户且请求未带昵称(微信新规场景)→ 默认昵称小虾同学。""" saved = {} mock_user_repo.save.side_effect = lambda u: saved.update({u.id: u}) @@ -375,9 +371,7 @@ class TestWechatSyncNicknameRules: assert u.display_name == "小虾同学" assert resp.nickname == "小虾同学" - def test_chinese_nickname_full_chain_no_mojibake( - self, mock_user_repo, mock_session_store - ): + def test_chinese_nickname_full_chain_no_mojibake(self, mock_user_repo, mock_session_store): """中文昵称全链路无损:建号写入 → 仓储对象 → 响应返回, 不允许出现 latin-1/utf-8 双重编码乱码(如 \u00e8\u0080\u0081 一类)。""" chinese_name = "老字号🏮肩腰腿🏮御众堂永康店" -- 2.54.0