mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-03 18:33:14 +08:00
163 lines
5.2 KiB
TypeScript
163 lines
5.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
vi.mock('electron', () => ({
|
|
app: {
|
|
getPath: vi.fn(() => '/tmp/tracememo-key-service-test')
|
|
}
|
|
}))
|
|
|
|
import {
|
|
SIP_ENABLED_ERROR,
|
|
buildAppleSiliconXkeyInvocation,
|
|
buildXkeyHelperArguments,
|
|
mapXkeyHelperFailure,
|
|
parseSipEnabled,
|
|
parseXkeyHelperOutput,
|
|
resolveXkeyHelperMode
|
|
} from '../../src/main/key-service-mac'
|
|
|
|
describe('parseXkeyHelperOutput', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('returns a valid helper key without exposing progress output', () => {
|
|
const key = 'ab'.repeat(32)
|
|
expect(
|
|
parseXkeyHelperOutput(`progress line\n${JSON.stringify({ success: true, key })}`)
|
|
).toEqual({ success: true, key })
|
|
})
|
|
|
|
it('maps a login-time capture timeout to an actionable message', () => {
|
|
const result = parseXkeyHelperOutput(
|
|
JSON.stringify({
|
|
success: false,
|
|
result:
|
|
'sink found at 0x1234\nsoftware breakpoint unavailable\nhardware breakpoint armed\ntimeout waiting for breakpoint hit'
|
|
})
|
|
)
|
|
|
|
expect(result).toEqual({
|
|
success: false,
|
|
code: 'CAPTURE_TIMEOUT',
|
|
error:
|
|
'已完成管理员授权,但监听期间微信没有触发账号密钥派生。请先停留在微信登录界面,在 TraceMemo 点击“自动获取密钥”,授权后点击微信“登录”;已有登录凭据时通常不需要扫码。'
|
|
})
|
|
})
|
|
|
|
it('does not expose raw helper diagnostics for an unknown failure', () => {
|
|
const result = parseXkeyHelperOutput(
|
|
JSON.stringify({
|
|
success: false,
|
|
result: 'private diagnostic /Users/example/source/xkey.mm:123'
|
|
})
|
|
)
|
|
|
|
expect(result).toEqual({
|
|
success: false,
|
|
code: 'HELPER_RESULT_INVALID',
|
|
error: '密钥工具未返回有效密钥,请确认微信仍在运行后重试。'
|
|
})
|
|
expect(result.error).not.toContain('/Users/example')
|
|
})
|
|
|
|
it('sanitizes diagnostics returned through an AppleScript command error', () => {
|
|
const result = mapXkeyHelperFailure(
|
|
'sink found at 0x1234\nhardware breakpoint armed on 81/81 threads\nWAIT_FAILED:no_breakpoint_hit',
|
|
'OSASCRIPT_1'
|
|
)
|
|
|
|
expect(result).toEqual({
|
|
success: false,
|
|
code: 'CAPTURE_TIMEOUT',
|
|
error:
|
|
'已完成管理员授权,但监听期间微信没有触发账号密钥派生。请先停留在微信登录界面,在 TraceMemo 点击“自动获取密钥”,授权后点击微信“登录”;已有登录凭据时通常不需要扫码。'
|
|
})
|
|
})
|
|
|
|
it.each([
|
|
['4.1.8', 'legacy'],
|
|
['4.1.9.57', 'legacy'],
|
|
['4.1.10', 'legacy'],
|
|
['4.1.13', 'wechat-4.1.13'],
|
|
['4.1.13.91', 'wechat-4.1.13'],
|
|
['4.1.130', 'legacy'],
|
|
['未检测到', 'legacy']
|
|
])('selects the helper mode for WeChat %s', (version, mode) => {
|
|
expect(resolveXkeyHelperMode(version)).toBe(mode)
|
|
})
|
|
|
|
it('builds the verified 4.1.13 account-key capture contract', () => {
|
|
expect(buildXkeyHelperArguments(60037, 120_000)).toEqual([
|
|
'60037',
|
|
'120000',
|
|
'--profile',
|
|
'wechat-4.1.13',
|
|
'--account'
|
|
])
|
|
})
|
|
|
|
it('uses the new helper only for WeChat 4.1.13', () => {
|
|
expect(buildAppleSiliconXkeyInvocation('4.1.13.91', 60037, 60_000)).toEqual({
|
|
mode: 'wechat-4.1.13',
|
|
resourceName: 'xkey_helper_4_1_13',
|
|
args: ['60037', '120000', '--profile', 'wechat-4.1.13', '--account'],
|
|
waitMs: 120_000,
|
|
timeoutSeconds: 130,
|
|
execTimeoutMs: 135_000
|
|
})
|
|
})
|
|
|
|
it.each(['4.1.8', '4.1.9.57', '4.1.10', '未检测到'])(
|
|
'preserves the legacy helper invocation for WeChat %s',
|
|
(version) => {
|
|
expect(buildAppleSiliconXkeyInvocation(version, 60037, 60_000)).toEqual({
|
|
mode: 'legacy',
|
|
resourceName: 'xkey_helper',
|
|
args: ['60037', '60000'],
|
|
waitMs: 60_000,
|
|
timeoutSeconds: 90,
|
|
execTimeoutMs: 80_000
|
|
})
|
|
}
|
|
)
|
|
|
|
it('extracts a helper error embedded in AppleScript diagnostics', () => {
|
|
expect(
|
|
mapXkeyHelperFailure(
|
|
'hardware breakpoint armed\n{"success":false,"result":"ERROR:HOOK_FAILED:ptrace"}',
|
|
'OSASCRIPT_1'
|
|
)
|
|
).toEqual({
|
|
success: false,
|
|
code: 'HOOK_FAILED',
|
|
error: '密钥工具执行未完成(HOOK_FAILED),请确认微信仍在运行后重试。'
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('parseSipEnabled', () => {
|
|
it.each<[string, boolean]>([
|
|
['System Integrity Protection status: enabled.', true],
|
|
['System Integrity Protection status: disabled.', false],
|
|
['System Integrity Protection status: unknown (Custom Configuration).', false],
|
|
['System Integrity Protection status: enabled (Apple Internal).', true],
|
|
['System Integrity Protection status: disabled (Apple Internal).', false]
|
|
])('reads "%s" as %s', (statusOutput, expected) => {
|
|
expect(parseSipEnabled(statusOutput)).toBe(expected)
|
|
})
|
|
|
|
it('only reads the status field, not the word enabled elsewhere in the output', () => {
|
|
expect(
|
|
parseSipEnabled('System Integrity Protection status: disabled.\nKernel Extensions: enabled')
|
|
).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('SIP blocking message', () => {
|
|
it('names the prerequisite and the next action', () => {
|
|
expect(SIP_ENABLED_ERROR).toContain('SIP')
|
|
expect(SIP_ENABLED_ERROR).toMatch(/关闭 SIP|手动粘贴/)
|
|
})
|
|
})
|