@zenfs/core 2.5.7 → 2.6.0
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/cow.d.ts +13 -0
- package/dist/backends/cow.js +48 -11
- package/dist/backends/single_buffer.d.ts +2 -2
- package/dist/backends/store/fs.d.ts +6 -6
- package/dist/backends/store/fs.js +14 -10
- package/dist/internal/devices.d.ts +6 -6
- package/dist/internal/filesystem.d.ts +4 -4
- package/dist/internal/index_fs.d.ts +4 -4
- package/dist/internal/index_fs.js +6 -8
- package/dist/internal/inode.js +3 -0
- package/dist/node/async.js +1 -1
- package/dist/node/dir.d.ts +3 -1
- package/dist/node/dir.js +23 -5
- package/dist/node/promises.d.ts +4 -2
- package/dist/node/promises.js +130 -89
- package/dist/node/streams.d.ts +4 -0
- package/dist/node/streams.js +9 -10
- package/dist/node/sync.js +71 -40
- package/dist/utils.d.ts +8 -1
- package/dist/utils.js +26 -7
- package/dist/vfs/async.js +224 -85
- package/dist/vfs/file.d.ts +13 -7
- package/dist/vfs/file.js +661 -357
- package/dist/vfs/shared.d.ts +1 -0
- package/dist/vfs/shared.js +19 -3
- package/dist/vfs/sync.js +214 -85
- package/dist/vfs/vcache.d.ts +66 -0
- package/dist/vfs/vcache.js +249 -0
- package/dist/vfs/vnode.d.ts +66 -0
- package/dist/vfs/vnode.js +200 -0
- package/dist/vfs/watchers.js +4 -2
- package/dist/vfs/xattr.js +2 -2
- package/eslint.shared.js +1 -1
- package/package.json +6 -3
- package/scripts/make-index.js +7 -1
- package/scripts/test.js +28 -21
- package/tests/backend/port.test.ts +3 -3
- package/tests/backend/store.test.ts +49 -0
- package/tests/common/inode.test.ts +8 -0
- package/tests/common.ts +62 -0
- package/tests/fetch/fetch.ts +6 -0
- package/tests/fetch/run.sh +2 -2
- package/tests/fetch/server.js +3 -3
- package/tests/fs/append.test.ts +5 -5
- package/tests/fs/concurrency.test.ts +52 -0
- package/tests/fs/dir.test.ts +13 -15
- package/tests/fs/directory.test.ts +32 -31
- package/tests/fs/errors.test.ts +151 -31
- package/tests/fs/exists.test.ts +4 -4
- package/tests/fs/links.test.ts +4 -11
- package/tests/fs/open.test.ts +4 -4
- package/tests/fs/permissions.test.ts +15 -15
- package/tests/fs/read.test.ts +9 -9
- package/tests/fs/readFile.test.ts +9 -9
- package/tests/fs/rename.test.ts +8 -8
- package/tests/fs/scaling.test.ts +4 -4
- package/tests/fs/stat.test.ts +20 -20
- package/tests/fs/streams.test.ts +9 -6
- package/tests/fs/tempdir.test.ts +33 -10
- package/tests/fs/times.test.ts +11 -11
- package/tests/fs/truncate.test.ts +6 -6
- package/tests/fs/watch.test.ts +107 -53
- package/tests/fs/write.test.ts +10 -10
- package/tests/fs/xattr.test.ts +3 -3
- package/tests/native/run.sh +3 -0
- package/tests/native/setup.ts +230 -0
- package/types/readable-stream.d.ts +2 -2
|
@@ -2,38 +2,38 @@
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { join } from 'node:path/posix';
|
|
4
4
|
import { suite, test } from 'node:test';
|
|
5
|
-
import { fs } from '../common.js';
|
|
5
|
+
import { config, fs } from '../common.js';
|
|
6
6
|
|
|
7
7
|
const testDir = 'test-dir';
|
|
8
8
|
const testFiles = ['file1.txt', 'file2.txt', 'file3.txt'];
|
|
9
9
|
const testDirectories = ['subdir1', 'subdir2'];
|
|
10
10
|
|
|
11
|
-
fs.
|
|
11
|
+
await fs.promises.mkdir(testDir);
|
|
12
12
|
for (const file of testFiles) {
|
|
13
|
-
fs.
|
|
13
|
+
await fs.promises.writeFile(`${testDir}/${file}`, 'Sample content');
|
|
14
14
|
}
|
|
15
15
|
for (const dir of testDirectories) {
|
|
16
|
-
fs.
|
|
16
|
+
await fs.promises.mkdir(`${testDir}/${dir}`);
|
|
17
17
|
for (const file of ['file4.txt', 'file5.txt']) {
|
|
18
|
-
fs.
|
|
18
|
+
await fs.promises.writeFile(`${testDir}/${dir}/${file}`, 'Sample content');
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
suite('Directories', () => {
|
|
23
|
-
test('mkdir', async () => {
|
|
22
|
+
suite('Directories', config('directories'), () => {
|
|
23
|
+
test('mkdir', config('async'), async () => {
|
|
24
24
|
await fs.promises.mkdir('/one', 0o755);
|
|
25
25
|
assert(await fs.promises.exists('/one'));
|
|
26
26
|
await assert.rejects(fs.promises.mkdir('/one', 0o755), { code: 'EEXIST' });
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test('mkdirSync',
|
|
29
|
+
test('mkdirSync', config('sync'), () => fs.mkdirSync('/two', 0o000));
|
|
30
30
|
|
|
31
|
-
test('mkdir, nested', async () => {
|
|
31
|
+
test('mkdir, nested', config('async'), async () => {
|
|
32
32
|
await assert.rejects(fs.promises.mkdir('/nested/dir'), { code: 'ENOENT', path: '/nested/dir' });
|
|
33
33
|
assert(!(await fs.promises.exists('/nested/dir')));
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
test('mkdir, recursive', async () => {
|
|
36
|
+
test('mkdir, recursive', config('async'), async () => {
|
|
37
37
|
assert.equal(await fs.promises.mkdir('/recursiveP/A/B', { recursive: true, mode: 0o755 }), '/recursiveP');
|
|
38
38
|
assert.equal(await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o777 }), '/recursiveP/A/B/C');
|
|
39
39
|
assert.equal(await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o700 }), undefined);
|
|
@@ -45,7 +45,7 @@ suite('Directories', () => {
|
|
|
45
45
|
assert.equal((await fs.promises.stat('/recursiveP/A/B/C/D')).mode, fs.constants.S_IFDIR | 0o777);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
test('mkdirSync, recursive', () => {
|
|
48
|
+
test('mkdirSync, recursive', config('sync'), () => {
|
|
49
49
|
assert.equal(fs.mkdirSync('/recursiveS/A/B', { recursive: true, mode: 0o755 }), '/recursiveS');
|
|
50
50
|
assert.equal(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o777 }), '/recursiveS/A/B/C');
|
|
51
51
|
assert.equal(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o700 }), undefined);
|
|
@@ -57,30 +57,30 @@ suite('Directories', () => {
|
|
|
57
57
|
assert.equal(fs.statSync('/recursiveS/A/B/C/D').mode, fs.constants.S_IFDIR | 0o777);
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
test('rmdir (non-empty)', async () => {
|
|
60
|
+
test('rmdir (non-empty)', config('async'), async () => {
|
|
61
61
|
await fs.promises.mkdir('/rmdirTest');
|
|
62
62
|
await fs.promises.mkdir('/rmdirTest/rmdirTest2');
|
|
63
63
|
|
|
64
64
|
await assert.rejects(fs.promises.rmdir('/rmdirTest'), { code: 'ENOTEMPTY' });
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
test('readdirSync on file', () => {
|
|
67
|
+
test('readdirSync on file', config('sync'), () => {
|
|
68
68
|
assert.throws(() => fs.readdirSync('a.js'), { code: 'ENOTDIR' });
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
test('readdir on file', async () => {
|
|
71
|
+
test('readdir on file', config('async'), async () => {
|
|
72
72
|
await assert.rejects(fs.promises.readdir('a.js'), { code: 'ENOTDIR' });
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
test('readdirSync on non-existent directory', () => {
|
|
75
|
+
test('readdirSync on non-existent directory', config('sync'), () => {
|
|
76
76
|
assert.throws(() => fs.readdirSync('/does/not/exist'), { code: 'ENOENT' });
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
test('readdir on non-existent directory', async () => {
|
|
79
|
+
test('readdir on non-existent directory', config('async'), async () => {
|
|
80
80
|
await assert.rejects(fs.promises.readdir('/does/not/exist'), { code: 'ENOENT' });
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
test('rm recursively', async () => {
|
|
83
|
+
test('rm recursively', config('async'), async () => {
|
|
84
84
|
await fs.promises.mkdir('/rmDirRecursively');
|
|
85
85
|
await fs.promises.mkdir('/rmDirRecursively/rmDirNested');
|
|
86
86
|
await fs.promises.writeFile('/rmDirRecursively/rmDirNested/test.txt', 'hello world!');
|
|
@@ -88,7 +88,7 @@ suite('Directories', () => {
|
|
|
88
88
|
await fs.promises.rm('/rmDirRecursively', { recursive: true });
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
test('rmSync recursively', () => {
|
|
91
|
+
test('rmSync recursively', config('sync'), () => {
|
|
92
92
|
fs.mkdirSync('/rmDirRecursively');
|
|
93
93
|
fs.mkdirSync('/rmDirRecursively/rmDirNested');
|
|
94
94
|
fs.writeFileSync('/rmDirRecursively/rmDirNested/test.txt', 'hello world!');
|
|
@@ -96,7 +96,7 @@ suite('Directories', () => {
|
|
|
96
96
|
fs.rmSync('/rmDirRecursively', { recursive: true });
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
test('readdir returns files and directories', async () => {
|
|
99
|
+
test('readdir returns files and directories', config('async'), async () => {
|
|
100
100
|
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true });
|
|
101
101
|
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name);
|
|
102
102
|
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name);
|
|
@@ -105,7 +105,7 @@ suite('Directories', () => {
|
|
|
105
105
|
assert(testDirectories.every(dir => dirs.includes(dir)));
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
test('readdirSync returns files and directories', () => {
|
|
108
|
+
test('readdirSync returns files and directories', config('sync'), () => {
|
|
109
109
|
const dirents = fs.readdirSync(testDir, { withFileTypes: true });
|
|
110
110
|
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name);
|
|
111
111
|
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name);
|
|
@@ -114,53 +114,54 @@ suite('Directories', () => {
|
|
|
114
114
|
assert(testDirectories.every(dir => dirs.includes(dir)));
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
-
test('readdir returns Dirent objects', async () => {
|
|
117
|
+
test('readdir returns Dirent objects', config('async'), async () => {
|
|
118
118
|
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true });
|
|
119
119
|
assert(dirents[0] instanceof fs.Dirent);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
test('readdirSync returns Dirent objects', () => {
|
|
122
|
+
test('readdirSync returns Dirent objects', config('sync'), () => {
|
|
123
123
|
const dirents = fs.readdirSync(testDir, { withFileTypes: true });
|
|
124
124
|
assert(dirents[0] instanceof fs.Dirent);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
test('readdir works without withFileTypes option', async () => {
|
|
127
|
+
test('readdir works without withFileTypes option', config('async'), async () => {
|
|
128
128
|
const files = await fs.promises.readdir(testDir);
|
|
129
129
|
assert(testFiles.every(entry => files.includes(entry)));
|
|
130
130
|
assert(testDirectories.every(entry => files.includes(entry)));
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
test('readdirSync works without withFileTypes option', () => {
|
|
133
|
+
test('readdirSync works without withFileTypes option', config('sync'), () => {
|
|
134
134
|
const files = fs.readdirSync(testDir);
|
|
135
135
|
assert(testFiles.every(entry => files.includes(entry)));
|
|
136
136
|
assert(testDirectories.every(entry => files.includes(entry)));
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
test('readdir returns files recursively', async () => {
|
|
139
|
+
test('readdir returns files recursively', config('async'), async () => {
|
|
140
140
|
const entries = await fs.promises.readdir(testDir, { recursive: true });
|
|
141
141
|
assert(entries.includes('file1.txt'));
|
|
142
142
|
assert(entries.includes('subdir1/file4.txt'));
|
|
143
143
|
assert(entries.includes('subdir2/file5.txt'));
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
-
test('readdir returns Dirent recursively', async () => {
|
|
146
|
+
test('readdir returns Dirent recursively', config('async'), async () => {
|
|
147
147
|
const entries = await fs.promises.readdir(testDir, { recursive: true, withFileTypes: true });
|
|
148
148
|
entries.sort((a, b) => join(a.parentPath, a.name).localeCompare(join(b.parentPath, b.name)));
|
|
149
149
|
const values = entries.map(entry => [entry.parentPath, entry.name]);
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
assert.deepEqual(values[
|
|
153
|
-
assert.deepEqual(values[
|
|
151
|
+
// `parentPath` is resolved against the directory that was read
|
|
152
|
+
assert.deepEqual(values[0], ['/' + testDir, 'file1.txt']);
|
|
153
|
+
assert.deepEqual(values[4], ['/' + testDir + '/subdir1', 'file4.txt']);
|
|
154
|
+
assert.deepEqual(values[8], ['/' + testDir + '/subdir2', 'file5.txt']);
|
|
154
155
|
});
|
|
155
156
|
|
|
156
|
-
test('readdirSync returns files recursively', () => {
|
|
157
|
+
test('readdirSync returns files recursively', config('sync'), () => {
|
|
157
158
|
const entries = fs.readdirSync(testDir, { recursive: true }).sort();
|
|
158
159
|
assert.equal(entries[0], 'file1.txt');
|
|
159
160
|
assert.equal(entries[4], 'subdir1/file4.txt');
|
|
160
161
|
assert.equal(entries[8], 'subdir2/file5.txt');
|
|
161
162
|
});
|
|
162
163
|
|
|
163
|
-
test('Cyrillic file names', () => {
|
|
164
|
+
test('Cyrillic file names', config('sync'), () => {
|
|
164
165
|
fs.writeFileSync('/мой-файл.txt', 'HELLO!', 'utf-8');
|
|
165
166
|
assert(fs.readdirSync('/').includes('мой-файл.txt'));
|
|
166
167
|
});
|
package/tests/fs/errors.test.ts
CHANGED
|
@@ -1,39 +1,159 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import test, { suite } from 'node:test';
|
|
4
|
-
import { fs } from '../common.js';
|
|
4
|
+
import { config, fs } from '../common.js';
|
|
5
5
|
|
|
6
6
|
const existingFile = '/exit.js';
|
|
7
|
+
const path = '/non-existent';
|
|
8
|
+
|
|
9
|
+
interface Case {
|
|
10
|
+
/** The name of the test */
|
|
11
|
+
name: string;
|
|
12
|
+
code: string;
|
|
13
|
+
/** The syscall the error should be attributed to, which is not always the name of the function */
|
|
14
|
+
syscall: string;
|
|
15
|
+
path: string;
|
|
16
|
+
dest?: string;
|
|
17
|
+
sync: (this: void) => unknown;
|
|
18
|
+
async: (this: void) => Promise<unknown>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const cases: Case[] = [
|
|
22
|
+
{
|
|
23
|
+
name: 'stat',
|
|
24
|
+
code: 'ENOENT',
|
|
25
|
+
syscall: 'stat',
|
|
26
|
+
path,
|
|
27
|
+
sync: () => fs.statSync(path),
|
|
28
|
+
async: () => fs.promises.stat(path),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'lstat',
|
|
32
|
+
code: 'ENOENT',
|
|
33
|
+
syscall: 'lstat',
|
|
34
|
+
path,
|
|
35
|
+
sync: () => fs.lstatSync(path),
|
|
36
|
+
async: () => fs.promises.lstat(path),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'mkdir',
|
|
40
|
+
code: 'EEXIST',
|
|
41
|
+
syscall: 'mkdir',
|
|
42
|
+
path: existingFile,
|
|
43
|
+
sync: () => fs.mkdirSync(existingFile, 0o666),
|
|
44
|
+
async: () => fs.promises.mkdir(existingFile, 0o666),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'rmdir (missing)',
|
|
48
|
+
code: 'ENOENT',
|
|
49
|
+
syscall: 'rmdir',
|
|
50
|
+
path,
|
|
51
|
+
sync: () => fs.rmdirSync(path),
|
|
52
|
+
async: () => fs.promises.rmdir(path),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'rmdir (not a directory)',
|
|
56
|
+
code: 'ENOTDIR',
|
|
57
|
+
syscall: 'rmdir',
|
|
58
|
+
path: existingFile,
|
|
59
|
+
sync: () => fs.rmdirSync(existingFile),
|
|
60
|
+
async: () => fs.promises.rmdir(existingFile),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'rename',
|
|
64
|
+
code: 'ENOENT',
|
|
65
|
+
syscall: 'rename',
|
|
66
|
+
path,
|
|
67
|
+
dest: '/foo',
|
|
68
|
+
sync: () => fs.renameSync(path, 'foo'),
|
|
69
|
+
async: () => fs.promises.rename(path, 'foo'),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'open',
|
|
73
|
+
code: 'ENOENT',
|
|
74
|
+
syscall: 'open',
|
|
75
|
+
path,
|
|
76
|
+
sync: () => fs.openSync(path, 'r'),
|
|
77
|
+
async: () => fs.promises.open(path, 'r'),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
// Note Node attributes `readdir` failures to `scandir`
|
|
81
|
+
name: 'readdir',
|
|
82
|
+
code: 'ENOENT',
|
|
83
|
+
syscall: 'scandir',
|
|
84
|
+
path,
|
|
85
|
+
sync: () => fs.readdirSync(path),
|
|
86
|
+
async: () => fs.promises.readdir(path),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'unlink',
|
|
90
|
+
code: 'ENOENT',
|
|
91
|
+
syscall: 'unlink',
|
|
92
|
+
path,
|
|
93
|
+
sync: () => fs.unlinkSync(path),
|
|
94
|
+
async: () => fs.promises.unlink(path),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
// Note `path` is the existing file and `dest` is the new link
|
|
98
|
+
name: 'link',
|
|
99
|
+
code: 'ENOENT',
|
|
100
|
+
syscall: 'link',
|
|
101
|
+
path,
|
|
102
|
+
dest: '/foo',
|
|
103
|
+
sync: () => fs.linkSync(path, 'foo'),
|
|
104
|
+
async: () => fs.promises.link(path, 'foo'),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'chmod',
|
|
108
|
+
code: 'ENOENT',
|
|
109
|
+
syscall: 'chmod',
|
|
110
|
+
path,
|
|
111
|
+
sync: () => fs.chmodSync(path, 0o666),
|
|
112
|
+
async: () => fs.promises.chmod(path, 0o666),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'readlink',
|
|
116
|
+
code: 'ENOENT',
|
|
117
|
+
syscall: 'readlink',
|
|
118
|
+
path,
|
|
119
|
+
sync: () => fs.readlinkSync(path),
|
|
120
|
+
async: () => fs.promises.readlink(path),
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Checks an error against the expected code, syscall, path, and message.
|
|
126
|
+
*
|
|
127
|
+
* The message is only matched at the start, since the description text for an errno
|
|
128
|
+
* is not standardized and implementations may add more information at the end.
|
|
129
|
+
*/
|
|
130
|
+
function check(error: unknown, c: Case): void {
|
|
131
|
+
const e = error as NodeJS.ErrnoException & { dest?: string };
|
|
132
|
+
|
|
133
|
+
assert.equal(e.code, c.code);
|
|
134
|
+
assert.equal(e.syscall, c.syscall);
|
|
135
|
+
assert.equal(e.path, c.path);
|
|
136
|
+
if (c.dest) assert.equal(e.dest, c.dest);
|
|
137
|
+
|
|
138
|
+
// e.g. `ENOENT: no such file or directory, stat '/non-existent'`
|
|
139
|
+
const expected = new RegExp(`^${c.code}: .+, ${c.syscall} '${c.path}'` + (c.dest ? ` -> '${c.dest}'` : ''));
|
|
140
|
+
assert.match(e.message, expected);
|
|
141
|
+
}
|
|
7
142
|
|
|
8
143
|
suite('Error messages', () => {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
test('chmod', async () => await assert.rejects(() => fs.promises.chmod(path, 0o666), missing));
|
|
25
|
-
test('lstat', async () => await assert.rejects(() => fs.promises.lstat(path), missing));
|
|
26
|
-
test('readlink', async () => await assert.rejects(() => fs.promises.readlink(path), missing));
|
|
27
|
-
test('statSync', () => assert.throws(() => fs.statSync(path), missing));
|
|
28
|
-
test('mkdirSync', () => assert.throws(() => fs.mkdirSync(existingFile, 0o666), existing));
|
|
29
|
-
test('rmdirSync', () => assert.throws(() => fs.rmdirSync(path), missing));
|
|
30
|
-
test('rmdirSync', () => assert.throws(() => fs.rmdirSync(existingFile), notDir));
|
|
31
|
-
test('renameSync', () => assert.throws(() => fs.renameSync(path, 'foo'), missing));
|
|
32
|
-
test('openSync', () => assert.throws(() => fs.openSync(path, 'r'), missing));
|
|
33
|
-
test('readdirSync', () => assert.throws(() => fs.readdirSync(path), missing));
|
|
34
|
-
test('unlinkSync', () => assert.throws(() => fs.unlinkSync(path), missing));
|
|
35
|
-
test('linkSync', () => assert.throws(() => fs.linkSync(path, 'foo'), { code: 'ENOENT', path: '/foo' }));
|
|
36
|
-
test('chmodSync', () => assert.throws(() => fs.chmodSync(path, 0o666), missing));
|
|
37
|
-
test('lstatSync', () => assert.throws(() => fs.lstatSync(path), missing));
|
|
38
|
-
test('readlinkSync', () => assert.throws(() => fs.readlinkSync(path), missing));
|
|
144
|
+
for (const c of cases) {
|
|
145
|
+
test(c.name, config('async'), async () => {
|
|
146
|
+
await assert.rejects(c.async, (error: unknown) => {
|
|
147
|
+
check(error, c);
|
|
148
|
+
return true;
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test(c.name + 'Sync', config('sync'), () => {
|
|
153
|
+
assert.throws(c.sync, (error: unknown) => {
|
|
154
|
+
check(error, c);
|
|
155
|
+
return true;
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
39
159
|
});
|
package/tests/fs/exists.test.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { suite, test } from 'node:test';
|
|
4
|
-
import { fs } from '../common.js';
|
|
4
|
+
import { config, fs } from '../common.js';
|
|
5
5
|
|
|
6
6
|
suite('exists', () => {
|
|
7
7
|
const f = 'x.txt';
|
|
8
8
|
|
|
9
|
-
test('return true for an existing file', async () => {
|
|
9
|
+
test('return true for an existing file', config('async'), async () => {
|
|
10
10
|
const exists = await fs.promises.exists(f);
|
|
11
11
|
assert(exists);
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
test('return false for a non-existent file', async () => {
|
|
14
|
+
test('return false for a non-existent file', config('async'), async () => {
|
|
15
15
|
const exists = await fs.promises.exists(f + '-NO');
|
|
16
16
|
assert(!exists);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
test('have sync methods that behave the same', () => {
|
|
19
|
+
test('have sync methods that behave the same', config('sync'), () => {
|
|
20
20
|
assert(fs.existsSync(f));
|
|
21
21
|
assert(!fs.existsSync(f + '-NO'));
|
|
22
22
|
});
|
package/tests/fs/links.test.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
2
|
import { join } from '@zenfs/core/path';
|
|
3
|
-
import type { Exception } from 'kerium';
|
|
4
3
|
import assert from 'node:assert/strict';
|
|
5
4
|
import { suite, test } from 'node:test';
|
|
6
|
-
import { fs } from '../common.js';
|
|
5
|
+
import { config, fs } from '../common.js';
|
|
7
6
|
|
|
8
|
-
suite('Links', () => {
|
|
7
|
+
suite('Links', config('symlinks'), () => {
|
|
9
8
|
const target = '/a1.js',
|
|
10
9
|
symlink = 'symlink1.js',
|
|
11
10
|
hardlink = 'link1.js';
|
|
@@ -69,14 +68,8 @@ suite('Links', () => {
|
|
|
69
68
|
assert(await fs.promises.exists(target));
|
|
70
69
|
});
|
|
71
70
|
|
|
72
|
-
test('link', async
|
|
73
|
-
|
|
74
|
-
if (e.code == 'ENOSYS') return e;
|
|
75
|
-
throw e;
|
|
76
|
-
});
|
|
77
|
-
if (_) {
|
|
78
|
-
return t.skip('Backend does not support hard links');
|
|
79
|
-
}
|
|
71
|
+
test('link', config('links'), async () => {
|
|
72
|
+
await fs.promises.link(target, hardlink);
|
|
80
73
|
const targetContent = await fs.promises.readFile(target, 'utf8');
|
|
81
74
|
const linkContent = await fs.promises.readFile(hardlink, 'utf8');
|
|
82
75
|
assert.equal(targetContent, linkContent);
|
package/tests/fs/open.test.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { suite, test } from 'node:test';
|
|
4
|
-
import { fs } from '../common.js';
|
|
4
|
+
import { config, fs } from '../common.js';
|
|
5
5
|
|
|
6
6
|
suite('Opening files', () => {
|
|
7
7
|
const filename = 'a.js';
|
|
8
8
|
|
|
9
|
-
test('throw ENOENT when opening non-existent file', async () => {
|
|
9
|
+
test('throw ENOENT when opening non-existent file', config('sync', 'async'), async () => {
|
|
10
10
|
assert.throws(() => fs.openSync('/path/to/file/that/does/not/exist', 'r'), { code: 'ENOENT' });
|
|
11
11
|
await assert.rejects(fs.promises.open('/path/to/file/that/does/not/exist', 'r'), { code: 'ENOENT' });
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
test('open file with mode "r"', async () => {
|
|
14
|
+
test('open file with mode "r"', config('async'), async () => {
|
|
15
15
|
const { fd } = await fs.promises.open(filename, 'r');
|
|
16
16
|
assert(fd >= -Infinity);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
test('open file with mode "rs"', async () => {
|
|
19
|
+
test('open file with mode "rs"', config('async'), async () => {
|
|
20
20
|
const { fd } = await fs.promises.open(filename, 'rs');
|
|
21
21
|
assert(fd >= -Infinity);
|
|
22
22
|
});
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
|
-
import {
|
|
2
|
+
import { R_OK, W_OK, X_OK } from '@zenfs/core/constants';
|
|
3
|
+
import { defaultContext } from '@zenfs/core/internal/contexts.js';
|
|
4
|
+
import { hasAccess } from '@zenfs/core/internal/inode.js';
|
|
5
|
+
import { join } from '@zenfs/core/path';
|
|
6
|
+
import type { Exception } from 'kerium';
|
|
3
7
|
import assert from 'node:assert/strict';
|
|
4
8
|
import { suite, test } from 'node:test';
|
|
5
9
|
import { encodeUTF8 } from 'utilium';
|
|
6
|
-
import {
|
|
7
|
-
import { join } from '@zenfs/core/path';
|
|
8
|
-
import { R_OK, W_OK, X_OK } from '@zenfs/core/constants';
|
|
9
|
-
import { fs } from '../common.js';
|
|
10
|
+
import { config, fs } from '../common.js';
|
|
10
11
|
|
|
11
12
|
const asyncMode = 0o777;
|
|
12
13
|
const syncMode = 0o644;
|
|
13
14
|
const file = 'a.js';
|
|
14
15
|
|
|
15
|
-
suite('Permissions', () => {
|
|
16
|
-
test('chmod', async () => {
|
|
16
|
+
suite('Permissions', config('permissions'), () => {
|
|
17
|
+
test('chmod', config('sync', 'async'), async () => {
|
|
17
18
|
await fs.promises.chmod(file, asyncMode.toString(8));
|
|
18
19
|
|
|
19
20
|
const stats = await fs.promises.stat(file);
|
|
@@ -23,7 +24,7 @@ suite('Permissions', () => {
|
|
|
23
24
|
assert.equal(fs.statSync(file).mode & 0o777, syncMode);
|
|
24
25
|
});
|
|
25
26
|
|
|
26
|
-
test('fchmod', async () => {
|
|
27
|
+
test('fchmod', config('sync', 'async'), async () => {
|
|
27
28
|
const handle = await fs.promises.open(file, 'a', 0o644);
|
|
28
29
|
|
|
29
30
|
await handle.chmod(asyncMode);
|
|
@@ -35,7 +36,7 @@ suite('Permissions', () => {
|
|
|
35
36
|
assert.equal(fs.statSync(file).mode & 0o777, syncMode);
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
test('lchmod', async () => {
|
|
39
|
+
test('lchmod', config('lchmod', 'async'), async () => {
|
|
39
40
|
const link = 'symbolic-link';
|
|
40
41
|
|
|
41
42
|
await fs.promises.symlink(file, link);
|
|
@@ -50,16 +51,15 @@ suite('Permissions', () => {
|
|
|
50
51
|
|
|
51
52
|
async function test_item(path: string): Promise<void> {
|
|
52
53
|
const stats = await fs.promises.stat(path).catch((error: Exception) => {
|
|
53
|
-
assert(error instanceof Exception);
|
|
54
54
|
assert.equal(error.code, 'EACCES');
|
|
55
55
|
});
|
|
56
56
|
if (!stats) return;
|
|
57
|
-
assert(
|
|
57
|
+
assert(hasAccess(defaultContext, stats, X_OK));
|
|
58
58
|
|
|
59
59
|
function checkError(access: number) {
|
|
60
60
|
return function (error: Exception) {
|
|
61
|
-
assert(error instanceof
|
|
62
|
-
assert(!
|
|
61
|
+
assert(error instanceof Error);
|
|
62
|
+
assert(!hasAccess(defaultContext, stats!, access));
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -70,7 +70,7 @@ suite('Permissions', () => {
|
|
|
70
70
|
} else {
|
|
71
71
|
await fs.promises.readFile(path).catch(checkError(R_OK));
|
|
72
72
|
}
|
|
73
|
-
assert(
|
|
73
|
+
assert(hasAccess(defaultContext, stats, R_OK));
|
|
74
74
|
|
|
75
75
|
if (stats.isDirectory()) {
|
|
76
76
|
const testFile = join(path, '__test_file_plz_ignore.txt');
|
|
@@ -81,7 +81,7 @@ suite('Permissions', () => {
|
|
|
81
81
|
if (!handle) return;
|
|
82
82
|
await handle.close();
|
|
83
83
|
}
|
|
84
|
-
assert(
|
|
84
|
+
assert(hasAccess(defaultContext, stats, W_OK));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const copy = { ...defaultContext.credentials };
|
package/tests/fs/read.test.ts
CHANGED
|
@@ -5,21 +5,21 @@ import assert from 'node:assert/strict';
|
|
|
5
5
|
import type { OpenMode, PathLike } from 'node:fs';
|
|
6
6
|
import { suite, test } from 'node:test';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
|
-
import { fs } from '../common.js';
|
|
8
|
+
import { config, fs } from '../common.js';
|
|
9
9
|
|
|
10
10
|
const filepath = 'x.txt';
|
|
11
11
|
const expected = 'xyz\n';
|
|
12
12
|
const ellipses = '…'.repeat(10_000);
|
|
13
13
|
|
|
14
14
|
suite('read', () => {
|
|
15
|
-
test('read file asynchronously', async () => {
|
|
15
|
+
test('read file asynchronously', config('async'), async () => {
|
|
16
16
|
const handle = await fs.promises.open(filepath, 'r');
|
|
17
17
|
const { bytesRead, buffer } = await handle.read(Buffer.alloc(expected.length), 0, expected.length, 0);
|
|
18
18
|
assert.equal(bytesRead, expected.length);
|
|
19
19
|
assert.equal(buffer.toString(), expected);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
test('read file synchronously', () => {
|
|
22
|
+
test('read file synchronously', config('sync'), () => {
|
|
23
23
|
const fd = fs.openSync(filepath, 'r');
|
|
24
24
|
const buffer = Buffer.alloc(expected.length);
|
|
25
25
|
const bytesRead = fs.readSync(fd, buffer, 0, expected.length, 0);
|
|
@@ -28,14 +28,14 @@ suite('read', () => {
|
|
|
28
28
|
assert.equal(buffer.toString(), expected);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
test('Read a file and check its binary bytes asynchronously', async () => {
|
|
31
|
+
test('Read a file and check its binary bytes asynchronously', config('async'), async () => {
|
|
32
32
|
const buff = await fs.promises.readFile('elipses.txt');
|
|
33
33
|
assert.equal(buff.length, 30_000);
|
|
34
34
|
assert.equal(buff.toString(), ellipses);
|
|
35
35
|
assert.equal((buff[1] << 8) | buff[0], 32994);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
test('Read a file and check its binary bytes synchronously', () => {
|
|
38
|
+
test('Read a file and check its binary bytes synchronously', config('sync'), () => {
|
|
39
39
|
const buff = fs.readFileSync('elipses.txt');
|
|
40
40
|
assert.equal(buff.length, 30_000);
|
|
41
41
|
assert.equal(buff.toString(), ellipses);
|
|
@@ -45,7 +45,7 @@ suite('read', () => {
|
|
|
45
45
|
const bufferAsync = Buffer.alloc(expected.length);
|
|
46
46
|
const bufferSync = Buffer.alloc(expected.length);
|
|
47
47
|
|
|
48
|
-
test('read file from handle asynchronously', async () => {
|
|
48
|
+
test('read file from handle asynchronously', config('async'), async () => {
|
|
49
49
|
const handle = await fs.promises.open(filepath, 'r');
|
|
50
50
|
const { bytesRead } = await handle.read(bufferAsync, 0, expected.length, 0);
|
|
51
51
|
|
|
@@ -53,7 +53,7 @@ suite('read', () => {
|
|
|
53
53
|
assert.equal(bufferAsync.toString(), expected);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
test('read file from handle synchronously', () => {
|
|
56
|
+
test('read file from handle synchronously', config('sync'), () => {
|
|
57
57
|
const fd = fs.openSync(filepath, 'r');
|
|
58
58
|
const bytesRead = fs.readSync(fd, bufferSync, 0, expected.length, 0);
|
|
59
59
|
|
|
@@ -61,7 +61,7 @@ suite('read', () => {
|
|
|
61
61
|
assert.equal(bytesRead, expected.length);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
test('read file synchronously to non-zero offset', () => {
|
|
64
|
+
test('read file synchronously to non-zero offset', config('sync'), () => {
|
|
65
65
|
const fd = fs.openSync(filepath, 'r');
|
|
66
66
|
const buffer = Buffer.alloc(expected.length + 10);
|
|
67
67
|
const bytesRead = fs.readSync(fd, buffer, 10, expected.length, 0);
|
|
@@ -70,7 +70,7 @@ suite('read', () => {
|
|
|
70
70
|
assert.equal(bytesRead, expected.length);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
test('read using callback API #239', async () => {
|
|
73
|
+
test('read using callback API #239', config('sync', 'write'), async () => {
|
|
74
74
|
const path = '/text.txt';
|
|
75
75
|
|
|
76
76
|
fs.writeFileSync(path, 'hello world');
|