103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import { NapcatLoginApiClient } from '../../../../src/modules/qqbot/napcat';
|
|
|
|
describe('NapCat new-device flow API client', () => {
|
|
it('runs GetNewDeviceQRCode -> PollNewDeviceQR -> NewDeviceLogin with normalized states', async () => {
|
|
const calls: Array<{ body: unknown; path: string }> = [];
|
|
const client = new NapcatLoginApiClient({
|
|
post: jest.fn(async (path: string, body: unknown) => {
|
|
calls.push({ body, path });
|
|
if (path.endsWith('/GetNewDeviceQRCode')) {
|
|
return {
|
|
newDevicePullQrCodeSig: 'sig-1',
|
|
qrcodeUrl: 'data:image/png;base64,new-device',
|
|
};
|
|
}
|
|
if (path.endsWith('/PollNewDeviceQR') && calls.length === 2) {
|
|
return { status: 'scanned' };
|
|
}
|
|
if (path.endsWith('/PollNewDeviceQR') && calls.length === 3) {
|
|
return { status: 'confirming' };
|
|
}
|
|
if (path.endsWith('/NewDeviceLogin')) {
|
|
return { message: 'ok', success: true };
|
|
}
|
|
throw new Error(`unexpected path ${path}`);
|
|
}),
|
|
});
|
|
|
|
const qr = await client.getNewDeviceQRCode('session-1');
|
|
const scanned = await client.pollNewDeviceQR('session-1');
|
|
const confirming = await client.pollNewDeviceQR('session-1');
|
|
const verified = await client.newDeviceLogin('session-1');
|
|
|
|
expect(qr).toEqual({
|
|
pullQrCodeSig: 'sig-1',
|
|
qrcodeUrl: 'data:image/png;base64,new-device',
|
|
sessionId: 'session-1',
|
|
status: 'qr-pending',
|
|
});
|
|
expect(scanned.status).toBe('scanned');
|
|
expect(confirming.status).toBe('confirming');
|
|
expect(verified).toEqual({
|
|
message: 'ok',
|
|
sessionId: 'session-1',
|
|
status: 'verified',
|
|
success: true,
|
|
});
|
|
expect(calls).toEqual([
|
|
{
|
|
body: { sessionId: 'session-1' },
|
|
path: '/api/QQLogin/GetNewDeviceQRCode',
|
|
},
|
|
{
|
|
body: { sessionId: 'session-1' },
|
|
path: '/api/QQLogin/PollNewDeviceQR',
|
|
},
|
|
{
|
|
body: { sessionId: 'session-1' },
|
|
path: '/api/QQLogin/PollNewDeviceQR',
|
|
},
|
|
{
|
|
body: { sessionId: 'session-1' },
|
|
path: '/api/QQLogin/NewDeviceLogin',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('encodes jumpUrl as a QR image before polling new-device verification', async () => {
|
|
const client = new NapcatLoginApiClient({
|
|
post: jest
|
|
.fn()
|
|
.mockResolvedValueOnce({ jumpUrl: 'https://qq.example/new-device' })
|
|
.mockResolvedValueOnce({ status: 'waiting' })
|
|
.mockResolvedValueOnce({ status: 'expired', message: 'expired' })
|
|
.mockResolvedValueOnce({ status: 'failed', message: 'denied' }),
|
|
});
|
|
|
|
const qr = await client.getNewDeviceQRCode('session-2');
|
|
|
|
expect(qr).toMatchObject({
|
|
deviceVerifyUrl: 'https://qq.example/new-device',
|
|
sessionId: 'session-2',
|
|
status: 'qr-pending',
|
|
});
|
|
expect(qr.qrcodeUrl).toMatch(/^data:image\/png;base64,/);
|
|
expect(qr.qrcodeUrl).not.toBe('https://qq.example/new-device');
|
|
await expect(client.pollNewDeviceQR('session-2')).resolves.toEqual({
|
|
message: undefined,
|
|
sessionId: 'session-2',
|
|
status: 'qr-pending',
|
|
});
|
|
await expect(client.pollNewDeviceQR('session-2')).resolves.toEqual({
|
|
message: 'expired',
|
|
sessionId: 'session-2',
|
|
status: 'expired',
|
|
});
|
|
await expect(client.pollNewDeviceQR('session-2')).resolves.toEqual({
|
|
message: 'denied',
|
|
sessionId: 'session-2',
|
|
status: 'failed',
|
|
});
|
|
});
|
|
});
|