Download src/server/__tests__/filesystem.test.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 10.9 kB
-
https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/__tests__/filesystem.test.ts
- Command line
-
hf download hf://chenbhao/codev@afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/__tests__/filesystem.test.ts
-
curl -L -o filesystem.test.ts https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/__tests__/filesystem.test.ts
10.9 kB
| import { afterEach, describe, expect, it } from 'bun:test' | |
| import { execFileSync } from 'child_process' | |
| import * as fs from 'fs' | |
| import * as fsp from 'fs/promises' | |
| import * as os from 'os' | |
| import * as path from 'path' | |
| import { handleFilesystemRoute } from '../api/filesystem.js' | |
| import { clearFilesystemAccessRootsForTests } from '../services/filesystemAccessRoots.js' | |
| import { getRepositoryContext } from '../services/repositoryLaunchService.js' | |
| const cleanupDirs = new Set<string>() | |
| function makeUrl(route: string, params: Record<string, string>): URL { | |
| const url = new URL(`http://localhost${route}`) | |
| for (const [key, value] of Object.entries(params)) { | |
| url.searchParams.set(key, value) | |
| } | |
| return url | |
| } | |
| afterEach(async () => { | |
| for (const dir of cleanupDirs) { | |
| await fsp.rm(dir, { recursive: true, force: true }) | |
| } | |
| cleanupDirs.clear() | |
| clearFilesystemAccessRootsForTests() | |
| }) | |
| function git(cwd: string, ...args: string[]): string { | |
| return execFileSync('git', args, { | |
| cwd, | |
| encoding: 'utf8', | |
| }) | |
| } | |
| function isWithinPath(targetPath: string, rootPath: string): boolean { | |
| const target = path.resolve(targetPath) | |
| const root = path.resolve(rootPath) | |
| return target === root || target.startsWith(`${root}${path.sep}`) | |
| } | |
| async function makeExternalFixtureDir(): Promise<string | null> { | |
| const candidates = ['/var/tmp', '/private/var/tmp', '/Users/Shared'] | |
| for (const baseDir of candidates) { | |
| try { | |
| const stat = await fsp.stat(baseDir) | |
| if (!stat.isDirectory()) continue | |
| const fixtureDir = await fsp.mkdtemp(path.join(baseDir, 'claude-filesystem-test-')) | |
| const isDefaultAllowed = | |
| isWithinPath(fixtureDir, os.homedir()) || | |
| isWithinPath(fixtureDir, '/tmp') || | |
| (process.platform === 'darwin' && isWithinPath(fixtureDir, '/private/tmp')) | |
| if (!isDefaultAllowed) return fixtureDir | |
| await fsp.rm(fixtureDir, { recursive: true, force: true }) | |
| } catch { | |
| // Try the next common writable system directory. | |
| } | |
| } | |
| return null | |
| } | |
| describe('filesystem API', () => { | |
| it('allows browsing a directory under the user home directory', async () => { | |
| const homeFixtureDir = await fsp.mkdtemp(path.join(os.homedir(), 'claude-filesystem-test-')) | |
| cleanupDirs.add(homeFixtureDir) | |
| await fsp.writeFile(path.join(homeFixtureDir, 'note.txt'), 'hello') | |
| const res = await handleFilesystemRoute( | |
| '/api/filesystem/browse', | |
| makeUrl('/api/filesystem/browse', { | |
| path: homeFixtureDir, | |
| includeFiles: 'true', | |
| }), | |
| ) | |
| expect(res.status).toBe(200) | |
| const body = await res.json() as { entries: Array<{ name: string }> } | |
| expect(body.entries.some((entry) => entry.name === 'note.txt')).toBe(true) | |
| }) | |
| it('allows browsing a selected workspace outside the default home/tmp roots', async () => { | |
| const externalFixtureDir = await makeExternalFixtureDir() | |
| if (!externalFixtureDir) return | |
| cleanupDirs.add(externalFixtureDir) | |
| await fsp.writeFile(path.join(externalFixtureDir, 'note.txt'), 'hello') | |
| const deniedRes = await handleFilesystemRoute( | |
| '/api/filesystem/browse', | |
| makeUrl('/api/filesystem/browse', { | |
| path: externalFixtureDir, | |
| includeFiles: 'true', | |
| }), | |
| ) | |
| expect(deniedRes.status).toBe(403) | |
| await getRepositoryContext(externalFixtureDir) | |
| const res = await handleFilesystemRoute( | |
| '/api/filesystem/browse', | |
| makeUrl('/api/filesystem/browse', { | |
| path: externalFixtureDir, | |
| includeFiles: 'true', | |
| }), | |
| ) | |
| expect(res.status).toBe(200) | |
| const body = await res.json() as { entries: Array<{ name: string }> } | |
| expect(body.entries.some((entry) => entry.name === 'note.txt')).toBe(true) | |
| }) | |
| it('fuzzy searches files and directories below the selected root', async () => { | |
| const homeFixtureDir = await fsp.mkdtemp(path.join(os.homedir(), 'claude-filesystem-test-')) | |
| cleanupDirs.add(homeFixtureDir) | |
| git(homeFixtureDir, 'init') | |
| await fsp.mkdir(path.join(homeFixtureDir, 'src', 'commands'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'src', 'commands', 'files'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'src', 'constants'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'src', 'hooks'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'desktop', 'src'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'scripts', 'quality-gate', 'baseline', 'fixtures', 'cross-module-refactor', 'src'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, '__pycache__'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'node_modules', 'pkg'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, '.venv', 'lib'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'tmp-ignore'), { recursive: true }) | |
| await fsp.writeFile(path.join(homeFixtureDir, '.gitignore'), ['__pycache__/', 'node_modules/', '.venv/', 'venv/'].join('\n')) | |
| await fsp.writeFile(path.join(homeFixtureDir, '.ignore'), 'tmp-ignore/') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'src', 'commands', 'files.ts'), 'export {}') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'src', 'commands', 'files', 'index.ts'), 'export {}') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'src', 'constants', 'fileSearch.ts'), 'export {}') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'src', 'hooks', 'useFileSearch.ts'), 'export {}') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'desktop', 'src', 'main.ts'), 'export {}') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'scripts', 'quality-gate', 'baseline', 'fixtures', 'cross-module-refactor', 'src', 'index.ts'), 'export {}') | |
| await fsp.writeFile(path.join(homeFixtureDir, '__pycache__', 'fileSearch.cpython-311.pyc'), '') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'node_modules', 'pkg', 'files.js'), '') | |
| await fsp.writeFile(path.join(homeFixtureDir, '.venv', 'lib', 'files.py'), '') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'tmp-ignore', 'files.tmp'), '') | |
| const res = await handleFilesystemRoute( | |
| '/api/filesystem/browse', | |
| makeUrl('/api/filesystem/browse', { | |
| path: homeFixtureDir, | |
| search: 'files', | |
| includeFiles: 'true', | |
| }), | |
| ) | |
| expect(res.status).toBe(200) | |
| const body = await res.json() as { entries: Array<{ name: string; relativePath?: string; isDirectory: boolean }> } | |
| expect(body.entries).toEqual(expect.arrayContaining([ | |
| expect.objectContaining({ | |
| name: 'files.ts', | |
| relativePath: 'src/commands/files.ts', | |
| isDirectory: false, | |
| }), | |
| ])) | |
| expect(body.entries.some((entry) => entry.relativePath === 'src/constants/fileSearch.ts')).toBe(true) | |
| expect(body.entries.find((entry) => entry.relativePath === 'src/commands/files')?.isDirectory).toBe(true) | |
| expect(body.entries.some((entry) => entry.relativePath === '__pycache__/fileSearch.cpython-311.pyc')).toBe(false) | |
| expect(body.entries.some((entry) => entry.relativePath === 'node_modules/pkg/files.js')).toBe(false) | |
| expect(body.entries.some((entry) => entry.relativePath === '.venv/lib/files.py')).toBe(false) | |
| expect(body.entries.some((entry) => entry.relativePath === 'tmp-ignore/files.tmp')).toBe(false) | |
| const srcRes = await handleFilesystemRoute( | |
| '/api/filesystem/browse', | |
| makeUrl('/api/filesystem/browse', { | |
| path: homeFixtureDir, | |
| search: 'src', | |
| includeFiles: 'true', | |
| }), | |
| ) | |
| expect(srcRes.status).toBe(200) | |
| const srcBody = await srcRes.json() as { entries: Array<{ relativePath?: string }> } | |
| const srcPaths = srcBody.entries.map(entry => entry.relativePath) | |
| expect(srcPaths[0]).toBe('src') | |
| expect(srcPaths.indexOf('src/hooks')).toBeGreaterThan(-1) | |
| expect(srcPaths.indexOf('desktop/src')).toBeGreaterThan(-1) | |
| expect(srcPaths.indexOf('scripts/quality-gate/baseline/fixtures/cross-module-refactor/src')).toBeGreaterThan(-1) | |
| expect(srcPaths.indexOf('src/hooks')).toBeLessThan(srcPaths.indexOf('desktop/src')) | |
| expect(srcPaths.indexOf('src/hooks')).toBeLessThan(srcPaths.indexOf('scripts/quality-gate/baseline/fixtures/cross-module-refactor/src')) | |
| }) | |
| it('falls back to ripgrep search outside git and still respects ignore files', async () => { | |
| const homeFixtureDir = await fsp.mkdtemp(path.join(os.homedir(), 'claude-filesystem-test-')) | |
| cleanupDirs.add(homeFixtureDir) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'app'), { recursive: true }) | |
| await fsp.mkdir(path.join(homeFixtureDir, 'node_modules', 'pkg'), { recursive: true }) | |
| await fsp.writeFile(path.join(homeFixtureDir, '.gitignore'), 'node_modules/') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'app', 'cache-result.ts'), 'export {}') | |
| await fsp.writeFile(path.join(homeFixtureDir, 'node_modules', 'pkg', 'cache-result.js'), '') | |
| const res = await handleFilesystemRoute( | |
| '/api/filesystem/browse', | |
| makeUrl('/api/filesystem/browse', { | |
| path: homeFixtureDir, | |
| search: 'cache', | |
| includeFiles: 'true', | |
| }), | |
| ) | |
| expect(res.status).toBe(200) | |
| const body = await res.json() as { entries: Array<{ relativePath?: string; isDirectory: boolean }> } | |
| expect(body.entries).toEqual(expect.arrayContaining([ | |
| expect.objectContaining({ | |
| relativePath: 'app/cache-result.ts', | |
| isDirectory: false, | |
| }), | |
| ])) | |
| expect(body.entries.some((entry) => entry.relativePath === 'node_modules/pkg/cache-result.js')).toBe(false) | |
| }) | |
| it('accepts /private/tmp aliases on macOS for browsing and file serving', async () => { | |
| if (process.platform !== 'darwin') return | |
| const tmpFixtureDir = await fsp.mkdtemp('/tmp/claude-filesystem-test-') | |
| cleanupDirs.add(tmpFixtureDir) | |
| const canonicalTmpDir = fs.realpathSync(tmpFixtureDir) | |
| const imagePath = path.join(canonicalTmpDir, 'preview.png') | |
| await fsp.writeFile( | |
| imagePath, | |
| Buffer.from('89504e470d0a1a0a0000000d4948445200000001000000010802000000907753de0000000c49444154789c63606060000000040001f61738550000000049454e44ae426082', 'hex'), | |
| ) | |
| const browseRes = await handleFilesystemRoute( | |
| '/api/filesystem/browse', | |
| makeUrl('/api/filesystem/browse', { | |
| path: canonicalTmpDir, | |
| includeFiles: 'true', | |
| }), | |
| ) | |
| expect(browseRes.status).toBe(200) | |
| const browseBody = await browseRes.json() as { entries: Array<{ name: string }> } | |
| expect(browseBody.entries.some((entry) => entry.name === 'preview.png')).toBe(true) | |
| const fileRes = await handleFilesystemRoute( | |
| '/api/filesystem/file', | |
| makeUrl('/api/filesystem/file', { | |
| path: imagePath, | |
| }), | |
| ) | |
| expect(fileRes.status).toBe(200) | |
| expect(fileRes.headers.get('Content-Type')).toBe('image/png') | |
| }) | |
| }) | |