UNPKG

levtor

Version:

Levtor — a smart, fuzzy, AI-powered chat command handler and guardian for wallets and AI integration.

163 lines (135 loc) 5.43 kB
import { describe, it, expect } from 'vitest'; import { CommandHandler } from '../commandHandler'; describe('Levtor', () => { it('registers and executes commands', async () => { const levtor = new CommandHandler({}); let executed = false; levtor.register('balance', async () => { executed = true; return 'Balance OK'; }, { aliases: ['bal', 'balnce'] }); const res = await levtor.handle('!balnce'); expect(executed).toBe(true); expect(res).toBe('Balance OK'); }); it('applies preprocessors', async () => { const levtor = new CommandHandler({}); levtor.usePreprocessor(async (msg: string) => ({ message: msg.trim().toLowerCase() })); levtor.register('ping', async () => 'pong'); const res = await levtor.handle(' !PING '); expect(res).toBe('pong'); }); it('aliases and fuzzy match work', async () => { const levtor = new CommandHandler({}); levtor.register('balance', async () => '100 USD', { aliases: ['bal'] }); expect(await levtor.handle('!bal')).toBe('100 USD'); expect(await levtor.handle('!balan')).toBe('100 USD'); // fuzzy match }); it('caching works', async () => { const levtor = new CommandHandler({}); let callCount = 0; levtor.register('count', async () => { callCount++; return callCount.toString(); }, { cacheTtlMs: 5000 }); const r1 = await levtor.handle('!count'); const r2 = await levtor.handle('!count'); expect(r1).toBe('1'); expect(r2).toBe('1'); // cached }); it('debounce works', async () => { const levtor = new CommandHandler({}); levtor.register('fast', async () => 'done', { debounceMs: 1000 }); const r1 = await levtor.handle('!fast'); const r2 = await levtor.handle('!fast'); expect(r1).toBe('done'); expect(r2).toBe('🕒 Please wait...'); }); it('lifecycle hooks fire', async () => { const levtor = new CommandHandler({}); const events: string[] = []; levtor.register('hooked', async () => 'ok'); levtor.on('before', async (cmd: string) => { events.push(`before:${cmd}`); }); levtor.on('after', async (cmd: string) => { events.push(`after:${cmd}`); }); levtor.on('error', async () => { events.push('error'); }); const res = await levtor.handle('!hooked'); expect(res).toBe('ok'); expect(events).toEqual(['before:hooked', 'after:hooked']); }); it('formatWithAI function applied', async () => { const levtor = new CommandHandler( {}, async (out: string) => `[AI] ${out}` ); levtor.register('test', async () => 'hello'); const res = await levtor.handle('!test'); expect(res).toBe('[AI] hello'); }); it('handles errors in command execution', async () => { const levtor = new CommandHandler({}); levtor.register('fail', async () => { throw new Error('fail!'); }); let errorFired = false; levtor.on('error', async () => { errorFired = true; }); const res = await levtor.handle('!fail'); expect(res).toBe('⚠️ An error occurred.'); expect(errorFired).toBe(true); }); it('debounce is per argument', async () => { const levtor = new CommandHandler({}); levtor.register('deb', async ([msg = '']) => msg, { debounceMs: 1000 }); const r1 = await levtor.handle('!deb a'); const r2 = await levtor.handle('!deb b'); expect(r1).toBe('a'); expect(r2).toBe('b'); }); it('cache is per argument', async () => { const levtor = new CommandHandler({}); let count = 0; levtor.register('cache', async ([msg]) => (++count).toString(), { cacheTtlMs: 5000 }); const r1 = await levtor.handle('!cache a'); const r2 = await levtor.handle('!cache b'); expect(r1).toBe('1'); expect(r2).toBe('2'); }); it('fuzzy matches with multiple close aliases', async () => { const levtor = new CommandHandler({}); levtor.register('alpha', async () => 'A', { aliases: ['alfa', 'alfa1'] }); expect(await levtor.handle('!alfa')).toBe('A'); expect(await levtor.handle('!alfa1')).toBe('A'); expect(await levtor.handle('!alph')).toBe('A'); }); it('AI formatting receives context', async () => { let ctxSeen: any = null; const levtor = new CommandHandler( {}, async (out: string, context) => { ctxSeen = context; return `[AI] ${out}`; } ); levtor.register('ctx', async () => 'ok'); const res = await levtor.handle('!ctx'); expect(res).toBe('[AI] ok'); expect(ctxSeen.command).toBe('ctx'); }); it('handles named arguments', async () => { const levtor = new CommandHandler({}); levtor.register('named', async (_pos, named) => named.foo as string); const res = await levtor.handle('!named foo=bar'); expect(res).toBe('bar'); }); it('handles both positional and named arguments', async () => { const levtor = new CommandHandler({}); levtor.register('mix', async (pos, named) => `${pos[0]}-${named.x}`); const res = await levtor.handle('!mix hello x=world'); expect(res).toBe('hello-world'); }); it('lifecycle hooks receive context', async () => { const levtor = new CommandHandler({}, undefined); let beforeCtx: any = null; levtor.register('ctx', async () => 'ok'); levtor.on('before', async (_cmd, _payload, ctx) => { beforeCtx = ctx; }); await levtor.handle('!ctx', { user: 'bob' }); expect(beforeCtx).toEqual({ user: 'bob' }); }); });