| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- /**
- * 会员有效态门控(`utils/membershipGate.js`)单元测试
- *
- * 背景:`membershipGate.js` 随 `merge(miniprogram): 聊天/消息类入口按有效会员隐藏`
- * (commit `3d35ab714`,2026-11-12 00:33)合入主线,**合入时零测试**。
- * 该函数决定 9 个页面(首页/心/身/智/行/我的/应用库/详情页)的聊天·消息入口可见性,
- * 属「越权即放行非会员」的强业务约束,必须有可执行锚点。
- *
- * 契约(源码注释自述,逐条断言):
- * - 唯一判据 = `POST /api/ai/gate` 的 `data.isActive === true`(严格等于,不看 canUse/status)
- * - 无 token 直接 false,**不发请求**(fail-closed)
- * - 接口异常 / 超时 / 字段缺失 一律 false(fail-closed)
- *
- * 锚点:TC-MG-U01 ~ TC-MG-U07
- */
- jest.mock('@/utils/api.js', () => ({
- aiGateStatus: jest.fn()
- }))
- import { aiGateStatus } from '@/utils/api.js'
- import { isActiveMember } from '@/utils/membershipGate.js'
- beforeEach(() => {
- global.uni._storage = {}
- aiGateStatus.mockReset()
- })
- describe('isActiveMember · 会员有效态(TC-MG-U01~U07)', () => {
- it('TC-MG-U01 无 token 直接 false,且不发起 /api/ai/gate 请求', async () => {
- const r = await isActiveMember()
- expect(r).toBe(false)
- expect(aiGateStatus).not.toHaveBeenCalled()
- })
- it('TC-MG-U02 有 token 且 data.isActive === true → true', async () => {
- global.uni._storage.token = 'tk-abc'
- aiGateStatus.mockResolvedValue({ code: 200, data: { isActive: true } })
- await expect(isActiveMember()).resolves.toBe(true)
- expect(aiGateStatus).toHaveBeenCalledTimes(1)
- })
- it('TC-MG-U03 有 token 且 data.isActive === false(过期会员)→ false', async () => {
- global.uni._storage.token = 'tk-abc'
- aiGateStatus.mockResolvedValue({ code: 200, data: { isActive: false } })
- await expect(isActiveMember()).resolves.toBe(false)
- })
- it('TC-MG-U04 响应缺 data 字段 → false(字段缺失 fail-closed)', async () => {
- global.uni._storage.token = 'tk-abc'
- aiGateStatus.mockResolvedValue({ code: 200 })
- await expect(isActiveMember()).resolves.toBe(false)
- })
- it('TC-MG-U05 isActive 为非布尔真值(后端返回 1 / "true")→ false(严格等值,不误放行)', async () => {
- global.uni._storage.token = 'tk-abc'
- aiGateStatus.mockResolvedValueOnce({ code: 200, data: { isActive: 1 } })
- await expect(isActiveMember()).resolves.toBe(false)
- aiGateStatus.mockResolvedValueOnce({ code: 200, data: { isActive: 'true' } })
- await expect(isActiveMember()).resolves.toBe(false)
- })
- it('TC-MG-U06 接口 reject(网络异常/超时)→ false,不抛错', async () => {
- global.uni._storage.token = 'tk-abc'
- aiGateStatus.mockRejectedValue(new Error('timeout'))
- await expect(isActiveMember()).resolves.toBe(false)
- })
- it('TC-MG-U07 接口 resolve 成 undefined → false(不因空响应放行)', async () => {
- global.uni._storage.token = 'tk-abc'
- aiGateStatus.mockResolvedValue(undefined)
- await expect(isActiveMember()).resolves.toBe(false)
- })
- })
|