create_file.test.ts•9.22 kB
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as fs from 'fs';
import * as path from 'path';
import { TEST_DIR, client } from './setup.js';
describe('create_file tool', () => {
// Test files that will be created during tests
const testFiles = {
simple: path.join(TEST_DIR, 'test-simple.txt'),
nested: path.join(TEST_DIR, 'nested', 'deep', 'test-nested.txt'),
existing: path.join(TEST_DIR, 'test-existing.txt'),
empty: path.join(TEST_DIR, 'test-empty.txt'),
specialChars: path.join(TEST_DIR, 'test-special-chars.txt')
};
// Clean up any test files before and after each test
beforeEach(() => {
// Remove any test files that might exist from previous failed tests
Object.values(testFiles).forEach(filePath => {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
});
// Remove nested directory structure
const nestedDir = path.dirname(testFiles.nested);
if (fs.existsSync(nestedDir)) {
fs.rmSync(nestedDir, { recursive: true, force: true });
}
});
afterEach(() => {
// Clean up test files after each test
Object.values(testFiles).forEach(filePath => {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
});
// Remove nested directory structure
const nestedDir = path.dirname(testFiles.nested);
if (fs.existsSync(nestedDir)) {
fs.rmSync(nestedDir, { recursive: true, force: true });
}
});
describe('Success cases', () => {
it('should create a new file with specified content', async () => {
const content = 'Hello, World!\nThis is a test file.';
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.simple,
contents: content
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created with correct content
expect(fs.existsSync(testFiles.simple)).toBe(true);
const fileContent = fs.readFileSync(testFiles.simple, 'utf-8');
expect(fileContent).toBe(content);
});
it('should create nested directories and file', async () => {
const content = 'File in nested directory';
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.nested,
contents: content
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created
expect(fs.existsSync(testFiles.nested)).toBe(true);
const fileContent = fs.readFileSync(testFiles.nested, 'utf-8');
expect(fileContent).toBe(content);
// Verify the nested directory structure was created
const nestedDir = path.dirname(testFiles.nested);
expect(fs.existsSync(nestedDir)).toBe(true);
});
it('should create an empty file', async () => {
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.empty,
contents: ''
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created and is empty
expect(fs.existsSync(testFiles.empty)).toBe(true);
const fileContent = fs.readFileSync(testFiles.empty, 'utf-8');
expect(fileContent).toBe('');
});
it('should create file with special characters and unicode', async () => {
const content = 'Special chars: !@#$%^&*()\nUnicode: café résumé naïve 🚀\nNewlines\nand\ttabs';
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.specialChars,
contents: content
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created with correct content
expect(fs.existsSync(testFiles.specialChars)).toBe(true);
const fileContent = fs.readFileSync(testFiles.specialChars, 'utf-8');
expect(fileContent).toBe(content);
});
it('should handle relative paths correctly', async () => {
const relativePath = path.relative(process.cwd(), testFiles.simple);
const content = 'File created with relative path';
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: relativePath,
contents: content
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created
expect(fs.existsSync(testFiles.simple)).toBe(true);
const fileContent = fs.readFileSync(testFiles.simple, 'utf-8');
expect(fileContent).toBe(content);
});
});
describe('Error cases', () => {
it('should fail when file already exists', async () => {
// Create a file first
fs.writeFileSync(testFiles.existing, 'Existing content');
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.existing,
contents: 'New content'
}
});
expect(result.isError).toBe(true);
expect((result.content as any)[0].text).toMatch(/File already exists/);
expect((result.content as any)[0].text).toMatch(/does not overwrite existing files/);
// Verify the original file content was not changed
const originalContent = fs.readFileSync(testFiles.existing, 'utf-8');
expect(originalContent).toBe('Existing content');
});
it('should fail when a directory exists at the target path', async () => {
// Create a directory at the target path
fs.mkdirSync(testFiles.existing, { recursive: true });
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.existing,
contents: 'Content'
}
});
expect(result.isError).toBe(true);
expect((result.content as any)[0].text).toMatch(/A directory already exists/);
expect((result.content as any)[0].text).toMatch(/Cannot create a file at this location/);
});
it('should reject relative paths that go outside the project', async () => {
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: '../../../etc/passwd',
contents: 'Should not work'
}
});
expect(result.isError).toBe(true);
expect((result.content as any)[0].text).toMatch(/must be an absolute path/);
});
});
describe('Edge cases', () => {
it('should handle very long file paths', async () => {
const longPath = path.join(TEST_DIR, 'a'.repeat(100), 'b'.repeat(100), 'c'.repeat(50) + '.txt');
const content = 'File with long path';
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: longPath,
contents: content
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created
expect(fs.existsSync(longPath)).toBe(true);
const fileContent = fs.readFileSync(longPath, 'utf-8');
expect(fileContent).toBe(content);
// Clean up the long path structure
const longDir = path.dirname(longPath);
fs.rmSync(path.dirname(longDir), { recursive: true, force: true });
});
it('should handle file with only newlines', async () => {
const content = '\n\n\n\n';
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.simple,
contents: content
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created with correct content
expect(fs.existsSync(testFiles.simple)).toBe(true);
const fileContent = fs.readFileSync(testFiles.simple, 'utf-8');
expect(fileContent).toBe(content);
});
it('should handle file with whitespace only', async () => {
const content = ' \t\t \n \t\t ';
const result = await client.callTool({
name: 'create_file',
arguments: {
file_path: testFiles.simple,
contents: content
}
});
expect(result.isError).toBe(false);
expect((result.content as any)[0].text).toMatch(/Successfully created file/);
// Verify the file was created with correct content
expect(fs.existsSync(testFiles.simple)).toBe(true);
const fileContent = fs.readFileSync(testFiles.simple, 'utf-8');
expect(fileContent).toBe(content);
});
});
});