Files
arestools/test/dispatcher.test.js
T

123 lines
4.8 KiB
JavaScript
Raw Normal View History

2026-08-09 16:53:38 +03:00
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { createDispatcher, parseCommand } from '../src/core/dispatcher.js';
import { CommandRegistry } from '../src/core/registry.js';
/** Telegram сам размечает только латинские команды — здесь это воспроизводим. */
const entities = (text) => {
const match = /^\/[a-zA-Z0-9_@]+/.exec(text);
return match ? [{ type: 'bot_command', offset: 0, length: match[0].length }] : [];
};
const ctxFor = (text, extra = {}) => ({
me: { username: 'MyBot' },
from: { id: 1 },
chat: { id: 1, type: 'private' },
msg: { text, entities: entities(text), date: 0 },
business: null,
...extra,
});
describe('parseCommand', () => {
it('разбирает команду без аргументов', () => {
assert.deepEqual(parseCommand(ctxFor('/start')), { name: 'start', rest: '', args: [] });
});
it('отделяет аргументы от имени', () => {
const parsed = parseCommand(ctxFor('/pause 60 now'));
assert.equal(parsed.name, 'pause');
assert.deepEqual(parsed.args, ['60', 'now']);
assert.equal(parsed.rest, '60 now');
});
it('понимает кириллический алиас без bot_command entity', () => {
assert.equal(parseCommand(ctxFor('/старт')).name, 'старт');
});
it('принимает /cmd@своего_бота', () => {
assert.equal(parseCommand(ctxFor('/ping@mybot')).name, 'ping');
});
it('игнорирует /cmd@чужого_бота', () => {
assert.equal(parseCommand(ctxFor('/ping@OtherBot')), null);
});
it('игнорирует обычный текст и команду не в начале строки', () => {
assert.equal(parseCommand(ctxFor('привет')), null);
assert.equal(parseCommand(ctxFor('см. /help')), null);
});
it('читает команду из подписи к медиа', () => {
const ctx = { me: { username: 'MyBot' }, msg: { caption: '/start', caption_entities: entities('/start') } };
assert.equal(parseCommand(ctx).name, 'start');
});
});
describe('createDispatcher', () => {
const build = (definition) => {
const registry = new CommandRegistry();
const calls = [];
registry.register({ handler: (ctx, args) => calls.push(args), ...definition }, 'test.js');
return { dispatch: createDispatcher(registry), calls };
};
const noop = async () => {};
it('вызывает обработчик и передаёт аргументы', async () => {
const { dispatch, calls } = build({ command: 'echo' });
await dispatch(ctxFor('/echo раз два'), noop);
assert.deepEqual(calls, [['раз', 'два']]);
});
it('срабатывает на алиас', async () => {
const { dispatch, calls } = build({ command: 'help', aliases: ['помощь'] });
await dispatch(ctxFor('/помощь'), noop);
assert.equal(calls.length, 1);
});
it('пропускает дальше неизвестную команду', async () => {
const { dispatch, calls } = build({ command: 'help' });
let nexted = false;
await dispatch(ctxFor('/nope'), async () => {
nexted = true;
});
assert.equal(calls.length, 0);
assert.ok(nexted);
});
it('не выполняет команду повторно при редактировании сообщения', async () => {
const { dispatch, calls } = build({ command: 'start' });
await dispatch({ ...ctxFor('/start'), editedMessage: { text: '/start' } }, noop);
assert.equal(calls.length, 0);
});
it('scope business не работает в личке с ботом', async () => {
const { dispatch, calls } = build({ command: 'pause', scope: 'business', adminOnly: false });
await dispatch(ctxFor('/pause'), noop);
assert.equal(calls.length, 0);
});
it('scope private не работает в бизнес-чате', async () => {
const { dispatch, calls } = build({ command: 'start', scope: 'private' });
await dispatch(ctxFor('/start', { businessConnectionId: 'conn-1' }), noop);
assert.equal(calls.length, 0);
});
it('adminOnly пускает владельца бизнес-аккаунта и блокирует клиента', async () => {
const owner = build({ command: 'pause', scope: 'business', adminOnly: true });
await owner.dispatch(
ctxFor('/pause', { businessConnectionId: 'conn-1', business: { isOwner: true } }),
noop,
);
assert.equal(owner.calls.length, 1);
const client = build({ command: 'pause', scope: 'business', adminOnly: true });
await client.dispatch(
ctxFor('/pause', { businessConnectionId: 'conn-1', business: { isOwner: false } }),
noop,
);
assert.equal(client.calls.length, 0);
});
});