Files
flynn/src/commands/builtin/index.test.ts
T

260 lines
8.5 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { createApproveCommand, createApprovalsCommand, createContextCommand, createDenyCommand, createElevateCommand, createModelCommand, createQueueCommand, createResearchCommand, createSkillCommand, createTransferCommand } from './index.js';
describe('builtin /model command', () => {
it('passes through the full argument string', async () => {
const cmd = createModelCommand();
const setModel = vi.fn(() => 'ok');
const result = await cmd.execute(['default', 'github/gpt-5-mini'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/model default github/gpt-5-mini',
services: { setModel },
});
expect(setModel).toHaveBeenCalledWith('default github/gpt-5-mini');
expect(result).toEqual({ handled: true, text: 'ok' });
});
it('still works for single-argument tier switching', async () => {
const cmd = createModelCommand();
const setModel = vi.fn(() => 'switched');
const result = await cmd.execute(['fast'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/model fast',
services: { setModel },
});
expect(setModel).toHaveBeenCalledWith('fast');
expect(result).toEqual({ handled: true, text: 'switched' });
});
});
describe('builtin /research command', () => {
it('delegates to the research agent with the full task string', async () => {
const cmd = createResearchCommand();
const delegateAgent = vi.fn(() => 'research output');
const result = await cmd.execute(['compare', 'k0s', 'vs', 'k3s'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/research compare k0s vs k3s',
services: { delegateAgent },
});
expect(delegateAgent).toHaveBeenCalledWith('research', 'compare k0s vs k3s');
expect(result).toEqual({ handled: true, text: 'research output' });
});
it('returns usage when no task is provided', async () => {
const cmd = createResearchCommand();
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/research',
services: {},
});
expect(result).toEqual({ handled: true, text: 'Usage: /research <question or task>' });
});
});
describe('builtin /elevate command', () => {
it('passes through the full argument string', async () => {
const cmd = createElevateCommand();
const setElevation = vi.fn(() => 'ok');
const result = await cmd.execute(['10m', 'reason', '--yes'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/elevate 10m reason --yes',
services: { setElevation },
});
expect(setElevation).toHaveBeenCalledWith('10m reason --yes');
expect(result).toEqual({ handled: true, text: 'ok' });
});
it('shows status when no args are provided', async () => {
const cmd = createElevateCommand();
const getElevation = vi.fn(() => 'status');
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/elevate',
services: { getElevation },
});
expect(getElevation).toHaveBeenCalledOnce();
expect(result).toEqual({ handled: true, text: 'status' });
});
});
describe('builtin /queue command', () => {
it('shows queue status with no args', async () => {
const cmd = createQueueCommand();
const getQueue = vi.fn(() => 'queue status');
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/queue',
services: { getQueue },
});
expect(getQueue).toHaveBeenCalledOnce();
expect(result).toEqual({ handled: true, text: 'queue status' });
});
it('passes through set operations', async () => {
const cmd = createQueueCommand();
const setQueue = vi.fn(() => 'updated');
const result = await cmd.execute(['set', 'mode', 'followup'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/queue set mode followup',
services: { setQueue },
});
expect(setQueue).toHaveBeenCalledWith('mode followup');
expect(result).toEqual({ handled: true, text: 'updated' });
});
it('calls reset operation', async () => {
const cmd = createQueueCommand();
const resetQueue = vi.fn(() => 'reset');
const result = await cmd.execute(['reset'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/queue reset',
services: { resetQueue },
});
expect(resetQueue).toHaveBeenCalledOnce();
expect(result).toEqual({ handled: true, text: 'reset' });
});
});
describe('builtin /context command', () => {
it('returns context usage text from services', async () => {
const cmd = createContextCommand();
const getContext = vi.fn(() => 'Context Usage\n\n75.0%');
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/context',
services: { getContext },
});
expect(getContext).toHaveBeenCalledOnce();
expect(result).toEqual({ handled: true, text: 'Context Usage\n\n75.0%' });
});
it('returns not-available when service is missing', async () => {
const cmd = createContextCommand();
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/context',
services: {},
});
expect(result).toEqual({ handled: true, text: 'Context command is not available in this session.' });
});
});
describe('builtin /transfer command', () => {
it('passes through the full target argument string', async () => {
const cmd = createTransferCommand();
const transferSession = vi.fn(() => 'Session transferred');
const result = await cmd.execute(['telegram'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/transfer telegram',
services: { transferSession },
});
expect(transferSession).toHaveBeenCalledWith('telegram');
expect(result).toEqual({ handled: true, text: 'Session transferred' });
});
it('returns not-available when service is missing', async () => {
const cmd = createTransferCommand();
const result = await cmd.execute(['tui'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/transfer tui',
services: {},
});
expect(result).toEqual({ handled: true, text: 'Transfer command is not available in this session.' });
});
});
describe('builtin approval commands', () => {
it('calls getApprovals for /approvals', async () => {
const cmd = createApprovalsCommand();
const getApprovals = vi.fn(() => '1 pending');
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/approvals',
services: { getApprovals },
});
expect(getApprovals).toHaveBeenCalledOnce();
expect(result).toEqual({ handled: true, text: '1 pending' });
});
it('passes raw input to approvePending for /approve', async () => {
const cmd = createApproveCommand();
const approvePending = vi.fn(() => 'approved');
const result = await cmd.execute(['abc-123'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/approve abc-123',
services: { approvePending },
});
expect(approvePending).toHaveBeenCalledWith('abc-123');
expect(result).toEqual({ handled: true, text: 'approved' });
});
it('passes raw input to denyPending for /deny', async () => {
const cmd = createDenyCommand();
const denyPending = vi.fn(() => 'denied');
const result = await cmd.execute(['abc-123', 'too', 'risky'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/deny abc-123 too risky',
services: { denyPending },
});
expect(denyPending).toHaveBeenCalledWith('abc-123 too risky');
expect(result).toEqual({ handled: true, text: 'denied' });
});
});
describe('builtin /skill command', () => {
it('passes subcommand text to skillCommand service', async () => {
const cmd = createSkillCommand();
const skillCommand = vi.fn(() => 'ok');
const result = await cmd.execute(['search', 'calendar'], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/skill search calendar',
services: { skillCommand },
});
expect(skillCommand).toHaveBeenCalledWith('search calendar');
expect(result).toEqual({ handled: true, text: 'ok' });
});
});