Files
eSIM-Tools/tests/simyo/api-config.test.js
Abner 0a70ce47cb feat(simyo): 完善 eSIM 更换流程(仅 EMAIL)
- 修 handleApiResponse:登录/查询体无 result.success 时正确判定成功
- 设备更换:先 GET settings/simcard,再 POST EMAIL 申请;等待验证码时跳过重复下单
- 登录识别 mfaStatus,未关闭 MFA 时明确阻断
- bad session 单独文案;UA 更新为 iOS 18.2 / iPhone12,8
2026-07-22 19:26:12 +08:00

138 lines
5.0 KiB
JavaScript
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.
'use strict';
/**
* Simyo api-config / client-identity请求头与设备身份对齐官方 App 抓包)
*/
import {
createHeaders,
getOrCreateDeviceId,
handleApiResponse,
mapSimyoErrorMessage,
simyoConfig
} from '../../src/simyo/js/modules/api-config.js';
import {
SIMYO_CLIENT_PLATFORM,
SIMYO_CLIENT_VERSION,
SIMYO_DEVICE_MODEL,
SIMYO_IOS_VERSION,
SIMYO_USER_AGENT,
simyoClientIdentity
} from '../../src/simyo/js/modules/client-identity.js';
const UUID_RE = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/;
/** 官方抓包 UA版本号后恰好两个空格 */
const OFFICIAL_UA = 'MijnSimyoFT/4.28.0 (iOS 18.2; iPhone12,8)';
describe('Simyo client-identity', () => {
it('版本 / 设备 / UA 应与 4.28.0 eSIM 更换抓包一致', () => {
expect(SIMYO_CLIENT_VERSION).toBe('4.28.0');
expect(SIMYO_CLIENT_PLATFORM).toBe('ios');
expect(SIMYO_IOS_VERSION).toBe('18.2');
expect(SIMYO_DEVICE_MODEL).toBe('iPhone12,8');
expect(SIMYO_USER_AGENT).toBe(OFFICIAL_UA);
// 版本号与括号之间必须是两个空格(不是一个)
expect(SIMYO_USER_AGENT).toMatch(/^MijnSimyoFT\/4\.28\.0 {2}\(iOS 18\.2; iPhone12,8\)$/);
expect(simyoClientIdentity.userAgent).toBe(OFFICIAL_UA);
});
});
describe('Simyo api-config headers', () => {
beforeEach(() => {
localStorage.clear();
delete getOrCreateDeviceId._sessionId;
});
it('应生成并持久化大写 UUID 形态的 X-Device-ID', () => {
const id1 = getOrCreateDeviceId();
const id2 = getOrCreateDeviceId();
expect(id1).toMatch(UUID_RE);
expect(id2).toBe(id1);
expect(localStorage.getItem('simyo_device_id')).toBe(id1);
});
it('createHeaders 必须包含官方抓包中的核心头', () => {
const headers = createHeaders(false);
expect(headers['X-Client-Token']).toBe(simyoConfig.clientToken);
expect(headers['X-Client-Platform']).toBe(SIMYO_CLIENT_PLATFORM);
expect(headers['X-Client-Version']).toBe(SIMYO_CLIENT_VERSION);
expect(headers['X-Device-ID']).toMatch(UUID_RE);
expect(headers['User-Agent']).toBe(OFFICIAL_UA);
expect(headers['User-Agent']).toBe(simyoConfig.userAgent);
expect(headers['Content-Type']).toBe('application/json');
expect(headers.Accept).toBe('application/json');
expect(headers['X-Session-Token']).toBeUndefined();
});
it('includeSession 时应附带 X-Session-Token', () => {
const headers = createHeaders(true, 'sess-token-1');
expect(headers['X-Session-Token']).toBe('sess-token-1');
expect(headers['X-Device-ID']).toMatch(UUID_RE);
expect(headers['User-Agent']).toBe(OFFICIAL_UA);
});
});
describe('Simyo mapSimyoErrorMessage / handleApiResponse', () => {
it('426 应映射为升级客户端友好文案,而不是 HTTP 426', () => {
const msg = mapSimyoErrorMessage(426, {});
expect(msg).not.toMatch(/HTTP\s*426/i);
expect(msg.toLowerCase()).toMatch(/upgrade|升级|客户端|client version|refresh|刷新/);
});
it('missing X-Device-ID 应映射为设备标识提示', () => {
const msg = mapSimyoErrorMessage(400, { message: 'missing X-Device-ID' });
expect(msg.toLowerCase()).toMatch(/device|设备|x-device-id/);
expect(msg).not.toBe('missing X-Device-ID');
});
it('bad session 应映射为会话无效,而非账号密码错误', () => {
const msg = mapSimyoErrorMessage(401, { message: 'Unauthorised - bad session' });
expect(msg.toLowerCase()).toMatch(/session|会话|mfa|bad session/);
expect(msg).not.toMatch(/密码不正确|incorrect phone number or password/i);
});
it('401 凭据错误原文应优先识别为账号密码错误', () => {
const msg = mapSimyoErrorMessage(401, { message: 'Invalid credentials' });
expect(msg.toLowerCase()).toMatch(/password|密码|phone|手机号|incorrect|不正确/);
});
it('handleApiResponse 在 426 时应抛出友好错误', async () => {
const response = {
ok: false,
status: 426,
headers: { get: () => 'application/json' },
json: async () => ({ message: 'Upgrade Required' })
};
await expect(handleApiResponse(response)).rejects.toThrow(/升级|upgrade|客户端|client version|refresh|刷新/i);
});
it('handleApiResponse 官方登录体(无 result.success应视为成功', async () => {
const response = {
ok: true,
status: 200,
headers: { get: () => 'application/json' },
json: async () => ({
result: {
sessionToken: 'abc',
mfaStatus: 'DISABLED_BY_CUSTOMER'
}
})
};
const data = await handleApiResponse(response);
expect(data.success).toBe(true);
expect(data.result.sessionToken).toBe('abc');
});
it('handleApiResponse 成功路径仍返回 result', async () => {
const response = {
ok: true,
status: 200,
headers: { get: () => 'application/json' },
json: async () => ({ success: true, result: { sessionToken: 'abc' } })
};
const data = await handleApiResponse(response);
expect(data.success).toBe(true);
expect(data.result.sessionToken).toBe('abc');
});
});