SEC_Warehouse/tests/unit/login/wxLoginFlow.spec.ts

156 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect, beforeEach, vi } from 'vitest'
const mockLogin = vi.hoisted(() => vi.fn())
const mockGetAccountInfo = vi.hoisted(() => vi.fn())
const mockGetUserProfile = vi.hoisted(() => vi.fn())
vi.mock('@tarojs/taro', () => ({
default: {
login: mockLogin,
getAccountInfoSync: mockGetAccountInfo,
getUserProfile: mockGetUserProfile
}
}))
const mockWxMiniappLogin = vi.hoisted(() => vi.fn())
vi.mock('@/api/auth', () => ({ wxMiniappLogin: mockWxMiniappLogin }))
const mem = new Map<string, unknown>()
vi.mock('@/utils/storage', () => ({
storage: {
get: <T>(key: string): T | null => (mem.has(key) ? (mem.get(key) as T) : null),
set: <T>(key: string, value: T): void => { mem.set(key, value) },
remove: (key: string): void => { mem.delete(key) }
}
}))
import { performWxLogin, completeUserInfoLogin, isNeedUserInfo } from '@/pages/login/wxLoginFlow'
const result = (over: Record<string, unknown> = {}) => ({
userInfo: {
userId: 'u-1', userType: 1, avatar: '', nickName: '张三', mobile: '',
gender: 1, region: '', loginFrom: 'miniapp'
},
code: 'tk-abc',
...over
})
// 首次登录 needUserInfo 响应形态(http 层在 Content 上打的标记)
const needInfoResult = () => ({
openId: 'o-need',
bindKey: 'bind-1',
needUserInfo: true as const
})
beforeEach(() => {
mem.clear()
mockLogin.mockReset()
mockGetAccountInfo.mockReset()
mockGetUserProfile.mockReset()
mockWxMiniappLogin.mockReset()
mockLogin.mockResolvedValue({ code: 'wx-code-1' })
mockGetAccountInfo.mockReturnValue({ miniProgram: { appId: 'wx-appid-live' } })
})
describe('performWxLogin', () => {
it('完整流程:Taro.login → appId/openId 组装 → 返回登录结果', async () => {
mockWxMiniappLogin.mockResolvedValue(result())
const res = await performWxLogin()
expect(res.code).toBe('tk-abc')
expect(mockWxMiniappLogin).toHaveBeenCalledWith({
appId: 'wx-appid-live',
code: 'wx-code-1',
openId: undefined
})
})
it('首次登录(本地无 openId)请求不带 openId,结果含 openId 时写入缓存', async () => {
mockWxMiniappLogin.mockResolvedValue(result({ openId: 'o-abc' }))
await performWxLogin()
expect(mockWxMiniappLogin.mock.calls[0][0].openId).toBeUndefined()
expect(mem.get('wx_openid')).toBe('o-abc')
})
it('二次登录:本地已有 openId 则随请求回传', async () => {
mem.set('wx_openid', 'o-saved')
mockWxMiniappLogin.mockResolvedValue(result())
await performWxLogin()
expect(mockWxMiniappLogin.mock.calls[0][0].openId).toBe('o-saved')
})
it('结果不含 openId 时不覆盖本地缓存', async () => {
mem.set('wx_openid', 'o-saved')
mockWxMiniappLogin.mockResolvedValue(result())
await performWxLogin()
expect(mem.get('wx_openid')).toBe('o-saved')
})
it('Taro.login 返回空 code 抛错(不发起登录请求)', async () => {
mockLogin.mockResolvedValue({ code: '' })
await expect(performWxLogin()).rejects.toThrow('获取微信登录凭证失败')
expect(mockWxMiniappLogin).not.toHaveBeenCalled()
})
it('Taro.login reject(errMsg 形状)时抛带 message 的 Error', async () => {
mockLogin.mockRejectedValue({ errMsg: 'login:fail:cancel' })
await expect(performWxLogin()).rejects.toThrow('微信登录失败')
expect(mockWxMiniappLogin).not.toHaveBeenCalled()
})
it('getAccountInfoSync 抛错时回退常量 appId', async () => {
mockGetAccountInfo.mockImplementation(() => { throw new Error('not supported') })
mockWxMiniappLogin.mockResolvedValue(result())
await performWxLogin()
expect(mockWxMiniappLogin.mock.calls[0][0].appId).toBe('wxe9773bb6f025ab00')
})
it('接口业务错(BusinessError)原样透传', async () => {
const err = new Error('code 已被使用')
err.name = 'BusinessError'
mockWxMiniappLogin.mockRejectedValue(err)
await expect(performWxLogin()).rejects.toThrow('code 已被使用')
})
})
describe('needUserInfo 首次登录分支', () => {
it('performWxLogin 返回 needUserInfo 形态时缓存 openId 并原样返回', async () => {
mockWxMiniappLogin.mockResolvedValue(needInfoResult())
const res = await performWxLogin()
expect(isNeedUserInfo(res)).toBe(true)
expect(res.openId).toBe('o-need')
expect(mem.get('wx_openid')).toBe('o-need')
})
it('isNeedUserInfo 对完整登录响应返回 false', () => {
expect(isNeedUserInfo(result())).toBe(false)
})
})
describe('completeUserInfoLogin 二次授权', () => {
it('getUserProfile → 换新 code → 带 iv/encryptedData/openId 重调登录', async () => {
mockGetUserProfile.mockResolvedValue({ iv: 'iv-1', encryptedData: 'enc-1' })
mockWxMiniappLogin.mockResolvedValue(result({ openId: 'o-need' }))
const res = await completeUserInfoLogin('o-need')
expect(res.code).toBe('tk-abc')
expect(mockWxMiniappLogin).toHaveBeenCalledWith({
appId: 'wx-appid-live',
code: 'wx-code-1',
openId: 'o-need',
iv: 'iv-1',
encryptedData: 'enc-1'
})
})
it('用户拒绝授权(errMsg 形状 reject)抛带 message 的 Error', async () => {
mockGetUserProfile.mockRejectedValue({ errMsg: 'getUserProfile:fail auth deny' })
await expect(completeUserInfoLogin('o-need')).rejects.toThrow('getUserProfile:fail auth deny')
expect(mockWxMiniappLogin).not.toHaveBeenCalled()
})
it('授权成功但换 code 失败:抛错且不发起登录请求', async () => {
mockGetUserProfile.mockResolvedValue({ iv: 'iv-1', encryptedData: 'enc-1' })
mockLogin.mockRejectedValue({ errMsg: 'login:fail' })
await expect(completeUserInfoLogin('o-need')).rejects.toThrow('获取微信登录凭证失败,请重试')
expect(mockWxMiniappLogin).not.toHaveBeenCalled()
})
})