mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const { openMock } = vi.hoisted(() => ({ openMock: vi.fn() }))
|
|
|
|
vi.mock('node:fs/promises', () => ({ open: openMock }))
|
|
|
|
import { NodeFileReadTooLargeError, readNodeFileWithinLimit } from './node-bounded-file-reader'
|
|
|
|
type FileHandleOptions = {
|
|
content: Buffer
|
|
initialSize?: number
|
|
readError?: Error
|
|
statError?: Error
|
|
}
|
|
|
|
function createFileHandle({ content, initialSize, readError, statError }: FileHandleOptions) {
|
|
const close = vi.fn().mockResolvedValue(undefined)
|
|
const read = vi.fn(async (target: Buffer, offset: number, length: number, position: number) => {
|
|
if (readError) {
|
|
throw readError
|
|
}
|
|
const bytesRead = Math.min(length, Math.max(0, content.byteLength - position))
|
|
content.copy(target, offset, position, position + bytesRead)
|
|
return { bytesRead, buffer: target }
|
|
})
|
|
const stat = statError
|
|
? vi.fn().mockRejectedValue(statError)
|
|
: vi.fn().mockResolvedValue({ size: initialSize ?? content.byteLength })
|
|
return { close, read, stat }
|
|
}
|
|
|
|
beforeEach(() => openMock.mockReset())
|
|
|
|
describe('readNodeFileWithinLimit', () => {
|
|
it('reads a stable file and closes its descriptor', async () => {
|
|
const handle = createFileHandle({ content: Buffer.from('stable') })
|
|
openMock.mockResolvedValue(handle)
|
|
|
|
const result = await readNodeFileWithinLimit('/workspace/stable.txt', 64)
|
|
|
|
expect(result.buffer).toEqual(Buffer.from('stable'))
|
|
expect(result.stats.size).toBe(6)
|
|
expect(handle.close).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('rejects an oversized initial size before reading file bytes', async () => {
|
|
const handle = createFileHandle({ content: Buffer.alloc(0), initialSize: 65 })
|
|
openMock.mockResolvedValue(handle)
|
|
|
|
await expect(readNodeFileWithinLimit('/workspace/oversized.txt', 64)).rejects.toEqual(
|
|
new NodeFileReadTooLargeError(65, 64)
|
|
)
|
|
expect(handle.read).not.toHaveBeenCalled()
|
|
expect(handle.close).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('includes growth that remains within the read limit', async () => {
|
|
const handle = createFileHandle({ content: Buffer.from('grown'), initialSize: 3 })
|
|
openMock.mockResolvedValue(handle)
|
|
|
|
const result = await readNodeFileWithinLimit('/workspace/growing.txt', 5)
|
|
|
|
expect(result.buffer).toEqual(Buffer.from('grown'))
|
|
expect(handle.close).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('reads proc-style content whose reported file size is zero', async () => {
|
|
const handle = createFileHandle({ content: Buffer.from('proc table'), initialSize: 0 })
|
|
openMock.mockResolvedValue(handle)
|
|
|
|
const result = await readNodeFileWithinLimit('/proc/net/tcp', 64)
|
|
|
|
expect(result.buffer).toEqual(Buffer.from('proc table'))
|
|
expect(result.stats.size).toBe(0)
|
|
expect(handle.close).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('rejects growth beyond the limit without allocating the oversized content', async () => {
|
|
const handle = createFileHandle({ content: Buffer.from('growth'), initialSize: 3 })
|
|
openMock.mockResolvedValue(handle)
|
|
|
|
await expect(readNodeFileWithinLimit('/workspace/growing.txt', 5)).rejects.toEqual(
|
|
new NodeFileReadTooLargeError(6, 5)
|
|
)
|
|
expect(handle.close).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it.each([
|
|
{ label: 'stat', options: { content: Buffer.alloc(0), statError: new Error('stat failed') } },
|
|
{
|
|
label: 'read',
|
|
options: { content: Buffer.from('data'), readError: new Error('read failed') }
|
|
}
|
|
])('closes its descriptor when $label fails', async ({ options }) => {
|
|
const handle = createFileHandle(options)
|
|
openMock.mockResolvedValue(handle)
|
|
|
|
await expect(readNodeFileWithinLimit('/workspace/failing.txt', 64)).rejects.toThrow(
|
|
`${options.statError ? 'stat' : 'read'} failed`
|
|
)
|
|
expect(handle.close).toHaveBeenCalledOnce()
|
|
})
|
|
})
|