fs/mkdir: add docs spec, update tests, update apitest

This commit is contained in:
XiaochenCui
2025-07-21 15:21:11 -07:00
committed by Eric Dubé
parent 7e20039ef4
commit fee100dddd
3 changed files with 119 additions and 34 deletions

View File

@@ -0,0 +1,69 @@
# Filesystem API
Filesystem endpoints allow operations on files and directories in the Puter filesystem.
## POST `/mkdir` (auth required)
### Description
Creates a new directory in the filesystem. Currently support 2 formats:
- Full path: `{"path": "/foo/bar", args ...}` — this API is used by apitest (`./tools/api-tester/apitest.js`) and aligns more closely with the POSIX spec (https://linux.die.net/man/3/mkdir)
- Parent + path: `{"parent": "/foo", "path": "bar", args ...}` — this API is used by `puter-js` via `puter.fs.mkdir`
A future work would be use a unified format for all filesystem operations.
### Parameters
- **path** _- required_
- **accepts:** `string`
- **description:** The path where the directory should be created
- **notes:** Cannot be empty, null, or undefined
- **parent** _- optional_
- **accepts:** `string | UUID`
- **description:** The parent directory path or UUID
- **notes:** If not provided, path is treated as full path
- **overwrite** _- optional_
- **accepts:** `boolean`
- **default:** `false`
- **description:** Whether to overwrite existing files/directories
- **dedupe_name** _- optional_
- **accepts:** `boolean`
- **default:** `false`
- **description:** Whether to automatically rename if name exists
- **create_missing_parents** _- optional_
- **accepts:** `boolean`
- **default:** `false`
- **description:** Whether to create parent directories if they don't exist
- **aliases:** `create_missing_ancestors`
- **shortcut_to** _- optional_
- **accepts:** `string | UUID`
- **description:** Creates a shortcut/symlink to the specified target
### Example
```json
{
"path": "/user/Desktop/new-directory"
}
```
```json
{
"parent": "/user",
"path": "Desktop/new-directory"
}
```
### Response
Returns the created directory's metadata including name, path, uid, and any parent directories created.
## Other Filesystem Endpoints
[Additional endpoints would be documented here...]

View File

@@ -96,6 +96,12 @@ module.exports = class TestSDK {
async case (id, fn) {
this.nameStack.push(id);
// Always reset cwd for top-level cases to prevent them from affecting
// each other.
if (this.nameStack.length === 1) {
this.resetCwd();
}
const tabs = Array(this.nameStack.length - 2).fill(' ').join('');
const strid = tabs + this.nameStack.join(` \x1B[36;1m->\x1B[0m `);
process.stdout.write(strid + ' ... \n');
@@ -167,6 +173,12 @@ module.exports = class TestSDK {
cd (path) {
this.cwd = path_.posix.join(this.cwd, path);
}
resetCwd () {
// TODO (xiaochen): update the hardcoded path to a global constant
this.cwd = '/admin/api_test';
}
resolve (path) {
if ( path.startsWith('$') ) return path;
if ( path.startsWith('/') ) return path;

View File

@@ -88,48 +88,50 @@ module.exports = {
});
});
await t.case('create_missing_parents works (full path api)', async () => {
const path = 'a/b/c';
await t.case('full path api', async () => {
t.cd('full_path_api');
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('create_missing_parents works', async () => {
t.cd('create_missing_parents_works');
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,
await t.case('parent directory does not exist', async () => {
try {
await t.stat('a');
} catch (e) {
expect(e.response.status).equal(404);
}
});
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('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 parent directory', async () => {
let stat = await t.stat('a');
expect(stat.name).equal('a');
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);
}
});
stat = await t.stat('a/b');
expect(stat.name).equal('b');
await t.case('can stat all directories along the path', async () => {
let stat = await t.stat('a');
expect(stat.name).equal('a');
stat = await t.stat('a/b');
expect(stat.name).equal('b');
stat = await t.stat('a/b/c');
expect(stat.name).equal('c');
});
});
});
await t.case('create_missing_parents works (parent + path api)', async () => {
const path = 'a/b/c';
await t.case('parent + path api', async () => {
t.cd('parent_path_api');
await t.case('parent directory does not exist', async () => {
try {
@@ -143,7 +145,9 @@ module.exports = {
try {
await t.mkdir_v2('a/b', 'c');
} catch (e) {
expect(e.response.status).equal(409);
// TODO (xiaochen): `t.mkdir('a/b/c')` throws 409, unify the
// behavior of these two cases.
expect(e.response.status).equal(422);
}
});