54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
createSafeVirtualFileName,
|
|
safeFileName,
|
|
safeOutputFileName,
|
|
splitFileName,
|
|
} from '../../../src/media/safe-file-name';
|
|
|
|
describe('safe filenames', () => {
|
|
it('removes path traversal, control characters and shell-like punctuation', () => {
|
|
expect(safeFileName('../../My $ video\u0000 (final).MP4')).toBe(
|
|
'My-video-final.mp4'
|
|
);
|
|
expect(safeFileName('..\\..\\evil.webm')).toBe('evil.webm');
|
|
expect(safeFileName('../')).toBe('file');
|
|
});
|
|
|
|
it('handles Unicode, hidden names and reserved device names', () => {
|
|
expect(safeFileName('Crème brûlée.mov')).toBe('Creme-brulee.mov');
|
|
expect(safeFileName('.env')).toBe('env');
|
|
expect(safeFileName('CON.mp4')).toBe('_CON.mp4');
|
|
});
|
|
|
|
it('preserves a short extension while enforcing the requested maximum', () => {
|
|
const result = safeFileName(`${'a'.repeat(200)}.mp4`, {
|
|
maxLength: 32,
|
|
});
|
|
expect(result).toHaveLength(32);
|
|
expect(result.endsWith('.mp4')).toBe(true);
|
|
});
|
|
|
|
it('creates generated virtual names without leaking the original stem', () => {
|
|
expect(createSafeVirtualFileName('private-holiday.mov', 3)).toBe(
|
|
'source-3.mov'
|
|
);
|
|
expect(() => createSafeVirtualFileName('x.mp4', -1)).toThrow();
|
|
});
|
|
|
|
it('creates deterministic download names and splits compound extensions', () => {
|
|
expect(safeOutputFileName('My export (1)', '.WEBM')).toBe(
|
|
'My-export-1.webm'
|
|
);
|
|
expect(safeOutputFileName('Project', 'avproject.json')).toBe(
|
|
'Project.avproject.json'
|
|
);
|
|
expect(safeOutputFileName('Project.v2', 'mp4')).toBe('Project.v2.mp4');
|
|
expect(splitFileName('archive.tar.gz')).toEqual({
|
|
stem: 'archive.tar',
|
|
extension: '.gz',
|
|
});
|
|
});
|
|
});
|