import { describe, expect, it } from 'vitest'; import { ValidationError } from '../src/errors.js'; import { parseRelativePath } from '../src/lib/paths.js'; describe('parseRelativePath', () => { it('sépare dossiers et nom de fichier', () => { expect(parseRelativePath('vacances/plage/img 001.jpg')).toEqual({ segments: ['vacances', 'plage'], fileName: 'img 001.jpg', }); }); it('accepte un simple nom de fichier (aucun sous-dossier)', () => { expect(parseRelativePath('photo.png')).toEqual({ segments: [], fileName: 'photo.png' }); }); it('accepte les séparateurs `/` et `\\` et les segments accentués', () => { expect(parseRelativePath('Été 2024\\sous-dossier/clé.mp4')).toEqual({ segments: ['Été 2024', 'sous-dossier'], fileName: 'clé.mp4', }); }); it('neutralise `.` et `..` (aucune remontée possible)', () => { expect(parseRelativePath('../../etc/passwd')).toEqual({ segments: ['etc'], fileName: 'passwd', }); expect(parseRelativePath('a/./b/../c/d.txt')).toEqual({ segments: ['a', 'b', 'c'], fileName: 'd.txt', }); }); it('retire les caractères de contrôle et remplace les caractères hostiles', () => { const parsed = parseRelativePath(`a${String.fromCharCode(1)}b/fi:le?.txt`); expect(parsed.segments).toEqual(['ab']); expect(parsed.fileName).toBe('fi_le_.txt'); }); it('rejette un chemin vide après nettoyage', () => { expect(() => parseRelativePath('../..')).toThrow(ValidationError); expect(() => parseRelativePath('///')).toThrow(ValidationError); }); it('rejette une arborescence trop profonde ou trop longue', () => { expect(() => parseRelativePath(Array(40).fill('d').join('/') + '/f.txt')).toThrow( ValidationError, ); expect(() => parseRelativePath('a/'.repeat(3000) + 'f.txt')).toThrow(ValidationError); }); });