Skip to main content
Glama
fsOperations.property.test.ts5.42 kB
/** * Property-based tests for file system operations * Tests universal properties that should hold across all valid inputs */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as fc from 'fast-check'; import fs from 'fs/promises'; import path from 'path'; import os from 'os'; import { writeFileAtomic, readFileContent, } from '../src/utils/fsUtils'; import { executeWriteFile } from '../src/tools/writeFile'; import { ServerConfig } from '../src/config'; describe('File System Operations - Property Tests', () => { let testDir: string; beforeEach(async () => { // Create a temporary test directory testDir = path.join(os.tmpdir(), `fsops-property-test-${Date.now()}`); await fs.mkdir(testDir, { recursive: true }); }); afterEach(async () => { // Clean up test directory try { await fs.rm(testDir, { recursive: true, force: true }); } catch { // Ignore cleanup errors } }); // Generator for valid relative file paths (no traversal) const validRelativePathGenerator = () => fc .array( fc.stringMatching(/^[a-zA-Z0-9_-]+$/), { minLength: 1, maxLength: 3 } ) .map(parts => parts.join(path.sep) + '.txt'); // Generator for file content (including unicode and special characters) const fileContentGenerator = () => fc.string({ minLength: 0, maxLength: 1000 }); // Property 5: Write-read round trip // Feature: mcp-workspace-server, Property 5: Write-read round trip // Validates: Requirements 3.1 it('Property 5: writing then reading returns same content', async () => { await fc.assert( fc.asyncProperty( fc.tuple(validRelativePathGenerator(), fileContentGenerator()), async ([relativePath, content]) => { const filePath = path.join(testDir, relativePath); // Write the content await writeFileAtomic(filePath, content, true); // Read it back const result = await readFileContent(filePath); // Content should match exactly expect(result.content).toBe(content); } ), { numRuns: 100 } ); }); // Property 8: Atomic write operations // Feature: mcp-workspace-server, Property 8: Atomic write operations // Validates: Requirements 3.5 it('Property 8: write failures leave no partial content', async () => { await fc.assert( fc.asyncProperty( fc.tuple(validRelativePathGenerator(), fileContentGenerator()), async ([relativePath, content]) => { const filePath = path.join(testDir, relativePath); // Perform the write operation await writeFileAtomic(filePath, content, true); // After a successful write, verify no temp files exist const dir = path.dirname(filePath); const files = await fs.readdir(dir); const tempFiles = files.filter(f => f.includes('.tmp')); // No temp files should remain after successful write expect(tempFiles).toHaveLength(0); // The target file should exist and contain the correct content const result = await readFileContent(filePath); expect(result.content).toBe(content); // File should be complete (size matches content) const expectedSize = Buffer.byteLength(content, 'utf-8'); expect(result.size).toBe(expectedSize); } ), { numRuns: 100 } ); }); // Property 6: Parent directory creation // Feature: mcp-workspace-server, Property 6: Parent directory creation // Validates: Requirements 3.2 it('Property 6: writing files creates all necessary parent directories', async () => { // Generator for nested paths (2-5 levels deep) const nestedPathGenerator = () => fc .array( fc.stringMatching(/^[a-zA-Z0-9_-]+$/), { minLength: 2, maxLength: 5 } ) .map(parts => parts.join(path.sep) + '.txt'); await fc.assert( fc.asyncProperty( fc.tuple(nestedPathGenerator(), fileContentGenerator()), async ([relativePath, content]) => { const config: ServerConfig = { workspaceRoot: testDir, allowedCommands: [], readOnly: false, logLevel: 'error', commandTimeout: 300000, }; // Write file with createDirectories option const result = await executeWriteFile( { path: relativePath, content, createDirectories: true, }, config ); // Verify the file was written expect(result.path).toBe(relativePath); expect(result.bytesWritten).toBeGreaterThanOrEqual(0); // Verify all parent directories were created const fullPath = path.join(testDir, relativePath); const dirPath = path.dirname(fullPath); // Check that the directory exists const dirStats = await fs.stat(dirPath); expect(dirStats.isDirectory()).toBe(true); // Verify the file exists and has correct content const fileStats = await fs.stat(fullPath); expect(fileStats.isFile()).toBe(true); const readContent = await fs.readFile(fullPath, 'utf-8'); expect(readContent).toBe(content); } ), { numRuns: 100 } ); }); });

Latest Blog Posts

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/ShayYeffet/mcp_server'

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