@zenfs/core 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/fetch.js +5 -5
- package/dist/config.js +1 -0
- package/license.md +1 -1
- package/package.json +7 -4
- package/readme.md +3 -8
- package/scripts/make-index.js +3 -3
- package/scripts/test.js +31 -0
- package/tests/assignment.ts +20 -0
- package/tests/common.ts +21 -0
- package/tests/data/49chars.txt +1 -0
- package/tests/data/a.js +46 -0
- package/tests/data/a1.js +46 -0
- package/tests/data/elipses.txt +1 -0
- package/tests/data/empty.txt +0 -0
- package/tests/data/exit.js +22 -0
- package/tests/data/x.txt +1 -0
- package/tests/devices.test.ts +29 -0
- package/tests/fs/appendFile.test.ts +33 -0
- package/tests/fs/chmod.test.ts +48 -0
- package/tests/fs/dir.test.ts +156 -0
- package/tests/fs/directory.test.ts +129 -0
- package/tests/fs/errors.test.ts +53 -0
- package/tests/fs/exists.test.ts +22 -0
- package/tests/fs/links.test.ts +46 -0
- package/tests/fs/open.test.ts +39 -0
- package/tests/fs/permissions.test.ts +52 -0
- package/tests/fs/read.test.ts +67 -0
- package/tests/fs/readFile.test.ts +73 -0
- package/tests/fs/readdir.test.ts +87 -0
- package/tests/fs/rename.test.ts +107 -0
- package/tests/fs/stat.test.ts +48 -0
- package/tests/fs/streams.test.ts +177 -0
- package/tests/fs/times.test.ts +84 -0
- package/tests/fs/truncate.test.ts +94 -0
- package/tests/fs/watch.test.ts +124 -0
- package/tests/fs/write.test.ts +58 -0
- package/tests/fs/writeFile.test.ts +69 -0
- package/tests/handle.test.ts +60 -0
- package/tests/mutex.test.ts +62 -0
- package/tests/path.test.ts +34 -0
- package/tests/port/channel.test.ts +39 -0
- package/tests/port/config.test.ts +31 -0
- package/tests/port/config.worker.ts +5 -0
- package/tests/port/remote.test.ts +33 -0
- package/tests/port/remote.worker.ts +5 -0
- package/tests/port/timeout.test.ts +48 -0
- package/tests/readme.md +5 -0
- package/tests/setup/common.ts +28 -0
- package/tests/setup/cow+fetch.ts +43 -0
- package/tests/setup/memory.ts +3 -0
- package/tests/tsconfig.json +14 -0
- package/src/backends/backend.ts +0 -160
- package/src/backends/fetch.ts +0 -179
- package/src/backends/file_index.ts +0 -210
- package/src/backends/memory.ts +0 -50
- package/src/backends/overlay.ts +0 -568
- package/src/backends/port/fs.ts +0 -335
- package/src/backends/port/readme.md +0 -54
- package/src/backends/port/rpc.ts +0 -167
- package/src/backends/readme.md +0 -3
- package/src/backends/store/fs.ts +0 -715
- package/src/backends/store/readme.md +0 -9
- package/src/backends/store/simple.ts +0 -146
- package/src/backends/store/store.ts +0 -173
- package/src/config.ts +0 -152
- package/src/credentials.ts +0 -31
- package/src/devices.ts +0 -471
- package/src/emulation/async.ts +0 -834
- package/src/emulation/constants.ts +0 -182
- package/src/emulation/dir.ts +0 -138
- package/src/emulation/index.ts +0 -8
- package/src/emulation/path.ts +0 -440
- package/src/emulation/promises.ts +0 -1098
- package/src/emulation/shared.ts +0 -135
- package/src/emulation/streams.ts +0 -34
- package/src/emulation/sync.ts +0 -845
- package/src/emulation/watchers.ts +0 -193
- package/src/error.ts +0 -307
- package/src/file.ts +0 -661
- package/src/filesystem.ts +0 -174
- package/src/index.ts +0 -25
- package/src/inode.ts +0 -132
- package/src/mixins/async.ts +0 -208
- package/src/mixins/index.ts +0 -5
- package/src/mixins/mutexed.ts +0 -257
- package/src/mixins/readonly.ts +0 -96
- package/src/mixins/shared.ts +0 -25
- package/src/mixins/sync.ts +0 -58
- package/src/polyfills.ts +0 -21
- package/src/stats.ts +0 -363
- package/src/utils.ts +0 -288
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import assert, { rejects } from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { fs } from '../common.js';
|
|
4
|
+
|
|
5
|
+
const testFile = 'test-file.txt';
|
|
6
|
+
await fs.promises.writeFile(testFile, 'Sample content');
|
|
7
|
+
await fs.promises.mkdir('test-directory');
|
|
8
|
+
await fs.promises.symlink(testFile, 'test-symlink');
|
|
9
|
+
const testDirPath = 'test-dir';
|
|
10
|
+
const testFiles = ['file1.txt', 'file2.txt'];
|
|
11
|
+
await fs.promises.mkdir(testDirPath);
|
|
12
|
+
for (const file of testFiles) {
|
|
13
|
+
await fs.promises.writeFile(`${testDirPath}/${file}`, 'Sample content');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
suite('Dirent', () => {
|
|
17
|
+
test('Dirent name and parentPath getters', async () => {
|
|
18
|
+
const stats = await fs.promises.lstat(testFile);
|
|
19
|
+
const dirent = new fs.Dirent(testFile, stats);
|
|
20
|
+
|
|
21
|
+
assert(dirent.name === testFile);
|
|
22
|
+
assert(dirent.parentPath === testFile);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('Dirent.isFile', async () => {
|
|
26
|
+
const fileStats = await fs.promises.lstat(testFile);
|
|
27
|
+
const fileDirent = new fs.Dirent(testFile, fileStats);
|
|
28
|
+
|
|
29
|
+
assert(fileDirent.isFile());
|
|
30
|
+
assert(!fileDirent.isDirectory());
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('Dirent.isDirectory', async () => {
|
|
34
|
+
const dirStats = await fs.promises.lstat('test-directory');
|
|
35
|
+
const dirDirent = new fs.Dirent('test-directory', dirStats);
|
|
36
|
+
|
|
37
|
+
assert(!dirDirent.isFile());
|
|
38
|
+
assert(dirDirent.isDirectory());
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('Dirent.isSymbolicLink', async () => {
|
|
42
|
+
const symlinkStats = await fs.promises.lstat('test-symlink');
|
|
43
|
+
const symlinkDirent = new fs.Dirent('test-symlink', symlinkStats);
|
|
44
|
+
|
|
45
|
+
assert(symlinkDirent.isSymbolicLink());
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('Dirent other methods return false', async () => {
|
|
49
|
+
const fileStats = await fs.promises.lstat(testFile);
|
|
50
|
+
const fileDirent = new fs.Dirent(testFile, fileStats);
|
|
51
|
+
|
|
52
|
+
assert(!fileDirent.isBlockDevice());
|
|
53
|
+
assert(!fileDirent.isCharacterDevice());
|
|
54
|
+
assert(!fileDirent.isSocket());
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
suite('Dir', () => {
|
|
59
|
+
test('Dir read() method (Promise varient)', async () => {
|
|
60
|
+
const dir = new fs.Dir(testDirPath);
|
|
61
|
+
|
|
62
|
+
const dirent1 = await dir.read();
|
|
63
|
+
assert(dirent1 instanceof fs.Dirent);
|
|
64
|
+
assert(testFiles.includes(dirent1?.name));
|
|
65
|
+
|
|
66
|
+
const dirent2 = await dir.read();
|
|
67
|
+
assert(dirent2 instanceof fs.Dirent);
|
|
68
|
+
assert(testFiles.includes(dirent2?.name));
|
|
69
|
+
|
|
70
|
+
const dirent3 = await dir.read();
|
|
71
|
+
assert(dirent3 === null);
|
|
72
|
+
|
|
73
|
+
await dir.close();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('Dir read() method (Callback varient)', (_, done) => {
|
|
77
|
+
const dir = new fs.Dir(testDirPath);
|
|
78
|
+
dir.read((err, dirent) => {
|
|
79
|
+
assert(err === undefined);
|
|
80
|
+
assert(dirent != undefined);
|
|
81
|
+
assert(dirent instanceof fs.Dirent);
|
|
82
|
+
assert(testFiles.includes(dirent?.name));
|
|
83
|
+
dir.closeSync();
|
|
84
|
+
done();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('Dir readSync() method', () => {
|
|
89
|
+
const dir = new fs.Dir(testDirPath);
|
|
90
|
+
|
|
91
|
+
const dirent1 = dir.readSync();
|
|
92
|
+
assert(dirent1 instanceof fs.Dirent);
|
|
93
|
+
assert(testFiles.includes(dirent1?.name));
|
|
94
|
+
|
|
95
|
+
const dirent2 = dir.readSync();
|
|
96
|
+
assert(dirent2 instanceof fs.Dirent);
|
|
97
|
+
assert(testFiles.includes(dirent2?.name));
|
|
98
|
+
|
|
99
|
+
const dirent3 = dir.readSync();
|
|
100
|
+
assert(dirent3 === null);
|
|
101
|
+
|
|
102
|
+
dir.closeSync();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('Dir close() method (Promise version)', async () => {
|
|
106
|
+
const dir = new fs.Dir(testDirPath);
|
|
107
|
+
await dir.close();
|
|
108
|
+
rejects(dir.read(), 'Can not use closed Dir');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('Dir closeSync() method', () => {
|
|
112
|
+
const dir = new fs.Dir(testDirPath);
|
|
113
|
+
dir.closeSync();
|
|
114
|
+
assert.throws(() => dir.readSync(), 'Can not use closed Dir');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('Dir asynchronous iteration', async () => {
|
|
118
|
+
const dir = new fs.Dir(testDirPath);
|
|
119
|
+
const dirents: fs.Dirent[] = [];
|
|
120
|
+
|
|
121
|
+
for await (const dirent of dir) {
|
|
122
|
+
dirents.push(dirent);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
assert(dirents.length === 2);
|
|
126
|
+
assert(dirents[0] instanceof fs.Dirent);
|
|
127
|
+
assert(testFiles.includes(dirents[0].name));
|
|
128
|
+
assert(testFiles.includes(dirents[1].name));
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('Dir read after directory is closed', async () => {
|
|
132
|
+
const dir = new fs.Dir(testDirPath);
|
|
133
|
+
await dir.close();
|
|
134
|
+
await assert.rejects(dir.read(), 'Can not use closed Dir');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('Dir readSync after directory is closed', () => {
|
|
138
|
+
const dir = new fs.Dir(testDirPath);
|
|
139
|
+
dir.closeSync();
|
|
140
|
+
assert.throws(() => dir.readSync(), 'Can not use closed Dir');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('Dir close multiple times', async () => {
|
|
144
|
+
const dir = new fs.Dir(testDirPath);
|
|
145
|
+
await dir.close();
|
|
146
|
+
await dir.close(); // Should not throw an error
|
|
147
|
+
assert(dir['closed']);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('Dir closeSync multiple times', () => {
|
|
151
|
+
const dir = new fs.Dir(testDirPath);
|
|
152
|
+
dir.closeSync();
|
|
153
|
+
dir.closeSync(); // Should not throw an error
|
|
154
|
+
assert(dir['closed']);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { ErrnoError } from '../../src/error.js';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
suite('Directory', () => {
|
|
7
|
+
test('mkdir', async () => {
|
|
8
|
+
await fs.promises.mkdir('/one', 0o755);
|
|
9
|
+
assert(await fs.promises.exists('/one'));
|
|
10
|
+
await assert.rejects(fs.promises.mkdir('/one', 0o755), /EEXIST/);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('mkdirSync', () => fs.mkdirSync('/two', 0o000));
|
|
14
|
+
|
|
15
|
+
test('mkdir, nested', async () => {
|
|
16
|
+
try {
|
|
17
|
+
await fs.promises.mkdir('/nested/dir');
|
|
18
|
+
} catch (error: any) {
|
|
19
|
+
assert(error instanceof ErrnoError);
|
|
20
|
+
assert(error.code === 'ENOENT');
|
|
21
|
+
}
|
|
22
|
+
assert(!(await fs.promises.exists('/nested/dir')));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('mkdir, recursive', async () => {
|
|
26
|
+
assert((await fs.promises.mkdir('/recursiveP/A/B', { recursive: true, mode: 0o755 })) == '/recursiveP');
|
|
27
|
+
assert((await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o777 })) == '/recursiveP/A/B/C');
|
|
28
|
+
assert((await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o700 })) == undefined);
|
|
29
|
+
|
|
30
|
+
assert((await fs.promises.stat('/recursiveP')).mode == (fs.constants.S_IFDIR | 0o755));
|
|
31
|
+
assert((await fs.promises.stat('/recursiveP/A')).mode == (fs.constants.S_IFDIR | 0o755));
|
|
32
|
+
assert((await fs.promises.stat('/recursiveP/A/B')).mode == (fs.constants.S_IFDIR | 0o755));
|
|
33
|
+
assert((await fs.promises.stat('/recursiveP/A/B/C')).mode == (fs.constants.S_IFDIR | 0o777));
|
|
34
|
+
assert((await fs.promises.stat('/recursiveP/A/B/C/D')).mode == (fs.constants.S_IFDIR | 0o777));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('mkdirSync, recursive', () => {
|
|
38
|
+
assert(fs.mkdirSync('/recursiveS/A/B', { recursive: true, mode: 0o755 }) === '/recursiveS');
|
|
39
|
+
assert(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o777 }) === '/recursiveS/A/B/C');
|
|
40
|
+
assert(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o700 }) === undefined);
|
|
41
|
+
|
|
42
|
+
assert(fs.statSync('/recursiveS').mode == (fs.constants.S_IFDIR | 0o755));
|
|
43
|
+
assert(fs.statSync('/recursiveS/A').mode == (fs.constants.S_IFDIR | 0o755));
|
|
44
|
+
assert(fs.statSync('/recursiveS/A/B').mode == (fs.constants.S_IFDIR | 0o755));
|
|
45
|
+
assert(fs.statSync('/recursiveS/A/B/C').mode == (fs.constants.S_IFDIR | 0o777));
|
|
46
|
+
assert(fs.statSync('/recursiveS/A/B/C/D').mode == (fs.constants.S_IFDIR | 0o777));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('readdirSync without permission', () => {
|
|
50
|
+
try {
|
|
51
|
+
fs.readdirSync('/two');
|
|
52
|
+
} catch (error: any) {
|
|
53
|
+
assert(error instanceof ErrnoError);
|
|
54
|
+
assert(error.code === 'EACCES');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('rmdir (non-empty)', async () => {
|
|
59
|
+
await fs.promises.mkdir('/rmdirTest');
|
|
60
|
+
await fs.promises.mkdir('/rmdirTest/rmdirTest2');
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await fs.promises.rmdir('/rmdirTest');
|
|
64
|
+
} catch (error: any) {
|
|
65
|
+
assert(error instanceof ErrnoError);
|
|
66
|
+
assert(error.code === 'ENOTEMPTY');
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('readdirSync on file', () => {
|
|
71
|
+
let wasThrown = false;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
fs.readdirSync('a.js');
|
|
75
|
+
} catch (error: any) {
|
|
76
|
+
assert(error instanceof ErrnoError);
|
|
77
|
+
wasThrown = true;
|
|
78
|
+
assert(error.code === 'ENOTDIR');
|
|
79
|
+
}
|
|
80
|
+
assert(wasThrown);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('readdir on file', async () => {
|
|
84
|
+
try {
|
|
85
|
+
await fs.promises.readdir('a.js');
|
|
86
|
+
} catch (error: any) {
|
|
87
|
+
assert(error instanceof ErrnoError);
|
|
88
|
+
assert(error.code === 'ENOTDIR');
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('readdirSync on non-existant directory', () => {
|
|
93
|
+
let wasThrown = false;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
fs.readdirSync('/does/not/exist');
|
|
97
|
+
} catch (error: any) {
|
|
98
|
+
assert(error instanceof ErrnoError);
|
|
99
|
+
wasThrown = true;
|
|
100
|
+
assert(error.code === 'ENOENT');
|
|
101
|
+
}
|
|
102
|
+
assert(wasThrown);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('readdir on non-existant directory', async () => {
|
|
106
|
+
try {
|
|
107
|
+
await fs.promises.readdir('/does/not/exist');
|
|
108
|
+
} catch (error: any) {
|
|
109
|
+
assert(error instanceof ErrnoError);
|
|
110
|
+
assert(error.code === 'ENOENT');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('rm recursively asynchronously', async () => {
|
|
115
|
+
await fs.promises.mkdir('/rmDirRecusrively');
|
|
116
|
+
await fs.promises.mkdir('/rmDirRecusrively/rmDirNested');
|
|
117
|
+
await fs.promises.writeFile('/rmDirRecusrively/rmDirNested/test.txt', 'hello world!');
|
|
118
|
+
|
|
119
|
+
await fs.promises.rm('/rmDirRecusrively', { recursive: true });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('rm recursively synchronously', () => {
|
|
123
|
+
fs.mkdirSync('/rmDirRecusrively');
|
|
124
|
+
fs.mkdirSync('/rmDirRecusrively/rmDirNested');
|
|
125
|
+
fs.writeFileSync('/rmDirRecusrively/rmDirNested/test.txt', 'hello world!');
|
|
126
|
+
|
|
127
|
+
fs.rmSync('/rmDirRecusrively', { recursive: true });
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { ErrnoError } from '../../src/error.js';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
const existingFile = '/exit.js';
|
|
7
|
+
|
|
8
|
+
async function expectError(fn: (...args: any[]) => unknown, path: string, ...args: any[]) {
|
|
9
|
+
let error: ErrnoError | undefined;
|
|
10
|
+
try {
|
|
11
|
+
await fn(path, ...args);
|
|
12
|
+
} catch (err: any) {
|
|
13
|
+
assert(err instanceof ErrnoError);
|
|
14
|
+
error = err;
|
|
15
|
+
}
|
|
16
|
+
assert(error);
|
|
17
|
+
assert.equal(error.path, path);
|
|
18
|
+
assert(error.message.includes(path));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
suite('Error messages', () => {
|
|
22
|
+
const path = '/non-existent';
|
|
23
|
+
|
|
24
|
+
fs.promises.stat(path).catch((error: ErrnoError) => {
|
|
25
|
+
assert.equal(error.toString(), error.message);
|
|
26
|
+
assert.equal(error.bufferSize(), 4 + JSON.stringify(error.toJSON()).length);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('stat', () => expectError(fs.promises.stat, path));
|
|
30
|
+
test('mkdir', () => expectError(fs.promises.mkdir, existingFile, 0o666));
|
|
31
|
+
test('rmdir', () => expectError(fs.promises.rmdir, path));
|
|
32
|
+
test('rmdir', () => expectError(fs.promises.rmdir, existingFile));
|
|
33
|
+
test('rename', () => expectError(fs.promises.rename, path, 'foo'));
|
|
34
|
+
test('open', () => expectError(fs.promises.open, path, 'r'));
|
|
35
|
+
test('readdir', () => expectError(fs.promises.readdir, path));
|
|
36
|
+
test('unlink', () => expectError(fs.promises.unlink, path));
|
|
37
|
+
test('link', () => expectError(fs.promises.link, path, 'foo'));
|
|
38
|
+
test('chmod', () => expectError(fs.promises.chmod, path, 0o666));
|
|
39
|
+
test('lstat', () => expectError(fs.promises.lstat, path));
|
|
40
|
+
test('readlink', () => expectError(fs.promises.readlink, path));
|
|
41
|
+
test('statSync', () => expectError(fs.statSync, path));
|
|
42
|
+
test('mkdirSync', () => expectError(fs.mkdirSync, existingFile, 0o666));
|
|
43
|
+
test('rmdirSync', () => expectError(fs.rmdirSync, path));
|
|
44
|
+
test('rmdirSync', () => expectError(fs.rmdirSync, existingFile));
|
|
45
|
+
test('renameSync', () => expectError(fs.renameSync, path, 'foo'));
|
|
46
|
+
test('openSync', () => expectError(fs.openSync, path, 'r'));
|
|
47
|
+
test('readdirSync', () => expectError(fs.readdirSync, path));
|
|
48
|
+
test('unlinkSync', () => expectError(fs.unlinkSync, path));
|
|
49
|
+
test('linkSync', () => expectError(fs.linkSync, path, 'foo'));
|
|
50
|
+
test('chmodSync', () => expectError(fs.chmodSync, path, 0o666));
|
|
51
|
+
test('lstatSync', () => expectError(fs.lstatSync, path));
|
|
52
|
+
test('readlinkSync', () => expectError(fs.readlinkSync, path));
|
|
53
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { fs } from '../common.js';
|
|
4
|
+
|
|
5
|
+
suite('exists', () => {
|
|
6
|
+
const f = 'x.txt';
|
|
7
|
+
|
|
8
|
+
test('return true for an existing file', async () => {
|
|
9
|
+
const exists = await fs.promises.exists(f);
|
|
10
|
+
assert(exists);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('return false for a non-existent file', async () => {
|
|
14
|
+
const exists = await fs.promises.exists(f + '-NO');
|
|
15
|
+
assert(!exists);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('have sync methods that behave the same', () => {
|
|
19
|
+
assert(fs.existsSync(f));
|
|
20
|
+
assert(!fs.existsSync(f + '-NO'));
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { join } from '../../src/emulation/path.js';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
suite('Links', () => {
|
|
7
|
+
const target = '/a1.js',
|
|
8
|
+
symlink = 'symlink1.js',
|
|
9
|
+
hardlink = 'link1.js';
|
|
10
|
+
|
|
11
|
+
test('symlink', async () => {
|
|
12
|
+
await fs.promises.symlink(target, symlink);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('lstat', async () => {
|
|
16
|
+
const stats = await fs.promises.lstat(symlink);
|
|
17
|
+
assert(stats.isSymbolicLink());
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('readlink', async () => {
|
|
21
|
+
const destination = await fs.promises.readlink(symlink);
|
|
22
|
+
assert(destination === target);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('unlink', async () => {
|
|
26
|
+
await fs.promises.unlink(symlink);
|
|
27
|
+
assert(!(await fs.promises.exists(symlink)));
|
|
28
|
+
assert(await fs.promises.exists(target));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('link', async () => {
|
|
32
|
+
await fs.promises.link(target, hardlink);
|
|
33
|
+
const targetContent = await fs.promises.readFile(target, 'utf8');
|
|
34
|
+
const linkContent = await fs.promises.readFile(hardlink, 'utf8');
|
|
35
|
+
assert(targetContent === linkContent);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('file inside symlinked directory', async () => {
|
|
39
|
+
await fs.promises.symlink('.', 'link');
|
|
40
|
+
const targetContent = await fs.promises.readFile(target, 'utf8');
|
|
41
|
+
const link = join('link', target);
|
|
42
|
+
assert((await fs.promises.realpath(link)) === target);
|
|
43
|
+
const linkContent = await fs.promises.readFile(link, 'utf8');
|
|
44
|
+
assert(targetContent === linkContent);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { ErrnoError } from '../../src/error.js';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
suite('fs file opening', () => {
|
|
7
|
+
const filename = 'a.js';
|
|
8
|
+
|
|
9
|
+
test('throw ENOENT when opening non-existent file (sync)', () => {
|
|
10
|
+
let caughtException = false;
|
|
11
|
+
try {
|
|
12
|
+
fs.openSync('/path/to/file/that/does/not/exist', 'r');
|
|
13
|
+
} catch (error: any) {
|
|
14
|
+
assert(error instanceof ErrnoError);
|
|
15
|
+
assert(error?.code === 'ENOENT');
|
|
16
|
+
caughtException = true;
|
|
17
|
+
}
|
|
18
|
+
assert(caughtException);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('throw ENOENT when opening non-existent file (async)', async () => {
|
|
22
|
+
try {
|
|
23
|
+
await fs.promises.open('/path/to/file/that/does/not/exist', 'r');
|
|
24
|
+
} catch (error: any) {
|
|
25
|
+
assert(error instanceof ErrnoError);
|
|
26
|
+
assert(error?.code === 'ENOENT');
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('open file with mode "r"', async () => {
|
|
31
|
+
const { fd } = await fs.promises.open(filename, 'r');
|
|
32
|
+
assert(fd >= -Infinity);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('open file with mode "rs"', async () => {
|
|
36
|
+
const { fd } = await fs.promises.open(filename, 'rs');
|
|
37
|
+
assert(fd >= -Infinity);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { R_OK, W_OK, X_OK } from '../../src/emulation/constants.js';
|
|
4
|
+
import { join } from '../../src/emulation/path.js';
|
|
5
|
+
import { ErrnoError } from '../../src/error.js';
|
|
6
|
+
import { encodeUTF8 } from '../../src/utils.js';
|
|
7
|
+
import { fs } from '../common.js';
|
|
8
|
+
|
|
9
|
+
suite('Permissions', () => {
|
|
10
|
+
async function test_item(path: string): Promise<void> {
|
|
11
|
+
const stats = await fs.promises.stat(path).catch((error: ErrnoError) => {
|
|
12
|
+
assert(error instanceof ErrnoError);
|
|
13
|
+
assert(error.code === 'EACCES');
|
|
14
|
+
});
|
|
15
|
+
if (!stats) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
assert(stats.hasAccess(X_OK));
|
|
19
|
+
|
|
20
|
+
function checkError(access: number) {
|
|
21
|
+
return function (error: ErrnoError) {
|
|
22
|
+
assert(error instanceof ErrnoError);
|
|
23
|
+
assert(error);
|
|
24
|
+
assert(!stats!.hasAccess(access));
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (stats.isDirectory()) {
|
|
29
|
+
for (const dir of await fs.promises.readdir(path)) {
|
|
30
|
+
await test_item(join(path, dir));
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
await fs.promises.readFile(path).catch(checkError(R_OK));
|
|
34
|
+
}
|
|
35
|
+
assert(stats.hasAccess(R_OK));
|
|
36
|
+
|
|
37
|
+
if (stats.isDirectory()) {
|
|
38
|
+
const testFile = join(path, '__test_file_plz_ignore.txt');
|
|
39
|
+
await fs.promises.writeFile(testFile, encodeUTF8('this is a test file, please ignore.')).catch(checkError(W_OK));
|
|
40
|
+
await fs.promises.unlink(testFile).catch(checkError(W_OK));
|
|
41
|
+
} else {
|
|
42
|
+
const handle = await fs.promises.open(path, 'a').catch(checkError(W_OK));
|
|
43
|
+
if (!handle) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await handle.close();
|
|
47
|
+
}
|
|
48
|
+
assert(stats.hasAccess(R_OK));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test('recursive', () => test_item('/'));
|
|
52
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { fs } from '../common.js';
|
|
4
|
+
|
|
5
|
+
const filepath: string = 'x.txt';
|
|
6
|
+
const expected: string = 'xyz\n';
|
|
7
|
+
|
|
8
|
+
suite('read', () => {
|
|
9
|
+
test('read file asynchronously', async () => {
|
|
10
|
+
const handle = await fs.promises.open(filepath, 'r');
|
|
11
|
+
const { bytesRead, buffer } = await handle.read(Buffer.alloc(expected.length), 0, expected.length, 0);
|
|
12
|
+
|
|
13
|
+
assert(bytesRead == expected.length);
|
|
14
|
+
assert(buffer.toString() == expected);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('read file synchronously', () => {
|
|
18
|
+
const fd = fs.openSync(filepath, 'r');
|
|
19
|
+
const buffer = Buffer.alloc(expected.length);
|
|
20
|
+
const bytesRead = fs.readSync(fd, buffer, 0, expected.length, 0);
|
|
21
|
+
|
|
22
|
+
assert(bytesRead == expected.length);
|
|
23
|
+
assert(buffer.toString() == expected);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
suite('read binary', () => {
|
|
28
|
+
test('Read a file and check its binary bytes (asynchronous)', async () => {
|
|
29
|
+
const buff = await fs.promises.readFile('elipses.txt');
|
|
30
|
+
assert(((buff[1] << 8) | buff[0]) === 32994);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('Read a file and check its binary bytes (synchronous)', () => {
|
|
34
|
+
const buff = fs.readFileSync('elipses.txt');
|
|
35
|
+
assert(((buff[1] << 8) | buff[0]) === 32994);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
suite('read buffer', () => {
|
|
40
|
+
const bufferAsync = Buffer.alloc(expected.length);
|
|
41
|
+
const bufferSync = Buffer.alloc(expected.length);
|
|
42
|
+
|
|
43
|
+
test('read file asynchronously', async () => {
|
|
44
|
+
const handle = await fs.promises.open(filepath, 'r');
|
|
45
|
+
const { bytesRead } = await handle.read(bufferAsync, 0, expected.length, 0);
|
|
46
|
+
|
|
47
|
+
assert(bytesRead === expected.length);
|
|
48
|
+
assert(bufferAsync.toString() === expected);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('read file synchronously', () => {
|
|
52
|
+
const fd = fs.openSync(filepath, 'r');
|
|
53
|
+
const bytesRead = fs.readSync(fd, bufferSync, 0, expected.length, 0);
|
|
54
|
+
|
|
55
|
+
assert(bufferSync.toString() === expected);
|
|
56
|
+
assert(bytesRead === expected.length);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('read file synchronously to non-zero offset', () => {
|
|
60
|
+
const fd = fs.openSync(filepath, 'r');
|
|
61
|
+
const buffer = Buffer.alloc(expected.length + 10);
|
|
62
|
+
const bytesRead = fs.readSync(fd, buffer, 10, expected.length, 0);
|
|
63
|
+
|
|
64
|
+
assert(buffer.subarray(10, buffer.length).toString() === expected);
|
|
65
|
+
assert(bytesRead === expected.length);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { fs } from '../common.js';
|
|
4
|
+
|
|
5
|
+
suite('Reading', () => {
|
|
6
|
+
test('Cannot read a file with an invalid encoding', () => {
|
|
7
|
+
assert.throws(() => fs.readFileSync('a.js', 'wrongencoding' as BufferEncoding));
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('Reading past the end of a file should not be an error', async () => {
|
|
11
|
+
const handle = await fs.promises.open('a.js', 'r');
|
|
12
|
+
const { bytesRead } = await handle.read(new Uint8Array(10), 0, 10, 10000);
|
|
13
|
+
assert.strictEqual(bytesRead, 0);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
suite('Read and Unlink', () => {
|
|
18
|
+
const dir = 'test-readfile-unlink';
|
|
19
|
+
const file = 'test-readfile-unlink/test.bin';
|
|
20
|
+
const data = new Uint8Array(512).fill(42);
|
|
21
|
+
|
|
22
|
+
test('create directory and write file', async () => {
|
|
23
|
+
await fs.promises.mkdir(dir);
|
|
24
|
+
await fs.promises.writeFile(file, data);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('read file and verify its content', async () => {
|
|
28
|
+
const read: Uint8Array = await fs.promises.readFile(file);
|
|
29
|
+
assert.equal(read.length, data.length);
|
|
30
|
+
assert.equal(read[0], 42);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('unlink file and remove directory', async () => {
|
|
34
|
+
await fs.promises.unlink(file);
|
|
35
|
+
await fs.promises.rmdir(dir);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
suite('Read File Test', () => {
|
|
40
|
+
const fn = 'empty.txt';
|
|
41
|
+
|
|
42
|
+
test('read file asynchronously', async () => {
|
|
43
|
+
const data: Uint8Array = await fs.promises.readFile(fn);
|
|
44
|
+
assert(data != undefined);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('read file with utf-8 encoding asynchronously', async () => {
|
|
48
|
+
const data: string = await fs.promises.readFile(fn, 'utf8');
|
|
49
|
+
assert.strictEqual(data, '');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('read file synchronously', () => {
|
|
53
|
+
const data: Uint8Array = fs.readFileSync(fn);
|
|
54
|
+
assert(data != undefined);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('read file with utf-8 encoding synchronously', () => {
|
|
58
|
+
const data: string = fs.readFileSync(fn, 'utf8');
|
|
59
|
+
assert.strictEqual(data, '');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
suite('fs file reading', () => {
|
|
64
|
+
test('read file synchronously and verify the content', () => {
|
|
65
|
+
const content = fs.readFileSync('elipses.txt', 'utf8');
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < content.length; i++) {
|
|
68
|
+
assert(content[i] === '…');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
assert(content.length === 10000);
|
|
72
|
+
});
|
|
73
|
+
});
|