100 lines
3.5 KiB
TypeScript
100 lines
3.5 KiB
TypeScript
|
|
/**
|
||
|
|
* vfs.readdir()'s `recursive` option: typed since 7.30 but never read, so it
|
||
|
|
* silently behaved exactly like `recursive: false`. This pins the real,
|
||
|
|
* documented contract: a recursive listing returns every descendant (files
|
||
|
|
* AND directories, all depths) as paths RELATIVE TO THE QUERIED DIRECTORY —
|
||
|
|
* the same convention Node's `fs.readdir(dir, { recursive: true })` uses —
|
||
|
|
* for both the plain string-array form and the `withFileTypes` VFSDirent
|
||
|
|
* form (whose `name` carries that same relative path when recursive).
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
||
|
|
import { Brainy } from '../../src/brainy.js'
|
||
|
|
import type { VFSDirent } from '../../src/vfs/types.js'
|
||
|
|
|
||
|
|
describe('vfs.readdir() recursive option', () => {
|
||
|
|
let brain: Brainy
|
||
|
|
|
||
|
|
beforeEach(async () => {
|
||
|
|
brain = new Brainy({
|
||
|
|
requireSubtype: false,
|
||
|
|
storage: { type: 'memory' },
|
||
|
|
silent: true
|
||
|
|
})
|
||
|
|
await brain.init()
|
||
|
|
|
||
|
|
// Build:
|
||
|
|
// /a/b.txt
|
||
|
|
// /a/sub/c.txt
|
||
|
|
// /a/sub/deeper/d.txt
|
||
|
|
// /a/sub2/ (empty directory)
|
||
|
|
await brain.vfs.writeFile('/a/b.txt', 'B')
|
||
|
|
await brain.vfs.writeFile('/a/sub/c.txt', 'C')
|
||
|
|
await brain.vfs.writeFile('/a/sub/deeper/d.txt', 'D')
|
||
|
|
await brain.vfs.mkdir('/a/sub2', { recursive: true })
|
||
|
|
})
|
||
|
|
|
||
|
|
afterEach(async () => {
|
||
|
|
await brain.close()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('non-recursive (default) still returns only direct children, by basename', async () => {
|
||
|
|
const entries = await brain.vfs.readdir('/a') as string[]
|
||
|
|
expect([...entries].sort()).toEqual(['b.txt', 'sub', 'sub2'])
|
||
|
|
})
|
||
|
|
|
||
|
|
it('recursive: true returns every descendant as a path relative to the queried directory', async () => {
|
||
|
|
const entries = await brain.vfs.readdir('/a', { recursive: true }) as string[]
|
||
|
|
expect([...entries].sort()).toEqual([
|
||
|
|
'b.txt',
|
||
|
|
'sub',
|
||
|
|
'sub/c.txt',
|
||
|
|
'sub/deeper',
|
||
|
|
'sub/deeper/d.txt',
|
||
|
|
'sub2'
|
||
|
|
])
|
||
|
|
})
|
||
|
|
|
||
|
|
it('recursive: true at the root has no leading slash on relative entries', async () => {
|
||
|
|
const entries = await brain.vfs.readdir('/', { recursive: true }) as string[]
|
||
|
|
expect(entries).toContain('a')
|
||
|
|
expect(entries).toContain('a/b.txt')
|
||
|
|
expect(entries).toContain('a/sub/deeper/d.txt')
|
||
|
|
for (const entry of entries) {
|
||
|
|
expect(entry.startsWith('/')).toBe(false)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
it('recursive + withFileTypes: VFSDirent.name is the relative path, .path stays absolute', async () => {
|
||
|
|
const entries = await brain.vfs.readdir('/a', {
|
||
|
|
recursive: true,
|
||
|
|
withFileTypes: true
|
||
|
|
}) as VFSDirent[]
|
||
|
|
|
||
|
|
const byName = new Map(entries.map((e) => [e.name, e]))
|
||
|
|
|
||
|
|
const nested = byName.get('sub/deeper/d.txt')
|
||
|
|
expect(nested).toBeDefined()
|
||
|
|
expect(nested!.path).toBe('/a/sub/deeper/d.txt')
|
||
|
|
expect(nested!.type).toBe('file')
|
||
|
|
|
||
|
|
const nestedDir = byName.get('sub/deeper')
|
||
|
|
expect(nestedDir).toBeDefined()
|
||
|
|
expect(nestedDir!.path).toBe('/a/sub/deeper')
|
||
|
|
expect(nestedDir!.type).toBe('directory')
|
||
|
|
|
||
|
|
// Non-recursive VFSDirent behavior is unchanged: name is the basename.
|
||
|
|
const direct = await brain.vfs.readdir('/a', { withFileTypes: true }) as VFSDirent[]
|
||
|
|
const directEntry = direct.find((e) => e.path === '/a/b.txt')
|
||
|
|
expect(directEntry?.name).toBe('b.txt')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('recursive + filter composes: only files survive a type filter', async () => {
|
||
|
|
const entries = await brain.vfs.readdir('/a', {
|
||
|
|
recursive: true,
|
||
|
|
filter: { type: 'file' }
|
||
|
|
}) as string[]
|
||
|
|
expect([...entries].sort()).toEqual(['b.txt', 'sub/c.txt', 'sub/deeper/d.txt'])
|
||
|
|
})
|
||
|
|
})
|