172 lines
5.8 KiB
TypeScript
172 lines
5.8 KiB
TypeScript
import {
|
|
createNapcatLoginSession,
|
|
NapcatLoginStateMachine,
|
|
} from '../../../../src/modules/qqbot/napcat';
|
|
|
|
describe('NapCat login state machine', () => {
|
|
it('advances through quick, password, captcha, new-device, manual QR, and success states', () => {
|
|
const machine = new NapcatLoginStateMachine();
|
|
let session = createNapcatLoginSession({
|
|
accountId: 'account-10001',
|
|
hasHistoricalSession: true,
|
|
hasSavedPassword: true,
|
|
sessionId: 'session-1',
|
|
});
|
|
|
|
expect(session.stage).toBe('quick-login');
|
|
expect(session.progressMessage).toBe('正在快速登录');
|
|
|
|
session = machine.advance(session, { type: 'quick-login-failed' });
|
|
expect(session.stage).toBe('password-login');
|
|
expect(session.progressMessage).toBe('快速登录失败,进入密码登录');
|
|
|
|
session = machine.advance(session, {
|
|
captchaUrl: 'https://ti.qq.com/safe/tools/captcha/sms-verify-login',
|
|
type: 'password-login-captcha-required',
|
|
});
|
|
expect(session.stage).toBe('captcha');
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({
|
|
status: 'pending',
|
|
type: 'captcha',
|
|
url: 'https://ti.qq.com/safe/tools/captcha/sms-verify-login',
|
|
}),
|
|
);
|
|
expect(session.progressMessage).toBe('需要验证码');
|
|
|
|
session = machine.advance(session, { type: 'captcha-submitted' });
|
|
expect(session.stage).toBe('captcha');
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({
|
|
status: 'submitted',
|
|
type: 'captcha',
|
|
}),
|
|
);
|
|
expect(session.progressMessage).toBe('验证码已提交,等待确认');
|
|
|
|
session = machine.advance(session, {
|
|
newDevicePullQrCodeSig: 'sig-1',
|
|
type: 'captcha-new-device-required',
|
|
});
|
|
expect(session.stage).toBe('new-device');
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({
|
|
pullQrCodeSig: 'sig-1',
|
|
status: 'qr-pending',
|
|
type: 'new-device',
|
|
}),
|
|
);
|
|
expect(session.progressMessage).toBe('需要新设备验证二维码');
|
|
|
|
session = machine.advance(session, {
|
|
qrcodeUrl: 'data:image/png;base64,qr',
|
|
type: 'new-device-qr-ready',
|
|
});
|
|
expect(session.progressMessage).toBe('新设备二维码待扫码');
|
|
|
|
session = machine.advance(session, { type: 'new-device-scanned' });
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({ status: 'scanned' }),
|
|
);
|
|
expect(session.progressMessage).toBe('新设备二维码已扫码');
|
|
|
|
session = machine.advance(session, { type: 'new-device-confirming' });
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({ status: 'confirming' }),
|
|
);
|
|
expect(session.progressMessage).toBe('新设备确认中');
|
|
|
|
session = machine.advance(session, { type: 'new-device-verified' });
|
|
expect(session.stage).toBe('password-login');
|
|
expect(session.challenge).toBeUndefined();
|
|
expect(session.progressMessage).toBe('新设备验证成功,继续登录');
|
|
|
|
session = machine.advance(session, { type: 'manual-qr-required' });
|
|
expect(session.stage).toBe('manual-qr');
|
|
expect(session.progressMessage).toBe('正在生成手动二维码');
|
|
|
|
session = machine.advance(session, {
|
|
selfId: '10001',
|
|
type: 'login-success',
|
|
});
|
|
expect(session.stage).toBe('success');
|
|
expect(session.status).toBe('success');
|
|
expect(session.progressMessage).toBe('登录成功');
|
|
});
|
|
|
|
it('keeps captcha and new-device challenges pending until resolved, expired, or failed', () => {
|
|
const machine = new NapcatLoginStateMachine();
|
|
let session = createNapcatLoginSession({
|
|
accountId: 'account-10001',
|
|
hasHistoricalSession: false,
|
|
hasSavedPassword: true,
|
|
sessionId: 'session-2',
|
|
});
|
|
|
|
expect(session.stage).toBe('password-login');
|
|
session = machine.advance(session, {
|
|
captchaUrl: 'https://ti.qq.com/safe/tools/captcha/sms-verify-login',
|
|
type: 'password-login-captcha-required',
|
|
});
|
|
session = machine.advance(session, { type: 'captcha-still-required' });
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({
|
|
status: 'pending',
|
|
type: 'captcha',
|
|
url: 'https://ti.qq.com/safe/tools/captcha/sms-verify-login',
|
|
}),
|
|
);
|
|
|
|
session = machine.advance(session, {
|
|
newDevicePullQrCodeSig: 'sig-2',
|
|
type: 'captcha-new-device-required',
|
|
});
|
|
session = machine.advance(session, { type: 'new-device-poll-pending' });
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({
|
|
pullQrCodeSig: 'sig-2',
|
|
status: 'qr-pending',
|
|
type: 'new-device',
|
|
}),
|
|
);
|
|
|
|
session = machine.advance(session, {
|
|
reason: '二维码已过期',
|
|
type: 'new-device-expired',
|
|
});
|
|
expect(session.status).toBe('pending');
|
|
expect(session.challenge).toEqual(
|
|
expect.objectContaining({
|
|
reason: '二维码已过期',
|
|
status: 'expired',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('treats runtime cleanup failure as a terminal login failure', () => {
|
|
const machine = new NapcatLoginStateMachine();
|
|
let session = createNapcatLoginSession({
|
|
accountId: 'account-10001',
|
|
hasHistoricalSession: true,
|
|
hasSavedPassword: true,
|
|
sessionId: 'session-3',
|
|
});
|
|
|
|
session = machine.advance(session, {
|
|
reason: 'NAPCAT_QUICK_PASSWORD cleanup failed',
|
|
type: 'runtime-cleanup-failed',
|
|
});
|
|
expect(session.stage).toBe('cleanup-failed');
|
|
expect(session.status).toBe('failure');
|
|
expect(session.progressMessage).toBe('运行态清理失败');
|
|
|
|
session = machine.advance(session, {
|
|
selfId: '10001',
|
|
type: 'login-success',
|
|
});
|
|
expect(session.stage).toBe('cleanup-failed');
|
|
expect(session.status).toBe('failure');
|
|
expect(session.progressMessage).toBe('运行态清理失败');
|
|
});
|
|
});
|