mirror of
https://github.com/n8n-io/n8n.git
synced 2026-08-28 17:22:01 +08:00
test(core): Support tool-result cleanup in the eval stub workspace (no-changelog) (#37202)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,35 @@ describe('createStubWorkspace', () => {
|
||||
await expect(filesystem().readFile('missing.ts')).rejects.toThrow('No such file');
|
||||
});
|
||||
|
||||
// The runtime offloads oversized tool results to `tool-results/runs/<hash>/`
|
||||
// and clears that directory at the end of every run, calling the filesystem
|
||||
// directly rather than through the exposed tools.
|
||||
describe('tool-result cleanup', () => {
|
||||
const runDirectory = 'tool-results/runs/run-hash';
|
||||
|
||||
it('reports a directory as existing once a file sits under it', async () => {
|
||||
const fs = filesystem();
|
||||
await fs.writeFile(`${runDirectory}/call.result.json`, '{}');
|
||||
|
||||
await expect(fs.exists(runDirectory)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('reports a directory that was never written as missing', async () => {
|
||||
await expect(filesystem().exists(runDirectory)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('removes every file under the directory it is given, and nothing else', async () => {
|
||||
const fs = filesystem();
|
||||
await fs.writeFile(`${runDirectory}/call.result.json`, '{}');
|
||||
await fs.writeFile('tool-results/runs/other-hash/call.result.json', '{}');
|
||||
|
||||
await fs.rmdir(runDirectory);
|
||||
|
||||
await expect(fs.exists(runDirectory)).resolves.toBe(false);
|
||||
await expect(fs.exists('tool-results/runs/other-hash')).resolves.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('offers only the tools production exposes', () => {
|
||||
expect(
|
||||
createStubWorkspace()
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
// Without a workspace, `build-workflow` fails its source read with a
|
||||
// `code_fixable` remediation telling the agent to write the file with
|
||||
// `workspace_write_file` — a tool that only exists when a workspace is attached.
|
||||
//
|
||||
// Tool exposure is filtered to CORE_WORKSPACE_TOOL_NAMES, but the runtime also
|
||||
// calls the filesystem directly for its own bookkeeping — offloading oversized
|
||||
// tool results and clearing them at the end of every run — so those operations
|
||||
// are implemented rather than rejected.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
import {
|
||||
@@ -58,13 +63,29 @@ class InMemoryWorkspaceFilesystem implements WorkspaceFilesystem {
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
async exists(path: string): Promise<boolean> {
|
||||
const key = relativePath(path);
|
||||
if (this.files.has(key)) return await Promise.resolve(true);
|
||||
const prefix = `${key}/`;
|
||||
for (const file of this.files.keys()) {
|
||||
if (file.startsWith(prefix)) return await Promise.resolve(true);
|
||||
}
|
||||
return await Promise.resolve(false);
|
||||
}
|
||||
|
||||
async rmdir(path: string): Promise<void> {
|
||||
const prefix = `${relativePath(path)}/`;
|
||||
for (const file of this.files.keys()) {
|
||||
if (file.startsWith(prefix)) this.files.delete(file);
|
||||
}
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
appendFile = unreachable;
|
||||
deleteFile = unreachable;
|
||||
copyFile = unreachable;
|
||||
moveFile = unreachable;
|
||||
rmdir = unreachable;
|
||||
readdir = unreachable;
|
||||
exists = unreachable;
|
||||
stat = unreachable;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user