import { describe, it, expect } from 'vitest'; import { sanitizeFolderName } from '../project_bootstrap.js'; describe('sanitizeFolderName', () => { it('passes through a normal slug-like name', () => { expect(sanitizeFolderName('my-project')).toBe('my-project'); }); it('lowercases and replaces whitespace with hyphens', () => { expect(sanitizeFolderName('Hello World')).toBe('hello-world'); }); it('strips path-traversal characters', () => { // dots and slashes fall outside [a-z0-9-] and are removed entirely. expect(sanitizeFolderName('../etc/passwd')).toBe('etcpasswd'); }); it('strips trailing and leading dots and slashes', () => { expect(sanitizeFolderName('./foo/')).toBe('foo'); }); it('collapses runs of hyphens and strips leading/trailing ones', () => { expect(sanitizeFolderName('---foo---')).toBe('foo'); }); it('returns empty string when nothing survives sanitization', () => { // NOTE: sanitizeFolderName itself does NOT throw — it returns ''. The // BootstrapNameError is raised by the caller (bootstrapProject) when the // sanitized result fails the SAFE_NAME regex. The spec's "throws" phrasing // refers to that caller-level validation, not this pure function. expect(sanitizeFolderName('...')).toBe(''); expect(sanitizeFolderName(' ')).toBe(''); }); it('strips control characters and null bytes', () => { // Null bytes and control characters are not in [a-z0-9-] so they're // filtered out (effectively rejected as folder-name content). expect(sanitizeFolderName('my\x00proj\x01')).toBe('myproj'); expect(sanitizeFolderName('foo\x00bar')).toBe('foobar'); }); it('truncates names longer than 64 characters', () => { const long = 'a'.repeat(100); expect(sanitizeFolderName(long)).toBe('a'.repeat(64)); expect(sanitizeFolderName(long)).toHaveLength(64); }); });