Adds vitest 3.x (pinned to ^3 because vitest 4 requires Vite 6, while the web app pins Vite 5). Tests live under src/**/__tests__/**. Three target functions: - sanitizeFolderName (project_bootstrap.ts): 8 cases covering happy path, path-traversal stripping, empty-after-sanitize, control chars, truncation at 64, null bytes, leading/trailing dot/slash stripping. - resolveProjectPath (projects.ts): 7 cases including symlink-escape via realpath, outside-whitelist rejection, nonexistent path, AND a flagged BEHAVIOR GAP: passing the whitelist path itself currently returns success rather than erroring out (function early-exits the scope check when real === whitelistReal). Test asserts current behavior with explicit comment flagging the spec violation — function NOT silently patched. Function made exportable for testing (single keyword change). - buildMessagesPayload (inference.ts): 8 cases for compact-marker logic (no marker, marker present, multiple compacts, tool-message position). tsconfig.json excludes __tests__ + *.test.ts from emit so dist/ stays clean. pnpm -C apps/server test => 23 passed in ~340ms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.9 KiB
TypeScript
48 lines
1.9 KiB
TypeScript
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);
|
|
});
|
|
});
|