Filesystem MCP Server
by bsmi021
import { promises as fs } from 'fs';
import path from 'path';
import * as metadata from '../../operations/metadata.js';
import {
TEST_DIR,
createTestFile,
createTestDir
} from '../setup.js';
describe('Metadata Operations', () => {
describe('getExtendedMetadata', () => {
it('should get extended file metadata', async () => {
const content = 'test content';
const filePath = await createTestFile('test.txt', content);
const meta = await metadata.getExtendedMetadata(filePath);
expect(meta.name).toBe('test.txt');
expect(meta.extension).toBe('.txt');
expect(meta.mimeType).toBe('text/plain');
expect(meta.size).toBe(content.length);
expect(meta.isSymlink).toBe(false);
expect(meta.isHidden).toBe(false);
expect(meta.parentDir).toBe(TEST_DIR);
expect(meta.absolutePath).toBe(path.resolve(filePath));
expect(meta.relativePath).toBe(path.relative(process.cwd(), filePath));
});
it('should detect hidden files', async () => {
const filePath = await createTestFile('.hidden-file', 'content');
const meta = await metadata.getExtendedMetadata(filePath);
expect(meta.isHidden).toBe(true);
});
it('should handle different file types', async () => {
const textFile = await createTestFile('text.txt', 'text content');
const jsonFile = await createTestFile('data.json', '{"key": "value"}');
const textMeta = await metadata.getExtendedMetadata(textFile);
const jsonMeta = await metadata.getExtendedMetadata(jsonFile);
expect(textMeta.extension).toBe('.txt');
expect(textMeta.mimeType).toBe('text/plain');
expect(jsonMeta.extension).toBe('.json');
expect(jsonMeta.mimeType).toBe('application/json');
});
});
describe('compareFiles', () => {
it('should compare identical files', async () => {
const content = 'test content';
const file1 = await createTestFile('file1.txt', content);
const file2 = await createTestFile('file2.txt', content);
const result = await metadata.compareFiles(file1, file2);
expect(result.areIdentical).toBe(true);
expect(result.differences).toEqual({});
});
it('should detect content differences', async () => {
const file1 = await createTestFile('file1.txt', 'content 1');
const file2 = await createTestFile('file2.txt', 'content 2');
const result = await metadata.compareFiles(file1, file2);
expect(result.areIdentical).toBe(false);
expect(result.differences.content).toBe(true);
});
it('should detect size differences', async () => {
const file1 = await createTestFile('file1.txt', 'short');
const file2 = await createTestFile('file2.txt', 'much longer content');
const result = await metadata.compareFiles(file1, file2);
expect(result.areIdentical).toBe(false);
expect(result.differences.size).toBe(true);
});
it('should handle non-existent files', async () => {
const file1 = await createTestFile('file1.txt', 'content');
const nonexistent = path.join(TEST_DIR, 'nonexistent.txt');
await expect(metadata.compareFiles(file1, nonexistent))
.rejects
.toThrow();
});
});
describe('createSymlink', () => {
it('should create symbolic link', async () => {
const targetPath = await createTestFile('target.txt', 'content');
const linkPath = path.join(TEST_DIR, 'link.txt');
await metadata.createSymlink(targetPath, linkPath);
const meta = await metadata.getExtendedMetadata(linkPath);
expect(meta.isSymlink).toBe(true);
const content = await fs.readFile(linkPath, 'utf8');
expect(content).toBe('content');
});
it('should handle non-existent target', async () => {
const targetPath = path.join(TEST_DIR, 'nonexistent.txt');
const linkPath = path.join(TEST_DIR, 'link.txt');
await metadata.createSymlink(targetPath, linkPath);
const meta = await metadata.getExtendedMetadata(linkPath);
expect(meta.isSymlink).toBe(true);
});
});
describe('touchFile', () => {
it('should update timestamps of existing file', async () => {
const filePath = await createTestFile('test.txt', 'content');
const originalStats = await fs.stat(filePath);
// Wait a moment to ensure timestamps will be different
await new Promise(resolve => setTimeout(resolve, 1000));
await metadata.touchFile(filePath);
const newStats = await fs.stat(filePath);
expect(newStats.mtime.getTime()).toBeGreaterThan(originalStats.mtime.getTime());
expect(newStats.atime.getTime()).toBeGreaterThan(originalStats.atime.getTime());
});
it('should create new empty file if it doesn\'t exist', async () => {
const filePath = path.join(TEST_DIR, 'new-file.txt');
await metadata.touchFile(filePath);
const exists = await fs.access(filePath).then(() => true).catch(() => false);
expect(exists).toBe(true);
const content = await fs.readFile(filePath, 'utf8');
expect(content).toBe('');
});
});
describe('setFileTimestamps', () => {
it('should set specific timestamps', async () => {
const filePath = await createTestFile('test.txt', 'content');
const accessTime = new Date('2023-01-01');
const modifyTime = new Date('2023-01-02');
await metadata.setFileTimestamps(filePath, accessTime, modifyTime);
const stats = await fs.stat(filePath);
expect(stats.atime.toISOString()).toBe(accessTime.toISOString());
expect(stats.mtime.toISOString()).toBe(modifyTime.toISOString());
});
it('should use current time when timestamps not provided', async () => {
const filePath = await createTestFile('test.txt', 'content');
const beforeTime = new Date();
await metadata.setFileTimestamps(filePath);
const stats = await fs.stat(filePath);
expect(stats.atime.getTime()).toBeGreaterThanOrEqual(beforeTime.getTime());
expect(stats.mtime.getTime()).toBeGreaterThanOrEqual(beforeTime.getTime());
});
it('should handle non-existent file', async () => {
const filePath = path.join(TEST_DIR, 'nonexistent.txt');
await expect(metadata.setFileTimestamps(filePath))
.rejects
.toThrow();
});
});
});