Skip to content

Commit 31b4bb6

Browse files
committed
test(dlx/packages): cover EACCES/EPERM/EROFS/generic error paths
1 parent 427f3e2 commit 31b4bb6

1 file changed

Lines changed: 83 additions & 1 deletion

File tree

test/unit/dlx/packages.test.mts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { existsSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs'
1010
import { tmpdir } from 'node:os'
1111
import path from 'node:path'
1212

13-
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
13+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
1414

1515
import {
1616
isDlxPackageInstalled,
@@ -127,5 +127,87 @@ describe.sequential('dlx/packages', () => {
127127
it('does not throw when removing a non-existent package', () => {
128128
expect(() => removeDlxPackageSync('does-not-exist')).not.toThrow()
129129
})
130+
131+
it('throws a permission-denied error when safeDeleteSync fails with EACCES', async () => {
132+
// Spy on safeDeleteSync to throw a synthetic EACCES.
133+
const fsModule = await import('@socketsecurity/lib/fs')
134+
const originalSafeDelete = fsModule.safeDeleteSync
135+
const err = new Error(
136+
'EACCES: permission denied',
137+
) as NodeJS.ErrnoException
138+
err.code = 'EACCES'
139+
const spy = vi
140+
.spyOn(fsModule, 'safeDeleteSync')
141+
.mockImplementation(() => {
142+
throw err
143+
})
144+
try {
145+
expect(() => removeDlxPackageSync('locked-pkg')).toThrow(
146+
/Permission denied removing DLX package/,
147+
)
148+
} finally {
149+
spy.mockRestore()
150+
// Sanity: restore is in place.
151+
expect(fsModule.safeDeleteSync).toBe(originalSafeDelete)
152+
}
153+
})
154+
155+
it('throws a permission-denied error when safeDeleteSync fails with EPERM', async () => {
156+
const fsModule = await import('@socketsecurity/lib/fs')
157+
const err = new Error(
158+
'EPERM: operation not permitted',
159+
) as NodeJS.ErrnoException
160+
err.code = 'EPERM'
161+
const spy = vi
162+
.spyOn(fsModule, 'safeDeleteSync')
163+
.mockImplementation(() => {
164+
throw err
165+
})
166+
try {
167+
expect(() => removeDlxPackageSync('eperm-pkg')).toThrow(
168+
/Permission denied removing DLX package/,
169+
)
170+
} finally {
171+
spy.mockRestore()
172+
}
173+
})
174+
175+
it('throws a read-only-filesystem error when safeDeleteSync fails with EROFS', async () => {
176+
const fsModule = await import('@socketsecurity/lib/fs')
177+
const err = new Error(
178+
'EROFS: read-only file system',
179+
) as NodeJS.ErrnoException
180+
err.code = 'EROFS'
181+
const spy = vi
182+
.spyOn(fsModule, 'safeDeleteSync')
183+
.mockImplementation(() => {
184+
throw err
185+
})
186+
try {
187+
expect(() => removeDlxPackageSync('rofs-pkg')).toThrow(
188+
/read-only filesystem/,
189+
)
190+
} finally {
191+
spy.mockRestore()
192+
}
193+
})
194+
195+
it('throws a generic failure error for unrecognized errno codes', async () => {
196+
const fsModule = await import('@socketsecurity/lib/fs')
197+
const err = new Error('EBUSY: resource busy') as NodeJS.ErrnoException
198+
err.code = 'EBUSY'
199+
const spy = vi
200+
.spyOn(fsModule, 'safeDeleteSync')
201+
.mockImplementation(() => {
202+
throw err
203+
})
204+
try {
205+
expect(() => removeDlxPackageSync('busy-pkg')).toThrow(
206+
/Failed to remove DLX package/,
207+
)
208+
} finally {
209+
spy.mockRestore()
210+
}
211+
})
130212
})
131213
})

0 commit comments

Comments
 (0)