Files
SnowLuma/packages/protocol/tests/oidb-services/contacts/fetch-group-requests.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

215 lines
8.4 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { protobuf_decode, protobuf_encode } from '@snowluma/proton';
import type { pb, pb_repeated, uint_32, uint_64 } from '@snowluma/proton';
import type { OidbBase, OidbSvcTrpcTcp0x10C0Response } from '@snowluma/proto-defs/oidb';
import type { OidbGroupRequestList } from '@snowluma/proto-defs/oidb-actions/base';
import type { SendPacketResult } from '@snowluma/common/packet-sender';
import {
FetchGroupRequests,
FetchGroupRequestsByUid,
groupRequestNotifyType,
} from '../../../src/oidb-services/contacts/fetch-group-requests';
interface ObservedGroupRequestUserByUin {
uin?: pb<1, uint_32>;
name?: pb<2, string>;
}
interface ObservedGroupRequestGroup {
groupUin?: pb<1, uint_32>;
groupName?: pb<2, string>;
}
interface ObservedGroupRequestByUin {
sequence?: pb<1, uint_64>;
eventType?: pb<2, uint_32>;
state?: pb<3, uint_32>;
group?: pb<4, ObservedGroupRequestGroup>;
target?: pb<5, ObservedGroupRequestUserByUin>;
invitor?: pb<6, ObservedGroupRequestUserByUin>;
operatorUser?: pb<7, ObservedGroupRequestUserByUin>;
comment?: pb<10, string>;
}
interface ObservedGroupRequestResponseByUin {
requests?: pb_repeated<1, ObservedGroupRequestByUin>;
cursor?: pb<2, uint_64>;
}
function makeSender() {
const r: SendPacketResult = {
success: true,
gotResponse: true,
errorCode: 0,
errorMessage: '',
responseData: Buffer.alloc(0),
};
return { sendRawPacket: vi.fn(async () => r) };
}
describe('FetchGroupRequests namespace', () => {
it('interprets list discriminators in the wire-to-notification direction', () => {
expect(groupRequestNotifyType(1)).toBe(7);
expect(groupRequestNotifyType(2)).toBe(1);
expect(groupRequestNotifyType(7)).toBe(10);
expect(groupRequestNotifyType(10)).toBe(2);
expect(groupRequestNotifyType(22)).toBe(5);
expect(groupRequestNotifyType(100)).toBe(17);
expect(groupRequestNotifyType(5)).toBeUndefined();
expect(groupRequestNotifyType(99)).toBeUndefined();
});
it('declares 0x10C0', () => {
expect(FetchGroupRequests.command).toBe(0x10c0);
expect(FetchGroupRequests.uinForm).toBe(true);
expect((FetchGroupRequestsByUid as { uinForm?: boolean }).uinForm).toBeUndefined();
});
describe('resolveSubCommand', () => {
it('returns 1 when filtered=false (main inbox)', () => {
expect(FetchGroupRequests.resolveSubCommand({ filtered: false })).toBe(1);
});
it('returns 2 when filtered=true (low-priority inbox)', () => {
expect(FetchGroupRequests.resolveSubCommand({ filtered: true })).toBe(2);
});
});
describe('serialize', () => {
it('defaults to one native 50-item screen at cursor zero', () => {
expect(FetchGroupRequests.serialize({} as any, { filtered: false })).toEqual({
count: 50,
field2: 0n,
});
expect(FetchGroupRequestsByUid.serialize({} as any, { filtered: true })).toEqual({
count: 50,
field2: 0n,
});
});
it('preserves a caller-provided count and 64-bit cursor', () => {
const cursor = 1_785_406_628_779_279n;
expect(
FetchGroupRequests.serialize({} as any, {
filtered: false,
count: 80,
cursor,
}),
).toEqual({ count: 80, field2: cursor });
});
});
describe('invoke (e2e)', () => {
it('routes to 0x10c0_1 for the main inbox', async () => {
const sender = makeSender();
await FetchGroupRequests.invoke(sender, { filtered: false });
expect(sender.sendRawPacket.mock.calls[0]![0]).toBe('OidbSvcTrpcTcp.0x10c0_1');
});
it('routes to 0x10c0_2 for the filtered inbox', async () => {
const sender = makeSender();
await FetchGroupRequests.invoke(sender, { filtered: true });
expect(sender.sendRawPacket.mock.calls[0]![0]).toBe('OidbSvcTrpcTcp.0x10c0_2');
});
it('encodes the native envelope and requested screen', async () => {
const sender = makeSender();
await FetchGroupRequests.invoke(sender, {
filtered: false,
count: 80,
cursor: 1_785_406_628_779_279n,
});
const [, bytes] = sender.sendRawPacket.mock.calls[0]!;
const env = protobuf_decode<OidbBase<OidbGroupRequestList>>(bytes);
expect(env.reserved).toBe(1);
expect(env.body).toEqual({ count: 80, field2: 1_785_406_628_779_279n });
});
it('decodes numeric account identifiers from the native response', async () => {
const responseData = Buffer.from(
protobuf_encode<OidbBase<ObservedGroupRequestResponseByUin>>({
body: {
requests: [
{
sequence: 1_785_525_232_784_291n,
eventType: 1,
state: 1,
group: { groupUin: 1_095_186_374, groupName: 'group' },
target: { uin: 1_234_567_890, name: 'requester' },
invitor: { uin: 2_345_678_901, name: 'inviter' },
operatorUser: { uin: 3_456_789_012, name: 'operator' },
comment: 'please',
},
],
cursor: 1_785_406_628_779_279n,
},
}),
);
const result: SendPacketResult = {
success: true,
gotResponse: true,
errorCode: 0,
errorMessage: '',
responseData,
};
const sender = { sendRawPacket: vi.fn(async () => result) };
const response = await FetchGroupRequests.invoke(sender, { filtered: false });
expect(response.requests?.[0]).toMatchObject({
eventType: 1,
target: { uin: 1_234_567_890, uid: '', name: 'requester' },
invitor: { uin: 2_345_678_901, uid: '', name: 'inviter' },
operatorUser: { uin: 3_456_789_012, uid: '', name: 'operator' },
});
expect(response.field2).toBe(1_785_406_628_779_279n);
});
it('keeps string account identifiers from the native reply', async () => {
const responseData = Buffer.from(
protobuf_encode<OidbBase<OidbSvcTrpcTcp0x10C0Response>>({
body: {
requests: [
{
sequence: 99n,
eventType: 22,
state: 1,
group: { groupUin: 1001, groupName: 'group' },
target: { uid: 'u_target', name: 'invitee' },
invitor: { uid: 'u_inviter', name: 'inviter' },
comment: 'join us',
},
],
},
}),
);
const sender = {
sendRawPacket: vi.fn(async () => ({
success: true,
gotResponse: true,
errorCode: 0,
errorMessage: '',
responseData,
})),
};
const response = await FetchGroupRequests.invoke(sender, { filtered: false });
expect(response.requests?.[0]).toMatchObject({
eventType: 22,
target: { uid: 'u_target', uin: 0, name: 'invitee' },
invitor: { uid: 'u_inviter', uin: 0, name: 'inviter' },
comment: 'join us',
});
});
it('keeps the UID response path on the non-native envelope', async () => {
const sender = makeSender();
await FetchGroupRequestsByUid.invoke(sender, { filtered: false });
const [, bytes] = sender.sendRawPacket.mock.calls[0]!;
const env = protobuf_decode<OidbBase<OidbGroupRequestList>>(bytes);
expect(env.reserved ?? 0).toBe(0);
});
});
});