收敛邀请码标签元数据校验

将邀请码 metadata.tags 写入时收敛为 userTags。

拒绝非数组或非字符串 tags 元数据。

补充链接邀请码自动兑换失败时的前端测试。
This commit is contained in:
2026-07-01 18:03:15 +08:00
parent 71a6ed5945
commit 69b4d5f090
2 changed files with 95 additions and 1 deletions
+42
View File
@@ -689,6 +689,48 @@ test('auth gate auto redeems link invite code for new sms account', async () =>
expect(authMocks.redeemRegistrationInviteCode).not.toHaveBeenCalled();
});
test('auth gate keeps invite modal open when link invite auto redeem fails', async () => {
const user = userEvent.setup();
window.history.replaceState(null, '', '/?inviteCode=spring-2026');
authMocks.getAuthLoginOptions.mockResolvedValue({
availableLoginMethods: ['phone'],
});
authMocks.loginWithPhoneCode.mockResolvedValueOnce({
token: 'jwt-phone-new',
user: mockUser,
created: true,
referral: {
ok: false,
message: '邀请码无效,已继续注册',
inviteeRewardGranted: false,
inviterRewardGranted: false,
inviteeBalanceAfter: null,
inviterBalanceAfter: null,
},
});
render(
<AuthGate>
<ProtectedActionButton onAuthenticated={vi.fn()} />
</AuthGate>,
);
await user.click(await screen.findByRole('button', { name: '进入作品' }));
const dialog = await screen.findByRole('dialog', { name: '账号入口' });
await user.type(within(dialog).getByLabelText('手机号'), '13800000000');
await user.type(within(dialog).getByLabelText('验证码'), '123456');
await acceptLegalConsent(user, dialog);
await user.click(within(dialog).getByRole('button', { name: '登录' }));
const inviteDialog = await screen.findByRole('dialog', {
name: '请填写邀请码',
});
expect(
(within(inviteDialog).getByLabelText('邀请码') as HTMLInputElement).value,
).toBe('SPRING2026');
expect(within(inviteDialog).getByText('邀请码无效,已继续注册')).toBeTruthy();
});
test('registration invite modal can skip when invite code is empty', async () => {
const user = userEvent.setup();
authMocks.getAuthLoginOptions.mockResolvedValue({