diff --git a/src/main/services/editor/ProjectFileService.ts b/src/main/services/editor/ProjectFileService.ts index 7ac0a2ee..bfc2c33a 100644 --- a/src/main/services/editor/ProjectFileService.ts +++ b/src/main/services/editor/ProjectFileService.ts @@ -505,7 +505,7 @@ export class ProjectFileService { const newPath = path.join(normalizedDest, path.basename(normalizedSrc)); // 8. Prevent parent → child move (moving dir into itself) - if (normalizedDest.startsWith(normalizedSrc + '/') || normalizedDest === normalizedSrc) { + if (normalizedDest.startsWith(normalizedSrc + path.sep) || normalizedDest === normalizedSrc) { throw new Error('Cannot move a directory into itself'); } diff --git a/test/main/services/editor/FileSearchService.test.ts b/test/main/services/editor/FileSearchService.test.ts index bbe40f34..f9fa4d45 100644 --- a/test/main/services/editor/FileSearchService.test.ts +++ b/test/main/services/editor/FileSearchService.test.ts @@ -29,7 +29,7 @@ import { isBinaryFile } from 'isbinaryfile'; import { FileSearchService } from '@main/services/editor/FileSearchService'; -const PROJECT_ROOT = '/test/project'; +const PROJECT_ROOT = path.resolve('/test/project'); describe('FileSearchService', () => { let service: FileSearchService; @@ -40,6 +40,12 @@ describe('FileSearchService', () => { }); function mockFileSystem(files: Record) { + // Normalize keys so lookups work on Windows (backslash vs forward slash) + const normalizedFiles: Record = {}; + for (const [key, value] of Object.entries(files)) { + normalizedFiles[path.normalize(key)] = value; + } + const entries = Object.keys(files).map((filePath) => { const name = path.basename(filePath); return { name, isFile: () => true, isDirectory: () => false }; @@ -49,15 +55,15 @@ describe('FileSearchService', () => { vi.mocked(isBinaryFile).mockResolvedValue(false); vi.mocked(fs.stat).mockImplementation(async (filePath: unknown) => { - const p = String(filePath); - const content = files[p]; + const p = path.normalize(String(filePath)); + const content = normalizedFiles[p]; if (content === undefined) throw new Error('ENOENT'); return { size: content.length } as never; }); vi.mocked(fs.readFile).mockImplementation(async (filePath: unknown) => { - const p = String(filePath); - const content = files[p]; + const p = path.normalize(String(filePath)); + const content = normalizedFiles[p]; if (content === undefined) throw new Error('ENOENT'); return content as never; }); diff --git a/test/main/services/editor/ProjectFileService.test.ts b/test/main/services/editor/ProjectFileService.test.ts index 73da1892..42843527 100644 --- a/test/main/services/editor/ProjectFileService.test.ts +++ b/test/main/services/editor/ProjectFileService.test.ts @@ -55,7 +55,7 @@ import { ProjectFileService } from '../../../../src/main/services/editor/Project // Setup // ============================================================================= -const PROJECT_ROOT = '/Users/test/my-project'; +const PROJECT_ROOT = path.resolve('/Users/test/my-project'); let service: ProjectFileService; const mockLstat = vi.mocked(fs.lstat); @@ -219,7 +219,7 @@ describe('ProjectFileService.readDir', () => { mockRealpath.mockImplementation(async (p) => { const name = path.basename(String(p)); - if (name === 'safe-link') return PROJECT_ROOT + '/actual-dir'; + if (name === 'safe-link') return path.join(PROJECT_ROOT, 'actual-dir'); return '/etc/shadow'; // escapes project }); @@ -559,8 +559,8 @@ describe('ProjectFileService.deleteFile', () => { // ============================================================================= describe('ProjectFileService.moveFile', () => { - const SRC_DIR = PROJECT_ROOT + '/src'; - const DEST_DIR = PROJECT_ROOT + '/lib'; + const SRC_DIR = path.join(PROJECT_ROOT, 'src'); + const DEST_DIR = path.join(PROJECT_ROOT, 'lib'); beforeEach(() => { mockRename.mockResolvedValue(undefined);