Files
TanTan fa9c73363d
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
SnowLuma v2.4.2 source snapshot
2026-11-13 09:02:00 +08:00

114 lines
4.1 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import type { SendPacketResult } from '@snowluma/common/packet-sender';
import { GetGroupShareLink } from '../../../src/oidb-services/contacts/get-group-share-link';
import type { OidbSender } from '../../../src/oidb-service';
import { env, m, s, v } from '../_pb-oracle';
const shortUrl = 'https://qm.qq.com/q/example';
const params: GetGroupShareLink.Params = {
groupId: 12345,
needShortUrl: true,
srcId: 73,
additionalParam: '',
};
function sender(body: number[] = m(12, s(1, shortUrl))) {
const packet: SendPacketResult = {
success: true,
gotResponse: true,
errorCode: 0,
errorMessage: '',
responseData: Buffer.from(env(0x9060, 3, body, true), 'hex'),
};
return {
packet,
sendRawPacket: vi.fn<OidbSender['sendRawPacket']>().mockResolvedValue(packet),
};
}
describe('GetGroupShareLink', () => {
it('requests a short group link using the verified packet layout', async () => {
const deps = sender();
await expect(GetGroupShareLink.invoke(deps, params)).resolves.toBe(shortUrl);
const body = [...v(1, 2), ...m(12, [...v(1, 12345), ...s(3, ''), ...v(4, 1)])];
expect(deps.sendRawPacket).toHaveBeenCalledExactlyOnceWith(
'OidbSvcTrpcTcp.0x9060_3',
new Uint8Array(Buffer.from(env(0x9060, 3, body, true), 'hex')),
);
});
it('preserves a false short-link flag, additional parameters, and the returned long URL', async () => {
const url = 'https://qm.qq.com/cgi-bin/qm/qr?k=example&jump_from=webapi';
const additionalParam = 'source=群二维码&test=1';
const deps = sender(m(12, s(1, url)));
await expect(
GetGroupShareLink.invoke(deps, { ...params, needShortUrl: false, additionalParam }),
).resolves.toBe(url);
const body = [...v(1, 2), ...m(12, [...v(1, 12345), ...s(3, additionalParam), ...v(4, 0)])];
expect(Buffer.from(deps.sendRawPacket.mock.calls[0]![1]).toString('hex')).toBe(
env(0x9060, 3, body, true),
);
});
it.each([0, 73, 0xffffffff])(
'retains client-compatible behavior for source %s',
async (srcId) => {
const deps = sender();
await GetGroupShareLink.invoke(deps, { ...params, srcId });
const body = [...v(1, 2), ...m(12, [...v(1, 12345), ...s(3, ''), ...v(4, 1)])];
expect(Buffer.from(deps.sendRawPacket.mock.calls[0]![1]).toString('hex')).toBe(
env(0x9060, 3, body, true),
);
},
);
it.each([
['missing result', []],
['missing URL', m(12, [])],
['empty URL', m(12, s(1, ''))],
['blank URL', m(12, s(1, ' '))],
] as const)('rejects a successful response with %s', async (_name, body) => {
await expect(GetGroupShareLink.invoke(sender([...body]), params)).rejects.toThrow(
'group share link response did not include a URL',
);
});
it('preserves server errors even if the response includes a URL', async () => {
const deps = sender();
deps.packet.responseData = Buffer.from([
...v(1, 0x9060),
...v(2, 3),
...v(3, 403),
...m(4, m(12, s(1, shortUrl))),
...s(5, 'permission denied'),
]);
await expect(GetGroupShareLink.invoke(deps, params)).rejects.toMatchObject({
name: 'OidbError',
code: 403,
serverMsg: 'permission denied',
});
});
it('reports a transport timeout instead of inventing a URL', async () => {
const deps = sender();
deps.packet.success = false;
deps.packet.gotResponse = false;
deps.packet.errorMessage = 'request timed out';
await expect(GetGroupShareLink.invoke(deps, params)).rejects.toThrow('request timed out');
});
it('rejects a truncated response', async () => {
const deps = sender();
deps.packet.responseData = Buffer.from([0x22, 0x03, 0x62, 0xff]);
await expect(GetGroupShareLink.invoke(deps, params)).rejects.toThrow();
});
});