mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-18 08:13:24 +02:00
Add working test config for filesystem
This commit is contained in:
140
src/filesystem/__tests__/path-utils.test.ts
Normal file
140
src/filesystem/__tests__/path-utils.test.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { normalizePath, expandHome, convertToWindowsPath } from '../path-utils.js';
|
||||
|
||||
describe('Path Utilities', () => {
|
||||
describe('convertToWindowsPath', () => {
|
||||
it('leaves Unix paths unchanged', () => {
|
||||
expect(convertToWindowsPath('/usr/local/bin'))
|
||||
.toBe('/usr/local/bin');
|
||||
expect(convertToWindowsPath('/home/user/some path'))
|
||||
.toBe('/home/user/some path');
|
||||
});
|
||||
|
||||
it('converts WSL paths to Windows format', () => {
|
||||
expect(convertToWindowsPath('/mnt/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('converts Unix-style Windows paths to Windows format', () => {
|
||||
expect(convertToWindowsPath('/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('leaves Windows paths unchanged but ensures backslashes', () => {
|
||||
expect(convertToWindowsPath('C:\\NS\\MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
expect(convertToWindowsPath('C:/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('handles Windows paths with spaces', () => {
|
||||
expect(convertToWindowsPath('C:\\Program Files\\Some App'))
|
||||
.toBe('C:\\Program Files\\Some App');
|
||||
expect(convertToWindowsPath('C:/Program Files/Some App'))
|
||||
.toBe('C:\\Program Files\\Some App');
|
||||
});
|
||||
|
||||
it('handles uppercase and lowercase drive letters', () => {
|
||||
expect(convertToWindowsPath('/mnt/d/some/path'))
|
||||
.toBe('D:\\some\\path');
|
||||
expect(convertToWindowsPath('/d/some/path'))
|
||||
.toBe('D:\\some\\path');
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizePath', () => {
|
||||
it('preserves Unix paths', () => {
|
||||
expect(normalizePath('/usr/local/bin'))
|
||||
.toBe('/usr/local/bin');
|
||||
expect(normalizePath('/home/user/some path'))
|
||||
.toBe('/home/user/some path');
|
||||
expect(normalizePath('"/usr/local/some app/"'))
|
||||
.toBe('/usr/local/some app');
|
||||
});
|
||||
|
||||
it('removes surrounding quotes', () => {
|
||||
expect(normalizePath('"C:\\NS\\My Kindle Content"'))
|
||||
.toBe('C:\\NS\\My Kindle Content');
|
||||
});
|
||||
|
||||
it('normalizes backslashes', () => {
|
||||
expect(normalizePath('C:\\\\NS\\\\MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('converts forward slashes to backslashes on Windows', () => {
|
||||
expect(normalizePath('C:/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('handles WSL paths', () => {
|
||||
expect(normalizePath('/mnt/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('handles Unix-style Windows paths', () => {
|
||||
expect(normalizePath('/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('handles paths with spaces and mixed slashes', () => {
|
||||
expect(normalizePath('C:/NS/My Kindle Content'))
|
||||
.toBe('C:\\NS\\My Kindle Content');
|
||||
expect(normalizePath('/mnt/c/NS/My Kindle Content'))
|
||||
.toBe('C:\\NS\\My Kindle Content');
|
||||
expect(normalizePath('C:\\Program Files (x86)\\App Name'))
|
||||
.toBe('C:\\Program Files (x86)\\App Name');
|
||||
expect(normalizePath('"C:\\Program Files\\App Name"'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
expect(normalizePath(' C:\\Program Files\\App Name '))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
});
|
||||
|
||||
it('preserves spaces in all path formats', () => {
|
||||
expect(normalizePath('/mnt/c/Program Files/App Name'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
expect(normalizePath('/c/Program Files/App Name'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
expect(normalizePath('C:/Program Files/App Name'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
});
|
||||
|
||||
it('handles special characters in paths', () => {
|
||||
// Test ampersand in path
|
||||
expect(normalizePath('C:\\NS\\Sub&Folder'))
|
||||
.toBe('C:\\NS\\Sub&Folder');
|
||||
expect(normalizePath('C:/NS/Sub&Folder'))
|
||||
.toBe('C:\\NS\\Sub&Folder');
|
||||
expect(normalizePath('/mnt/c/NS/Sub&Folder'))
|
||||
.toBe('C:\\NS\\Sub&Folder');
|
||||
|
||||
// Test tilde in path (short names in Windows)
|
||||
expect(normalizePath('C:\\NS\\MYKIND~1'))
|
||||
.toBe('C:\\NS\\MYKIND~1');
|
||||
expect(normalizePath('/Users/NEMANS~1/FOLDER~2/SUBFO~1/Public/P12PST~1'))
|
||||
.toBe('/Users/NEMANS~1/FOLDER~2/SUBFO~1/Public/P12PST~1');
|
||||
|
||||
// Test other special characters
|
||||
expect(normalizePath('C:\\Path with #hash'))
|
||||
.toBe('C:\\Path with #hash');
|
||||
expect(normalizePath('C:\\Path with (parentheses)'))
|
||||
.toBe('C:\\Path with (parentheses)');
|
||||
expect(normalizePath('C:\\Path with [brackets]'))
|
||||
.toBe('C:\\Path with [brackets]');
|
||||
expect(normalizePath('C:\\Path with @at+plus$dollar%percent'))
|
||||
.toBe('C:\\Path with @at+plus$dollar%percent');
|
||||
});
|
||||
});
|
||||
|
||||
describe('expandHome', () => {
|
||||
it('expands ~ to home directory', () => {
|
||||
const result = expandHome('~/test');
|
||||
expect(result).toContain('test');
|
||||
expect(result).not.toContain('~');
|
||||
});
|
||||
|
||||
it('leaves other paths unchanged', () => {
|
||||
expect(expandHome('C:/test')).toBe('C:/test');
|
||||
});
|
||||
});
|
||||
});
|
||||
23
src/filesystem/jest.config.cjs
Normal file
23
src/filesystem/jest.config.cjs
Normal file
@@ -0,0 +1,23 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
moduleNameMapper: {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
useESM: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
testMatch: ['**/__tests__/**/*.test.ts'],
|
||||
collectCoverageFrom: [
|
||||
'**/*.ts',
|
||||
'!**/__tests__/**',
|
||||
'!**/dist/**',
|
||||
],
|
||||
}
|
||||
@@ -16,20 +16,26 @@
|
||||
"scripts": {
|
||||
"build": "tsc && shx chmod +x dist/*.js",
|
||||
"prepare": "npm run build",
|
||||
"watch": "tsc --watch"
|
||||
"watch": "tsc --watch",
|
||||
"test": "jest --config=jest.config.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "0.5.0",
|
||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
||||
"diff": "^5.1.0",
|
||||
"glob": "^10.3.10",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod-to-json-schema": "^3.23.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@types/diff": "^5.0.9",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/node": "^22",
|
||||
"jest": "^29.7.0",
|
||||
"shx": "^0.3.4",
|
||||
"typescript": "^5.3.3"
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
}
|
||||
86
src/filesystem/path-utils.ts
Normal file
86
src/filesystem/path-utils.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import path from "path";
|
||||
import os from 'os';
|
||||
|
||||
/**
|
||||
* Converts WSL or Unix-style Windows paths to Windows format
|
||||
* @param p The path to convert
|
||||
* @returns Converted Windows path
|
||||
*/
|
||||
export function convertToWindowsPath(p: string): string {
|
||||
// Handle WSL paths (/mnt/c/...)
|
||||
if (p.startsWith('/mnt/')) {
|
||||
const driveLetter = p.charAt(5).toUpperCase();
|
||||
const pathPart = p.slice(6).replace(/\//g, '\\');
|
||||
return `${driveLetter}:${pathPart}`;
|
||||
}
|
||||
|
||||
// Handle Unix-style Windows paths (/c/...)
|
||||
if (p.match(/^\/[a-zA-Z]\//)) {
|
||||
const driveLetter = p.charAt(1).toUpperCase();
|
||||
const pathPart = p.slice(2).replace(/\//g, '\\');
|
||||
return `${driveLetter}:${pathPart}`;
|
||||
}
|
||||
|
||||
// Handle standard Windows paths, ensuring backslashes
|
||||
if (p.match(/^[a-zA-Z]:/)) {
|
||||
return p.replace(/\//g, '\\');
|
||||
}
|
||||
|
||||
// Leave non-Windows paths unchanged
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes path by standardizing format while preserving OS-specific behavior
|
||||
* @param p The path to normalize
|
||||
* @returns Normalized path
|
||||
*/
|
||||
export function normalizePath(p: string): string {
|
||||
// Remove any surrounding quotes and whitespace
|
||||
p = p.trim().replace(/^["']|["']$/g, '');
|
||||
|
||||
// Check if this is a Unix path (starts with / but not a Windows or WSL path)
|
||||
const isUnixPath = p.startsWith('/') &&
|
||||
!p.match(/^\/mnt\/[a-z]\//i) &&
|
||||
!p.match(/^\/[a-zA-Z]\//);
|
||||
|
||||
if (isUnixPath) {
|
||||
// For Unix paths, just normalize without converting to Windows format
|
||||
// Replace double slashes with single slashes and remove trailing slashes
|
||||
return p.replace(/\/+/g, '/').replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
// Convert WSL or Unix-style Windows paths to Windows format
|
||||
p = convertToWindowsPath(p);
|
||||
|
||||
// Handle double backslashes and ensure proper escaping
|
||||
p = p.replace(/\\\\/g, '\\');
|
||||
|
||||
// Use Node's path normalization, which handles . and .. segments
|
||||
const normalized = path.normalize(p);
|
||||
|
||||
// Handle Windows paths: convert slashes and ensure drive letter is capitalized
|
||||
if (normalized.match(/^[a-zA-Z]:|^\/mnt\/[a-z]\/|^\/[a-z]\//i)) {
|
||||
let result = normalized.replace(/\//g, '\\');
|
||||
// Capitalize drive letter if present
|
||||
if (/^[a-z]:/.test(result)) {
|
||||
result = result.charAt(0).toUpperCase() + result.slice(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Leave other paths unchanged
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands home directory tildes in paths
|
||||
* @param filepath The path to expand
|
||||
* @returns Expanded path
|
||||
*/
|
||||
export function expandHome(filepath: string): string {
|
||||
if (filepath.startsWith('~/') || filepath === '~') {
|
||||
return path.join(os.homedir(), filepath.slice(1));
|
||||
}
|
||||
return filepath;
|
||||
}
|
||||
Reference in New Issue
Block a user