Files
hermes-agent/tests-js/frontend-common.test.mjs

40 lines
1.8 KiB
JavaScript

import { expect, test, vi } from 'vitest'
// rmTree retries ENOTEMPTY from Finder/Spotlight dropping files into a
// directory mid-removal (#122803). Node's rmSync is the only seam that can
// reproduce that race deterministically, so replace it and keep the rest of
// node:fs real.
const { rmSync } = vi.hoisted(() => ({ rmSync: vi.fn() }))
vi.mock('node:fs', async (importOriginal) => {
const actual = await importOriginal()
return { ...actual, rmSync }
})
const enotempty = () => { throw Object.assign(new Error('directory not empty'), { code: 'ENOTEMPTY' }) }
test('rmTree retries ENOTEMPTY removals until the directory clears', async () => {
const { rmTree } = await import('../scripts/build/frontend-common.mjs')
rmSync.mockReset()
rmSync.mockImplementationOnce(enotempty).mockImplementationOnce(enotempty).mockImplementationOnce(() => {})
await expect(rmTree('/tmp/product-scratch')).resolves.toBeUndefined()
expect(rmSync).toHaveBeenCalledTimes(3)
})
test('rmTree stops retrying after a bounded number of attempts', async () => {
const { rmTree } = await import('../scripts/build/frontend-common.mjs')
rmSync.mockReset()
rmSync.mockImplementation(enotempty)
await expect(rmTree('/tmp/product-scratch')).rejects.toThrow('directory not empty')
expect(rmSync.mock.calls.length).toBeGreaterThanOrEqual(3)
expect(rmSync.mock.calls.length).toBeLessThanOrEqual(5)
})
test('rmTree does not retry unrelated failures like ENOENT or EPERM', async () => {
const { rmTree } = await import('../scripts/build/frontend-common.mjs')
rmSync.mockReset()
rmSync.mockImplementationOnce(() => { throw Object.assign(new Error('no such file'), { code: 'ENOENT' }) })
await expect(rmTree('/tmp/product-scratch')).rejects.toThrow('no such file')
expect(rmSync).toHaveBeenCalledTimes(1)
})