Add #help command — short single-message list of all commands (v0.21.6)
CI / build + unit (pull_request) Failing after 21s
CI / build + unit (pull_request) Failing after 21s
Owner request: 'now add #help with short list of all command'. Registered in
the CommandService constructor next to #ping; returns a SINGLE multi-line
string (one packet, ~203 chars) — not an array, which would drip-feed one
spaced packet per line through the v0.21.5 MESH_MULTI_SEND_DELAY_MS loop.
Also hardened the v0.21.5 multi-send-spacing spec: its wall-clock assertion
('first send < 8 ms after t0') flaked under full-suite load — replaced with
fake timers, which assert deterministically that line 1 is out before any time
elapses and lines 2-3 land exactly one delay apart.
This commit is contained in:
@@ -41,6 +41,15 @@ describe('CommandService.onRx', () => {
|
||||
expect(mesh.sendText).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('replies #help with a short single-message list of all commands', async () => {
|
||||
const [svc, mesh] = makeService();
|
||||
await svc.onRx(2, '#help', 42, 7);
|
||||
expect(mesh.respond).toHaveBeenCalledTimes(1); // one packet — not one per line
|
||||
const text = mesh.respond.mock.calls[0][1];
|
||||
expect(text).toContain('#ping');
|
||||
expect(text).toContain('#rss');
|
||||
});
|
||||
|
||||
it('falls back to a directed sendText on COMMAND_CHANNEL when the trigger has no packet id (DM)', async () => {
|
||||
const [svc, mesh] = makeService();
|
||||
await svc.onRx(2, '#ping', 42); // from present but no id — defensive path
|
||||
@@ -126,26 +135,32 @@ describe('CommandService.onRx', () => {
|
||||
});
|
||||
|
||||
it('spaces consecutive multi-line sends by MESH_MULTI_SEND_DELAY_MS — no delay before the first line', async () => {
|
||||
process.env.MESH_MULTI_SEND_DELAY_MS = '10'; // short so the test stays fast
|
||||
const sentAt: number[] = [];
|
||||
process.env.MESH_MULTI_SEND_DELAY_MS = '50'; // short so the test stays fast
|
||||
let sends = 0;
|
||||
const respond = vi.fn(async () => {
|
||||
sentAt.push(Date.now());
|
||||
sends++;
|
||||
return { id: 1 };
|
||||
});
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const [svc, mesh] = makeService(undefined, respond);
|
||||
svc.register('multi3', () => ['a: one', 'b: two', 'c: three']);
|
||||
|
||||
const t0 = Date.now();
|
||||
await svc.onRx(2, '#multi3', 42, 9);
|
||||
const p = svc.onRx(2, '#multi3', 42, 9);
|
||||
// flush microtasks without advancing time — the first line must already be out (no delay before it)
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
expect(sends).toBe(1);
|
||||
|
||||
// each subsequent line waits exactly MESH_MULTI_SEND_DELAY_MS after the previous one landed
|
||||
await vi.advanceTimersByTimeAsync(50);
|
||||
expect(sends).toBe(2);
|
||||
await vi.advanceTimersByTimeAsync(50);
|
||||
expect(sends).toBe(3);
|
||||
|
||||
await p;
|
||||
expect(mesh.respond).toHaveBeenCalledTimes(3);
|
||||
// the first line goes out immediately (no delay before it) — only microtask-scale overhead between t0 and the first send
|
||||
expect(sentAt[0] - t0).toBeLessThan(8);
|
||||
// each subsequent line waits at least MESH_MULTI_SEND_DELAY_MS after the previous one landed
|
||||
expect(sentAt[1] - sentAt[0]).toBeGreaterThanOrEqual(10);
|
||||
expect(sentAt[2] - sentAt[1]).toBeGreaterThanOrEqual(10);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
delete process.env.MESH_MULTI_SEND_DELAY_MS;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -43,6 +43,17 @@ export class CommandService {
|
||||
rss?: RssService,
|
||||
) {
|
||||
this.register('ping', () => '#pong!');
|
||||
// single multi-line string = ONE packet — not an array, which would drip-feed one spaced packet per line
|
||||
this.register('help', () =>
|
||||
[
|
||||
'#ping - alive check',
|
||||
'#rss help - rss subcommands',
|
||||
'#rss add <url> <name> - add feed with AI tags',
|
||||
'#rss remove <name> - drop feed',
|
||||
'#rss get <name|#tag> - news summary',
|
||||
'#rss sources / #rss tags - list feeds/tags',
|
||||
].join('\n'),
|
||||
);
|
||||
if (rss) this.registerPrefix('rss', (ctx) => rss.handle(ctx));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user