Files
SnowLuma/packages/protocol/tests/oidb-services/group-admin/get-admin-receive-verify.test.ts
T
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

153 lines
6.0 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import type { SendPacketResult } from '@snowluma/common/packet-sender';
import { GetAdminReceiveVerify } from '../../../src/oidb-services/group-admin/get-admin-receive-verify';
import { env, m, s, v } from '../_pb-oracle';
const target = { groupId: 12345, userId: 67890 };
const uid = 'test-member-uid';
function member(flag?: number, memberUid = uid, uin: number | undefined = target.userId) {
return m(2, [
...m(1, [...s(2, memberUid), ...(uin === undefined ? [] : v(4, uin))]),
...(flag === undefined ? [] : v(104, flag)),
]);
}
function response(members: number[], group: number | undefined = target.groupId) {
return m(4, [...(group === undefined ? [] : v(1, group)), ...members]);
}
function sender(body = response(member(0))) {
const result: SendPacketResult = {
success: true,
gotResponse: true,
errorCode: 0,
errorMessage: '',
responseData: Buffer.from(body),
};
return {
sendRawPacket: vi.fn(async (_command: string, _bytes: Uint8Array) => result),
resolveUserUid: vi.fn(async (_userId: number, _groupId?: number) => uid),
};
}
describe('administrator receive-verification read', () => {
it('resolves the member once in its group and requests only the verification state', async () => {
const deps = sender();
await expect(GetAdminReceiveVerify.invoke(deps, target)).resolves.toBe(true);
expect(deps.resolveUserUid).toHaveBeenCalledExactlyOnceWith(target.userId, target.groupId);
expect(deps.sendRawPacket).toHaveBeenCalledTimes(1);
const [command, bytes] = deps.sendRawPacket.mock.calls[0]!;
expect(command).toBe('OidbSvcTrpcTcp.0xfe7_4');
expect(Buffer.from(bytes).toString('hex')).toBe(
env(
0xfe7,
4,
[
...v(1, target.groupId),
...v(2, 3),
...v(3, 0),
...m(4, v(104, 1)),
...m(5, s(2, uid)),
],
false,
),
);
});
it.each([
[0, true],
[64, false],
[32, true],
[96, false],
[0x80000000, true],
[0xffffffff, false],
[undefined, null],
] as const)('decodes flag %s as %s while preserving missing data', async (flag, enabled) => {
const deps = sender(response(member(flag)));
await expect(GetAdminReceiveVerify.invoke(deps, target)).resolves.toBe(enabled);
});
it('selects the requested member even when unrelated members are returned first', async () => {
const deps = sender(response([...member(64, 'unrelated-member', 10001), ...member(0)]));
await expect(GetAdminReceiveVerify.invoke(deps, target)).resolves.toBe(true);
});
it('accepts UID-bound responses without an optional numeric identity', async () => {
const deps = sender(response(m(2, [...m(1, s(2, uid)), ...v(104, 64)])));
await expect(GetAdminReceiveVerify.invoke(deps, target)).resolves.toBe(false);
});
it('preserves large numeric group and member identifiers', async () => {
const large = { groupId: 2 ** 40 + 17, userId: 2 ** 41 + 19 };
const deps = sender(response(member(0, uid, large.userId), large.groupId));
await expect(GetAdminReceiveVerify.invoke(deps, large)).resolves.toBe(true);
expect(Buffer.from(deps.sendRawPacket.mock.calls[0]![1]).toString('hex')).toBe(
env(
0xfe7,
4,
[
...v(1, large.groupId),
...v(2, 3),
...v(3, 0),
...m(4, v(104, 1)),
...m(5, s(2, uid)),
],
false,
),
);
});
it.each([
['missing group', m(4, member(0)), 'missing or mismatched group'],
['wrong group', response(member(0), 999), 'missing or mismatched group'],
['missing member', response([]), 'exactly one matching member'],
['wrong member', response(member(0, 'other-uid')), 'exactly one matching member'],
[
'missing UID',
response(m(2, [...m(1, v(4, target.userId)), ...v(104, 0)])),
'exactly one matching member',
],
[
'duplicate member',
response([...member(0), ...member(64)]),
'exactly one matching member',
],
['conflicting numeric identity', response(member(0, uid, 999)), 'mismatched member UIN'],
] as const)(
'rejects %s rather than reporting a successful state',
async (_label, body, error) => {
const deps = sender([...body]);
await expect(GetAdminReceiveVerify.invoke(deps, target)).rejects.toThrow(error);
},
);
it('does not send when identity resolution fails', async () => {
const deps = sender();
deps.resolveUserUid.mockRejectedValueOnce(new Error('identity unavailable'));
await expect(GetAdminReceiveVerify.invoke(deps, target)).rejects.toThrow(
'identity unavailable',
);
expect(deps.sendRawPacket).not.toHaveBeenCalled();
});
it('propagates server rejection even if the body contains a valid state', async () => {
const deps = sender([...v(3, 1287), ...response(member(0))]);
await expect(GetAdminReceiveVerify.invoke(deps, target)).rejects.toThrow('OIDB error 1287');
});
it('propagates transport failures without retrying', async () => {
const deps = sender();
deps.sendRawPacket.mockRejectedValueOnce(new Error('transport disconnected'));
await expect(GetAdminReceiveVerify.invoke(deps, target)).rejects.toThrow(
'transport disconnected',
);
expect(deps.sendRawPacket).toHaveBeenCalledTimes(1);
});
it('rejects truncated responses', async () => {
const deps = sender([0x22, 0x80]);
await expect(GetAdminReceiveVerify.invoke(deps, target)).rejects.toThrow();
});
});