fs: adapt to different mkdir api, block write to root dir, add tests

This commit is contained in:
XiaochenCui
2025-07-21 12:45:30 -07:00
committed by Eric Dubé
parent 6d09566ba7
commit f906c1ebad
3 changed files with 136 additions and 8 deletions
@@ -271,8 +271,32 @@ class HLMkdir extends HLFilesystemOperation {
});
}
// Unify the following formats:
// - full path: {"path":"/foo/bar", args...}, used by apitest (./tools/api-tester/apitest.js)
// - parent + path: {"parent": "/foo", "path":"bar", args...}, used by puter-js (puter.fs.mkdir("/foo/bar"))
if ( !values.parent && values.path ) {
values.parent = await fs.node(new NodePathSelector(_path.dirname(values.path)));
values.path = _path.basename(values.path);
}
let parent_node = values.parent || await fs.node(new RootNodeSelector());
if ( parent_node.isRoot ) {
// root directory is read-only
throw APIError.create('forbidden', null, {
message: 'Cannot create directories in the root directory.'
});
}
console.log('USING PARENT', parent_node.selector.describe());
// TODO: this can be removed upon completion of: https://github.com/HeyPuter/puter/issues/1352
if ( parent_node.isRoot ) {
// root directory is read-only
throw APIError.create('forbidden', null, {
message: 'Cannot create directories in the root directory.'
});
}
let target_basename = _path.basename(values.path);
// "top_parent" is the immediate parent of the target directory
@@ -282,14 +306,6 @@ class HLMkdir extends HLFilesystemOperation {
: await this._get_existing_top_parent({ top_parent: parent_node })
;
// TODO: this can be removed upon completion of: https://github.com/HeyPuter/puter/issues/1352
if ( top_parent.isRoot ) {
// root directory is read-only
throw APIError.create('forbidden', null, {
message: 'Cannot create directories in the root directory.'
});
}
// `parent_node` becomes the parent of the last directory name
// specified under `path`.
parent_node = await this._create_parents({
+10
View File
@@ -188,6 +188,16 @@ module.exports = class TestSDK {
});
return res.data;
};
// parent + path format: {"parent": "/foo", "path":"bar", args...}
// this is used by puter-js (puter.fs.mkdir("/foo/bar"))
this.mkdir_v2 = async (parent, path, opts) => {
const res = await this.post('mkdir', {
parent: p(parent),
path: p(path),
...(opts ?? {})
});
return res.data;
}
this.write = async (path, bin, params) => {
path = p(path);
params = params ?? {};
+102
View File
@@ -65,5 +65,107 @@ module.exports = {
expect(stat.name).equal(`a (${i})`);
}
});
await t.case('mkdir in root directory is prohibited', async () => {
const path = '/a';
await t.case('throws 403', async () => {
try {
// full path format: {"path":"/foo/bar", args...}
await t.mkdir(path);
} catch (e) {
expect(e.response.status).equal(403);
}
try {
// parent + path format: {"parent": "/foo", "path":"bar", args...}
const parent = '/';
await t.mkdir(path, {
parent: parent,
});
} catch (e) {
expect(e.response.status).equal(403);
}
});
});
await t.case('create_missing_parents works (full path api)', async () => {
const path = 'a/b/c';
await t.case('parent directory does not exist', async () => {
try {
await t.stat('a');
} catch (e) {
expect(e.response.status).equal(404);
}
});
await t.case('mkdir failed without create_missing_parents', async () => {
try {
await t.mkdir('a/b/c');
} catch (e) {
expect(e.response.status).equal(409);
}
});
await t.case('mkdir succeeds with create_missing_parents', async () => {
const result = await t.mkdir('a/b/c', {
create_missing_parents: true,
});
expect(result.name).equal('c');
});
await t.case('can stat the directory', async () => {
const stat = await t.stat(path);
expect(stat.name).equal('c');
});
await t.case('can stat the parent directory', async () => {
let stat = await t.stat('a');
expect(stat.name).equal('a');
stat = await t.stat('a/b');
expect(stat.name).equal('b');
});
});
await t.case('create_missing_parents works (parent + path api)', async () => {
const path = 'a/b/c';
await t.case('parent directory does not exist', async () => {
try {
await t.stat('a');
} catch (e) {
expect(e.response.status).equal(404);
}
});
await t.case('mkdir failed without create_missing_parents', async () => {
try {
await t.mkdir_v2('a/b', 'c');
} catch (e) {
expect(e.response.status).equal(409);
}
});
await t.case('mkdir succeeds with create_missing_parents', async () => {
const result = await t.mkdir_v2('a/b', 'c', {
create_missing_parents: true,
});
expect(result.name).equal('c');
});
await t.case('can stat the directory', async () => {
const stat = await t.stat(path);
expect(stat.name).equal('c');
});
await t.case('can stat the parent directory', async () => {
let stat = await t.stat('a');
expect(stat.name).equal('a');
stat = await t.stat('a/b');
expect(stat.name).equal('b');
});
});
}
};