import { changePassword, getPasswordRuleMessage, loginByPassword, resetPassword, sendSmsCode, validatePassword, validatePasswordConfirm, } from '../api/api'; import { PASSWORD_RULE_LABEL } from '../constants'; import type { PasswordRuleError } from '../types'; import { isValidPhone, isValidSmsCode } from '../utils'; // api.ts 依赖 storage,Jest 环境无原生 AsyncStorage,需内存版 mock(与 salary.test.ts 同) jest.mock('../lib/storage', () => ({ storageGet: jest.fn(async () => null), storageSet: jest.fn(async () => undefined), storageRemove: jest.fn(async () => undefined), })); /** 联合类型的全部成员:新增成员而漏配文案时,下面的完整性用例会失败 */ const ALL_RULE_ERRORS: PasswordRuleError[] = [ 'empty', 'too-short', 'too-long', 'need-letter-digit', 'mismatch', 'same-as-old', ]; describe('validatePassword(密码强度业务规则)', () => { it('空密码返回 empty', () => { expect(validatePassword('')).toBe('empty'); }); it('少于 8 位返回 too-short', () => { expect(validatePassword('ab12')).toBe('too-short'); expect(validatePassword('abc1234')).toBe('too-short'); }); it('超过 20 位返回 too-long', () => { expect(validatePassword('abc12345678901234567890')).toBe('too-long'); }); it('纯数字返回 need-letter-digit', () => { expect(validatePassword('12345678')).toBe('need-letter-digit'); }); it('纯字母返回 need-letter-digit', () => { expect(validatePassword('abcdefgh')).toBe('need-letter-digit'); }); it('8–20 位且含字母和数字时通过', () => { expect(validatePassword('abc12345')).toBeNull(); expect(validatePassword('a1b2c3d4e5f6g7h8i9j0')).toBeNull(); }); }); describe('validatePasswordConfirm(两次输入一致性)', () => { it('一致时返回 null', () => { expect(validatePasswordConfirm('abc12345', 'abc12345')).toBeNull(); }); it('不一致时返回 mismatch', () => { expect(validatePasswordConfirm('abc12345', 'abc12346')).toBe('mismatch'); }); }); describe('PASSWORD_RULE_LABEL(错误码 → 文案映射完整性)', () => { it('每个错误码都有非空中文文案', () => { ALL_RULE_ERRORS.forEach((code) => { const label = PASSWORD_RULE_LABEL[code]; expect(typeof label).toBe('string'); expect(label.length).toBeGreaterThan(0); }); }); it('getPasswordRuleMessage 与映射表一致', () => { ALL_RULE_ERRORS.forEach((code) => { expect(getPasswordRuleMessage(code)).toBe(PASSWORD_RULE_LABEL[code]); }); }); }); describe('validators(手机号 / 验证码格式)', () => { it('手机号必须是 1 开头共 11 位', () => { expect(isValidPhone('13800138000')).toBe(true); expect(isValidPhone('1380013800')).toBe(false); expect(isValidPhone('23800138000')).toBe(false); expect(isValidPhone('138001380001')).toBe(false); expect(isValidPhone('')).toBe(false); }); it('验证码为 4–6 位数字', () => { expect(isValidSmsCode('1234')).toBe(true); expect(isValidSmsCode('123456')).toBe(true); expect(isValidSmsCode('123')).toBe(false); expect(isValidSmsCode('1234567')).toBe(false); expect(isValidSmsCode('12a4')).toBe(false); }); }); describe('loginByPassword(手机号 + 密码登录)', () => { it('手机号非法时拒绝', async () => { await expect(loginByPassword('138', 'abc12345')).rejects.toThrow('手机号格式不正确'); }); it('密码不合规时统一提示账号或密码错误(不泄露账号是否存在)', async () => { await expect(loginByPassword('13800138000', 'abc')).rejects.toThrow('手机号或密码错误'); }); it('参数合法时返回令牌', async () => { const result = await loginByPassword('13800138000', 'abc12345'); expect(result.phone).toBe('13800138000'); expect(result.token).toContain('13800138000'); }); }); describe('sendSmsCode(发送验证码)', () => { it('手机号非法时拒绝', async () => { await expect(sendSmsCode('138')).rejects.toThrow('手机号格式不正确'); }); it('手机号合法时成功', async () => { await expect(sendSmsCode('13800138000')).resolves.toBeUndefined(); }); }); describe('resetPassword(重置 / 首次设置密码)', () => { it('验证码不合法时拒绝', async () => { await expect( resetPassword({ phone: '13800138000', code: '12', password: 'abc12345' }), ).rejects.toThrow('验证码不正确'); }); it('新密码不合规时返回对应文案', async () => { await expect( resetPassword({ phone: '13800138000', code: '1234', password: '12345678' }), ).rejects.toThrow(PASSWORD_RULE_LABEL['need-letter-digit']); }); it('参数合法时直接下发登录态,用户无需再登录一次', async () => { const result = await resetPassword({ phone: '13800138000', code: '1234', password: 'abc12345', }); expect(result.phone).toBe('13800138000'); expect(result.token).toContain('13800138000'); }); }); describe('changePassword(已登录态改密)', () => { it('未填原密码时拒绝', async () => { await expect( changePassword({ oldPassword: '', newPassword: 'abc12345', confirmPassword: 'abc12345' }), ).rejects.toThrow('请输入原密码'); }); it('两次新密码不一致时拒绝', async () => { await expect( changePassword({ oldPassword: 'old12345', newPassword: 'abc12345', confirmPassword: 'abc12346' }), ).rejects.toThrow(PASSWORD_RULE_LABEL.mismatch); }); it('新密码与原密码相同时拒绝', async () => { await expect( changePassword({ oldPassword: 'abc12345', newPassword: 'abc12345', confirmPassword: 'abc12345' }), ).rejects.toThrow(PASSWORD_RULE_LABEL['same-as-old']); }); it('参数合法时成功', async () => { await expect( changePassword({ oldPassword: 'old12345', newPassword: 'abc12345', confirmPassword: 'abc12345' }), ).resolves.toBeUndefined(); }); });