mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-15 04:18:20 +00:00
fix(namespace): refresh invitation state before creating link
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
parent
3de13c033f
commit
efbb50f4aa
3 changed files with 47 additions and 3 deletions
|
|
@ -116,3 +116,8 @@ API SHALL 使用 Issue #911 的路径与响应合同。
|
|||
- **THEN** 按原方式安全捕获并清除片段,保持兼容
|
||||
- **WHEN** 新的具名片段格式无效
|
||||
- **THEN** 清除先前暂存凭证,不能误用上一条邀请;无片段的登录回跳仍恢复合法暂存邀请
|
||||
|
||||
#### Scenario: 他人消费邀请后生成新链接
|
||||
- **WHEN** 他人已消费邀请,而管理员页面还缓存旧的可兑换状态
|
||||
- **THEN** 点击生成先刷新服务端状态,已消费或过期的邀请不误弹替换确认,仍可兑换的邀请必须确认
|
||||
- **AND** 状态刷新失败时不沿用旧缓存生成或替换,显示可重试错误
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ describe('InvitationControls', () => {
|
|||
api.createInvitation.mockRejectedValue(new Error('unavailable'))
|
||||
api.revokeInvitation.mockRejectedValue(new Error('unavailable'))
|
||||
const dialog = await openConfirmation(action)
|
||||
if (action === 'replace') {
|
||||
expect(api.invitationStatus).toHaveBeenCalledTimes(2)
|
||||
expect(api.createInvitation).not.toHaveBeenCalled()
|
||||
}
|
||||
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: 'dialog.confirm' }))
|
||||
|
||||
|
|
@ -41,4 +45,37 @@ describe('InvitationControls', () => {
|
|||
expect(screen.getByRole('alert').textContent).toBe('invitation.error')
|
||||
expect(action === 'replace' ? api.createInvitation : api.revokeInvitation).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it.each(['consumed', 'expired'])('creates without a replacement prompt when the cached link was %s', async () => {
|
||||
api.invitationStatus.mockResolvedValueOnce({ active: true }).mockResolvedValue({ active: false })
|
||||
api.createInvitation.mockImplementation(() => new Promise(() => {}))
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } })
|
||||
render(<QueryClientProvider client={client}><InvitationControls slug="team" /></QueryClientProvider>)
|
||||
const createButton = screen.getByRole('button', { name: 'invitation.create' })
|
||||
await waitFor(() => expect((createButton as HTMLButtonElement).disabled).toBe(false))
|
||||
expect(screen.getByRole('button', { name: 'invitation.revoke' })).toBeTruthy()
|
||||
|
||||
fireEvent.click(createButton)
|
||||
|
||||
await waitFor(() => expect(api.createInvitation).toHaveBeenCalledWith('team', false))
|
||||
expect(api.invitationStatus).toHaveBeenCalledTimes(2)
|
||||
expect(screen.queryByRole('dialog')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not create or confirm replacement when the fresh status fails despite cached active data', async () => {
|
||||
api.invitationStatus.mockResolvedValueOnce({ active: true }).mockRejectedValue(new Error('unavailable'))
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } })
|
||||
render(<QueryClientProvider client={client}><InvitationControls slug="team" /></QueryClientProvider>)
|
||||
const createButton = screen.getByRole('button', { name: 'invitation.create' })
|
||||
await waitFor(() => expect((createButton as HTMLButtonElement).disabled).toBe(false))
|
||||
|
||||
fireEvent.click(createButton)
|
||||
|
||||
await screen.findByRole('alert')
|
||||
expect(api.invitationStatus).toHaveBeenCalledTimes(2)
|
||||
expect(api.createInvitation).not.toHaveBeenCalled()
|
||||
expect(api.revokeInvitation).not.toHaveBeenCalled()
|
||||
expect(screen.queryByRole('dialog')).toBeNull()
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
|||
|
|
@ -41,9 +41,11 @@ export function InvitationControls({ slug, compact = false }: { slug: string; co
|
|||
},
|
||||
})
|
||||
const handleCreate = async () => {
|
||||
const current = compact ? (await status.refetch()).data : status.data
|
||||
if (current?.active) setConfirm('replace')
|
||||
else if (current) create.mutate(false)
|
||||
const result = await status.refetch()
|
||||
if (result.isError || !result.data) return
|
||||
const current = result.data
|
||||
if (current.active) setConfirm('replace')
|
||||
else create.mutate(false)
|
||||
}
|
||||
return (
|
||||
<div id={compact ? undefined : 'invite'} className="space-y-2 scroll-mt-24" onClick={compact ? (event) => event.stopPropagation() : undefined}>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue