Skip to main content
Glama

ABSD DevOps MCP Server

by anthonybir
move.test.ts8.11 kB
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { moveFileTool } from '../../src/tools/filesystem/move.js'; import { SecurityValidator } from '../../src/security/validator.js'; import type { Config } from '../../src/types/config.js'; import { tmpdir } from 'node:os'; import { mkdirSync, writeFileSync, rmSync, realpathSync, existsSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; describe('move_file tool', () => { let testDir: string; let validator: SecurityValidator; let mockLogger: any; beforeEach(() => { // Create temp test directory testDir = join(tmpdir(), `absd-mcp-test-move-${Date.now()}`); mkdirSync(testDir, { recursive: true }); // Resolve symlinks AFTER directory creation (macOS /tmp -> /private/var/folders) testDir = realpathSync(testDir); // Mock logger mockLogger = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {}, }; const config: Config = { // Use resolved path for config to handle macOS symlinks allowedDirectories: [testDir], blockedCommands: [], fileReadLineLimit: 1000, fileWriteLineLimit: 50, sessionTimeout: 30000, logLevel: 'error', urlDenylist: [], urlTimeout: 10000, }; validator = new SecurityValidator(config, mockLogger); }); afterEach(() => { // Cleanup try { rmSync(testDir, { recursive: true, force: true }); } catch { // Ignore cleanup errors } }); describe('Successful moves', () => { it('should move a file successfully', async () => { const sourcePath = join(testDir, 'source.txt'); const destPath = join(testDir, 'destination.txt'); writeFileSync(sourcePath, 'Test content'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Successfully moved'); // Verify file was moved (source no longer exists, destination exists) expect(existsSync(sourcePath)).toBe(false); expect(existsSync(destPath)).toBe(true); expect(readFileSync(destPath, 'utf-8')).toBe('Test content'); }); it('should rename a file in the same directory', async () => { const sourcePath = join(testDir, 'old-name.txt'); const destPath = join(testDir, 'new-name.txt'); writeFileSync(sourcePath, 'Rename test'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].text).toContain('Successfully moved'); expect(existsSync(sourcePath)).toBe(false); expect(existsSync(destPath)).toBe(true); expect(readFileSync(destPath, 'utf-8')).toBe('Rename test'); }); it('should move a file to a subdirectory', async () => { const sourcePath = join(testDir, 'file.txt'); const subdir = join(testDir, 'subdir'); mkdirSync(subdir, { recursive: true }); const destPath = join(subdir, 'file.txt'); writeFileSync(sourcePath, 'Move to subdir'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].text).toContain('Successfully moved'); expect(existsSync(sourcePath)).toBe(false); expect(existsSync(destPath)).toBe(true); expect(readFileSync(destPath, 'utf-8')).toBe('Move to subdir'); }); it('should move a directory', async () => { const sourceDir = join(testDir, 'source-dir'); const destDir = join(testDir, 'dest-dir'); mkdirSync(sourceDir, { recursive: true }); writeFileSync(join(sourceDir, 'file.txt'), 'Content in directory'); const result = await moveFileTool( { source: sourceDir, destination: destDir }, validator, mockLogger ); expect(result.content[0].text).toContain('Successfully moved'); expect(existsSync(sourceDir)).toBe(false); expect(existsSync(destDir)).toBe(true); expect(existsSync(join(destDir, 'file.txt'))).toBe(true); }); it('should overwrite destination file if it exists', async () => { const sourcePath = join(testDir, 'source.txt'); const destPath = join(testDir, 'destination.txt'); writeFileSync(sourcePath, 'New content'); writeFileSync(destPath, 'Old content'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].text).toContain('Successfully moved'); expect(existsSync(sourcePath)).toBe(false); expect(existsSync(destPath)).toBe(true); expect(readFileSync(destPath, 'utf-8')).toBe('New content'); }); }); describe('Security validation', () => { it('should reject source path outside allowed directories', async () => { const sourcePath = '/tmp/outside-allowed.txt'; const destPath = join(testDir, 'destination.txt'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Error (source)'); }); it('should reject destination path outside allowed directories', async () => { const sourcePath = join(testDir, 'source.txt'); const destPath = '/tmp/outside-allowed.txt'; writeFileSync(sourcePath, 'Test'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Error (destination)'); // Source should still exist since move failed expect(existsSync(sourcePath)).toBe(true); }); it('should reject path traversal attempts in source', async () => { const sourcePath = join(testDir, '../../../etc/passwd'); const destPath = join(testDir, 'destination.txt'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].text).toContain('Error (source)'); }); it('should reject path traversal attempts in destination', async () => { const sourcePath = join(testDir, 'source.txt'); const destPath = join(testDir, '../../../tmp/evil.txt'); writeFileSync(sourcePath, 'Test'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].text).toContain('Error (destination)'); expect(existsSync(sourcePath)).toBe(true); }); }); describe('Error handling', () => { it('should handle non-existent source file', async () => { const sourcePath = join(testDir, 'nonexistent.txt'); const destPath = join(testDir, 'destination.txt'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Error:'); }); it('should handle moving to non-existent directory', async () => { const sourcePath = join(testDir, 'source.txt'); const destPath = join(testDir, 'nonexistent-dir', 'destination.txt'); writeFileSync(sourcePath, 'Test'); const result = await moveFileTool( { source: sourcePath, destination: destPath }, validator, mockLogger ); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Error:'); // Source should still exist since move failed expect(existsSync(sourcePath)).toBe(true); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anthonybir/ABSD_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server