Files
SnowLuma/packages/onebot/tests/sdk-reverse-websocket.test.ts
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

147 lines
5.8 KiB
TypeScript

import { createHash } from 'node:crypto';
import type { AddressInfo } from 'node:net';
import { Readable } from 'node:stream';
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
createReverseWebSocketServer,
type SnowLumaReverseWebSocketServer,
} from '../../sdk/src/node/index';
import { WsClientAdapter } from '../src/network/ws-client-adapter';
import { buildDispatchPayload } from '../src/event-filter';
import { okResponse, type JsonObject, type WsRole } from '../src/types';
import type { ApiActionContext } from '../src/api-handler';
import { actions as uploadActions, __test as uploadTest } from '../src/actions/stream-file';
import { actions as downloadActions } from '../src/actions/stream-download';
import { createCompiledTestHandler, testAction } from './helpers/compiled-action-handler';
describe('SDK reverse server against the real OneBot reverse adapter', () => {
const adapters: WsClientAdapter[] = [];
let server: SnowLumaReverseWebSocketServer | undefined;
afterEach(async () => {
await Promise.all(adapters.splice(0).map((adapter) => adapter.close()));
await server?.close();
for (const key of uploadTest.uploads.keys()) {
uploadTest.cleanupUpload(key, true);
}
});
it.each(['universal', 'split'] as const)(
'%s carries events, actions and complete binary streams',
async (mode) => {
server = createReverseWebSocketServer({
port: 0,
accessToken: 'integration-test',
selfIds: ['10001'],
});
const errors = vi.fn();
server.on('error', errors);
await server.listen();
const { port } = server.address as AddressInfo;
const api = createCompiledTestHandler({} as ApiActionContext, [
...uploadActions,
...downloadActions,
testAction('sdk_ping', () => okResponse({ pong: true })),
]);
const roles: WsRole[] = mode === 'universal' ? ['Universal'] : ['Api', 'Event'];
for (const role of roles) {
const adapter = new WsClientAdapter(
`reverse-${role}`,
{
name: `reverse-${role}`,
url: `ws://127.0.0.1:${port}/onebot/v11/ws`,
accessToken: 'integration-test',
role,
messageFormat: 'array',
reportSelfMessage: false,
},
{
uin: '10001',
api,
buildLifecycleEvent: (subType) => ({
self_id: 10001,
post_type: 'meta_event',
meta_event_type: 'lifecycle',
sub_type: subType,
}),
buildHeartbeatEvent: () => ({
self_id: 10001,
post_type: 'meta_event',
meta_event_type: 'heartbeat',
}),
},
);
adapters.push(adapter);
adapter.open();
}
await vi.waitFor(() => {
expect(server!.getClient('10001')?.canCallActions).toBe(true);
expect(server!.getClient('10001')?.canReceiveEvents).toBe(true);
expect(adapters.every((adapter) => adapter.describeStatus().status === 'ok')).toBe(
true,
);
});
const client = server.getClient('10001')!;
expect(await client.raw('sdk_ping')).toEqual({ pong: true });
const seen = vi.fn();
client.on('event', seen);
const received = client.waitForMessage((event) => event.message_id === -42, {
timeoutMs: 1000,
});
const event: JsonObject = {
self_id: 10001,
time: 1,
post_type: 'message',
message_type: 'private',
sub_type: 'friend',
user_id: 20001,
message_id: -42,
message: [{ type: 'text', data: { text: 'wire test' } }],
raw_message: 'wire test',
};
for (const adapter of adapters) {
const own = { ...event, post_type: 'message_sent' };
adapter.onEvent(own, buildDispatchPayload(own));
adapter.onEvent(event, buildDispatchPayload(event));
}
expect((await received).message_id).toBe(-42);
expect(seen.mock.calls.some(([event]) => event.post_type === 'message_sent')).toBe(
false,
);
const bytes = Buffer.from('reverse transport binary stream\n'.repeat(3000));
const sha256 = createHash('sha256').update(bytes).digest('hex');
const uploaded = await client.uploadFileStream(Readable.from([bytes]), {
fileSize: bytes.length,
chunkSize: 8192,
filename: 'reverse.bin',
expectedSha256: sha256,
fileRetention: 0,
});
const chunks: Uint8Array[] = [];
const stream = client.downloadFileStream({
file: uploaded.file_path,
chunk_size: 4096,
});
while (true) {
const next = await stream.next();
if (next.done) {
expect(next.value.total_bytes).toBe(bytes.length);
break;
}
chunks.push(next.value);
}
expect(Buffer.concat(chunks)).toEqual(bytes);
expect(errors).not.toHaveBeenCalled();
},
);
});