import { describe, expect, it } from 'vitest'; import { isClientAuthError } from '../src/services/clientAuthError'; import type { AuthServiceUnavailable } from '../src/services/generated/AuthServiceUnavailable'; import type { ClientAuthError } from '../src/services/generated/ClientAuthError'; import type { PhoneNumberInvalid } from '../src/services/generated/PhoneNumberInvalid'; /** * 编译期用例:每个变体在 `type` 上判别后都能落到一个**具名载荷类型**, * 等价于 Java 的 `catch (PhoneNumberInvalid e)`;类型不匹配时 `tsc` 会直接报错。 */ function handleByNamedType(error: ClientAuthError) { switch (error.type) { case 'phoneNumberInvalid': { const payload: PhoneNumberInvalid = error; return payload.message; } case 'authServiceUnavailable': { const payload: AuthServiceUnavailable = error; return `${payload.status}:${payload.message}`; } default: return ''; } } describe('AGC 认证命令错误的形状读取', () => { it('判别后按具名载荷类型分支处理', () => { expect( handleByNamedType({ type: 'phoneNumberInvalid', message: '请输入正确的手机号', }), ).toBe('请输入正确的手机号'); expect( handleByNamedType({ type: 'authServiceUnavailable', status: 503, message: '登录服务暂时不可用', }), ).toBe('503:登录服务暂时不可用'); }); it('只按形状读取 Rust 的结构化拒绝,裸字符串与 Error 都不算', () => { expect( isClientAuthError({ type: 'phoneOrPasswordMismatch', message: '手机号或密码错误', }), ).toEqual({ type: 'phoneOrPasswordMismatch', message: '手机号或密码错误' }); expect(isClientAuthError('手机号或密码错误')).toBeNull(); expect(isClientAuthError(new Error('手机号或密码错误'))).toBeNull(); expect( isClientAuthError({ type: '', message: '手机号或密码错误' }), ).toBeNull(); expect(isClientAuthError({ type: 'phoneNumberInvalid' })).toBeNull(); expect(isClientAuthError(null)).toBeNull(); }); it('未知变体也算形状合法,由调用方按 default 交给错误池', () => { expect( isClientAuthError({ type: 'brandNewRejection', message: '新变体' }), ).toEqual({ type: 'brandNewRejection', message: '新变体' }); }); it('status 故意不校验:缺 status 但 message 可用的拒绝仍可读', () => { expect( isClientAuthError({ type: 'authServiceUnavailable', message: '登录服务暂时不可用', }), ).toEqual({ type: 'authServiceUnavailable', message: '登录服务暂时不可用', }); }); });