Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/fs-core/src/Superblock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,8 +653,7 @@ export class Superblock {

public readonly unlink = (filename: string) => {
const link: Link = this.getLinkOrThrow(filename, 'unlink');
// TODO: Check if it is file, dir, other...
if (link.length) throw Error('Dir not empty...');
if (link.getNode().isDirectory()) throw createError(ERROR_CODE.EPERM, 'unlink', filename);
this._emitDeleteRecursive(link);
this.deleteLink(link);
const node = link.getNode();
Expand Down
1 change: 1 addition & 0 deletions packages/fs-node/src/__tests__/Stats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('Stats', () => {

it('keeps birthtime and ctime as separate instances even when equal', () => {
const { node } = setup();
node.ctime = new Date(node.btime.getTime());
const stats = Stats.build(node, false);
expect(stats.birthtime.getTime()).toBe(stats.ctime.getTime());
expect(stats.birthtime).not.toBe(stats.ctime);
Expand Down
89 changes: 89 additions & 0 deletions packages/fs-node/src/__tests__/volume/unlinkSync.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { create } from '../util';

describe('unlinkSync', () => {
it('removes a file', () => {
const vol = create({
'/foo.txt': 'bar',
'/baz.txt': 'qux',
});
vol.unlinkSync('/foo.txt');
expect(vol.toJSON()).toEqual({
'/baz.txt': 'qux',
});
});

it('throws ENOENT when file does not exist', () => {
const vol = create({
'/foo.txt': 'bar',
});
expect(() => vol.unlinkSync('/bar.txt')).toThrowError(
new Error("ENOENT: no such file or directory, unlink '/bar.txt'"),
);
});

describe('when path is a directory', () => {
it('throws EPERM for an empty directory', () => {
const vol = create({});
vol.mkdirSync('/dir');
expect(() => vol.unlinkSync('/dir')).toThrowError(new Error("EPERM: operation not permitted, unlink '/dir'"));
expect(vol.existsSync('/dir')).toBe(true);
});

it('throws EPERM for a non-empty directory', () => {
const vol = create({
'/dir/foo.txt': 'bar',
});
expect(() => vol.unlinkSync('/dir')).toThrowError(new Error("EPERM: operation not permitted, unlink '/dir'"));
expect(vol.toJSON()).toEqual({
'/dir/foo.txt': 'bar',
});
});

it('throws EPERM for the root directory', () => {
const vol = create({
'/foo.txt': 'bar',
});
expect(() => vol.unlinkSync('/')).toThrowError(new Error("EPERM: operation not permitted, unlink '/'"));
});

it('has an EPERM error code', () => {
const vol = create({});
vol.mkdirSync('/dir');
try {
vol.unlinkSync('/dir');
throw new Error('not this error');
} catch (error) {
expect(error.code).toBe('EPERM');
expect(error.path).toBe('/dir');
}
});

it('removes a symlink pointing to a directory', () => {
const vol = create({
'/dir/foo.txt': 'bar',
});
vol.symlinkSync('/dir', '/link');
vol.unlinkSync('/link');
expect(vol.existsSync('/link')).toBe(false);
expect(vol.existsSync('/dir')).toBe(true);
});
});

it('async unlink of a directory returns EPERM', done => {
const vol = create({});
vol.mkdirSync('/dir');
vol.unlink('/dir', err => {
expect((err as any).code).toBe('EPERM');
expect(vol.existsSync('/dir')).toBe(true);
done();
});
});

it('promises unlink of a directory rejects with EPERM', async () => {
const vol = create({});
vol.mkdirSync('/dir');
await expect(vol.promises.unlink('/dir')).rejects.toThrowError(
new Error("EPERM: operation not permitted, unlink '/dir'"),
);
});
});
Loading