109 lines
3.0 KiB
TypeScript
109 lines
3.0 KiB
TypeScript
|
|
import { tmpdir } from 'node:os'
|
||
|
|
import { join } from 'node:path'
|
||
|
|
|
||
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||
|
|
|
||
|
|
import type { AuthProbeResult } from './auth-types'
|
||
|
|
|
||
|
|
const probeAIAccountSession = vi.fn()
|
||
|
|
const probePublishAccountSession = vi.fn()
|
||
|
|
const silentRefreshAIAccountSession = vi.fn()
|
||
|
|
const silentRefreshPublishAccountSession = vi.fn()
|
||
|
|
const getSessionHandle = vi.fn()
|
||
|
|
const getPersistedPartition = vi.fn()
|
||
|
|
const emitRuntimeInvalidated = vi.fn()
|
||
|
|
|
||
|
|
vi.mock('electron/main', () => ({
|
||
|
|
app: {
|
||
|
|
getPath: () => join(tmpdir(), 'geo-rankly-account-health-test'),
|
||
|
|
},
|
||
|
|
}))
|
||
|
|
|
||
|
|
vi.mock('./account-binder', () => ({
|
||
|
|
probeAIAccountSession,
|
||
|
|
probePublishAccountSession,
|
||
|
|
silentRefreshAIAccountSession,
|
||
|
|
silentRefreshPublishAccountSession,
|
||
|
|
}))
|
||
|
|
|
||
|
|
vi.mock('./session-registry', () => ({
|
||
|
|
getSessionHandle,
|
||
|
|
getPersistedPartition,
|
||
|
|
}))
|
||
|
|
|
||
|
|
vi.mock('./runtime-events', () => ({
|
||
|
|
emitRuntimeInvalidated,
|
||
|
|
}))
|
||
|
|
|
||
|
|
const account = {
|
||
|
|
id: 'account-1',
|
||
|
|
platform: 'doubao',
|
||
|
|
platformUid: 'doubao-session:demo',
|
||
|
|
displayName: '豆包账号',
|
||
|
|
}
|
||
|
|
|
||
|
|
function challenge(reason: AuthProbeResult['reason'] = 'captcha_gate'): AuthProbeResult {
|
||
|
|
return {
|
||
|
|
verdict: 'challenge_required',
|
||
|
|
reason,
|
||
|
|
profile: null,
|
||
|
|
sessionFingerprint: null,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function active(): AuthProbeResult {
|
||
|
|
return {
|
||
|
|
verdict: 'active',
|
||
|
|
reason: 'probe_success',
|
||
|
|
profile: {
|
||
|
|
displayName: '豆包账号',
|
||
|
|
avatarUrl: null,
|
||
|
|
subjectUid: 'doubao-session:demo',
|
||
|
|
},
|
||
|
|
sessionFingerprint: 'fingerprint-live',
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('account health challenge recheck', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
vi.resetModules()
|
||
|
|
vi.clearAllMocks()
|
||
|
|
getSessionHandle.mockReturnValue({ partition: 'persist:test' })
|
||
|
|
getPersistedPartition.mockReturnValue('persist:test')
|
||
|
|
silentRefreshAIAccountSession.mockResolvedValue(true)
|
||
|
|
silentRefreshPublishAccountSession.mockResolvedValue(true)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('hides a first challenge result when the automatic recheck succeeds', async () => {
|
||
|
|
probeAIAccountSession.mockResolvedValueOnce(challenge()).mockResolvedValueOnce(active())
|
||
|
|
const { probeTrackedAccount } = await import('./account-health')
|
||
|
|
|
||
|
|
const snapshot = await probeTrackedAccount(account, {
|
||
|
|
trigger: 'manual',
|
||
|
|
allowSilentRefresh: true,
|
||
|
|
force: true,
|
||
|
|
})
|
||
|
|
|
||
|
|
expect(probeAIAccountSession).toHaveBeenCalledTimes(2)
|
||
|
|
expect(snapshot.authState).toBe('active')
|
||
|
|
expect(snapshot.authReason).toBe('probe_success')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('shows manual verification only after the automatic recheck still needs it', async () => {
|
||
|
|
probeAIAccountSession
|
||
|
|
.mockResolvedValueOnce(challenge('risk_control'))
|
||
|
|
.mockResolvedValueOnce(challenge('risk_control'))
|
||
|
|
const { probeTrackedAccount } = await import('./account-health')
|
||
|
|
|
||
|
|
const snapshot = await probeTrackedAccount(account, {
|
||
|
|
trigger: 'manual',
|
||
|
|
allowSilentRefresh: true,
|
||
|
|
force: true,
|
||
|
|
})
|
||
|
|
|
||
|
|
expect(probeAIAccountSession).toHaveBeenCalledTimes(2)
|
||
|
|
expect(snapshot.authState).toBe('challenge_required')
|
||
|
|
expect(snapshot.authReason).toBe('risk_control')
|
||
|
|
})
|
||
|
|
})
|