Mac Shell MCP Server
by cfdude
Verified
- mac-shell-mcp
- tests
import { CommandService, CommandSecurityLevel } from '../build/services/command-service.js';
describe('CommandService', () => {
let commandService;
beforeEach(() => {
// Create a new CommandService instance for each test
commandService = new CommandService('/bin/zsh');
});
test('should initialize with default whitelist', () => {
const whitelist = commandService.getWhitelist();
expect(whitelist).toBeDefined();
expect(whitelist.length).toBeGreaterThan(0);
// Check if common commands are in the whitelist
const lsCommand = whitelist.find(entry => entry.command === 'ls');
expect(lsCommand).toBeDefined();
expect(lsCommand.securityLevel).toBe(CommandSecurityLevel.SAFE);
const rmCommand = whitelist.find(entry => entry.command === 'rm');
expect(rmCommand).toBeDefined();
expect(rmCommand.securityLevel).toBe(CommandSecurityLevel.FORBIDDEN);
});
test('should add command to whitelist', () => {
const testCommand = {
command: 'test-command',
securityLevel: CommandSecurityLevel.SAFE,
description: 'Test command'
};
commandService.addToWhitelist(testCommand);
const whitelist = commandService.getWhitelist();
const addedCommand = whitelist.find(entry => entry.command === 'test-command');
expect(addedCommand).toBeDefined();
expect(addedCommand.securityLevel).toBe(CommandSecurityLevel.SAFE);
expect(addedCommand.description).toBe('Test command');
});
test('should update command security level', () => {
// First add a command
const testCommand = {
command: 'test-command',
securityLevel: CommandSecurityLevel.SAFE,
description: 'Test command'
};
commandService.addToWhitelist(testCommand);
// Then update its security level
commandService.updateSecurityLevel('test-command', CommandSecurityLevel.REQUIRES_APPROVAL);
const whitelist = commandService.getWhitelist();
const updatedCommand = whitelist.find(entry => entry.command === 'test-command');
expect(updatedCommand).toBeDefined();
expect(updatedCommand.securityLevel).toBe(CommandSecurityLevel.REQUIRES_APPROVAL);
});
test('should remove command from whitelist', () => {
// First add a command
const testCommand = {
command: 'test-command',
securityLevel: CommandSecurityLevel.SAFE,
description: 'Test command'
};
commandService.addToWhitelist(testCommand);
// Then remove it
commandService.removeFromWhitelist('test-command');
const whitelist = commandService.getWhitelist();
const removedCommand = whitelist.find(entry => entry.command === 'test-command');
expect(removedCommand).toBeUndefined();
});
test('should execute safe command', async () => {
// Execute a safe command (echo)
const result = await commandService.executeCommand('echo', ['test']);
expect(result).toBeDefined();
expect(result.stdout.trim()).toBe('test');
});
test('should reject forbidden command', async () => {
// Try to execute a forbidden command (rm)
await expect(commandService.executeCommand('rm', ['-rf', 'test'])).rejects.toThrow();
});
test('should queue command requiring approval', async () => {
// Set up event listener to capture pending command
let pendingCommandId = null;
commandService.on('command:pending', (pendingCommand) => {
pendingCommandId = pendingCommand.id;
});
// Execute a command that requires approval (mkdir)
const executePromise = commandService.executeCommand('mkdir', ['test-dir']);
// Wait a bit for the event to fire
await new Promise(resolve => setTimeout(resolve, 100));
// Check if we got a pending command
expect(pendingCommandId).not.toBeNull();
// Get pending commands
const pendingCommands = commandService.getPendingCommands();
expect(pendingCommands.length).toBe(1);
expect(pendingCommands[0].id).toBe(pendingCommandId);
// Approve the command
const approvePromise = commandService.approveCommand(pendingCommandId);
// Wait for both promises to resolve
await Promise.all([executePromise, approvePromise]);
// Check that there are no more pending commands
expect(commandService.getPendingCommands().length).toBe(0);
// Clean up
try {
await commandService.executeCommand('rmdir', ['test-dir']);
} catch (error) {
// Ignore cleanup errors
}
});
test('should deny command requiring approval', async () => {
// Set up event listener to capture pending command
let pendingCommandId = null;
commandService.on('command:pending', (pendingCommand) => {
pendingCommandId = pendingCommand.id;
});
// Execute a command that requires approval (mkdir)
const executePromise = commandService.executeCommand('mkdir', ['test-dir']);
// Wait a bit for the event to fire
await new Promise(resolve => setTimeout(resolve, 100));
// Check if we got a pending command
expect(pendingCommandId).not.toBeNull();
// Deny the command
commandService.denyCommand(pendingCommandId, 'Test denial');
// The execute promise should be rejected
await expect(executePromise).rejects.toThrow('Test denial');
// Check that there are no more pending commands
expect(commandService.getPendingCommands().length).toBe(0);
});
});