diff --git a/packages/server/src/server/daemon-e2e/filesystem.e2e.test.ts b/packages/server/src/server/daemon-e2e/filesystem.e2e.test.ts index 140feaa569..fae56c0ec7 100644 --- a/packages/server/src/server/daemon-e2e/filesystem.e2e.test.ts +++ b/packages/server/src/server/daemon-e2e/filesystem.e2e.test.ts @@ -23,6 +23,71 @@ afterEach(async () => { }, 60000); describe("file explorer", () => { + test.each([ + { description: "a long ASCII name", fileName: `${"a".repeat(236)}.txt` }, + { description: "an ordinary name", fileName: "notes.txt" }, + { description: "a long Unicode name", fileName: `${"文".repeat(76)}.txt` }, + ])("saves text files with $description", async ({ fileName }) => { + const cwd = tmpCwd(); + try { + const created = await ctx.client.createFileEntry({ + cwd, + parentPath: ".", + name: fileName, + kind: "file", + }); + expect(created).toMatchObject({ path: fileName, success: true, error: null }); + writeFileSync(path.join(cwd, fileName), "original payload\n"); + const initial = await ctx.client.readFile(cwd, fileName); + + const saved = await ctx.client.writeFile({ + cwd, + path: fileName, + content: "saved payload\n", + expectedModifiedAt: initial.modifiedAt, + expectedRevision: initial.revision, + }); + expect(saved).toMatchObject({ status: "written", size: 14 }); + const after = await ctx.client.readFile(cwd, fileName); + expect(after.path).toBe(fileName); + expect(new TextDecoder().decode(after.bytes)).toBe("saved payload\n"); + + const staleSave = await ctx.client.writeFile({ + cwd, + path: fileName, + content: "stale payload\n", + expectedModifiedAt: initial.modifiedAt, + expectedRevision: initial.revision, + }); + expect(staleSave).toMatchObject({ + status: "conflict", + version: { + status: "ready", + path: fileName, + size: after.size, + modifiedAt: after.modifiedAt, + revision: after.revision, + }, + }); + const afterConflict = await ctx.client.readFile(cwd, fileName); + expect(new TextDecoder().decode(afterConflict.bytes)).toBe("saved payload\n"); + + const secondSave = await ctx.client.writeFile({ + cwd, + path: fileName, + content: "second payload\n", + expectedModifiedAt: afterConflict.modifiedAt, + expectedRevision: afterConflict.revision, + }); + expect(secondSave).toMatchObject({ status: "written", size: 15 }); + const final = await ctx.client.readFile(cwd, fileName); + expect(new TextDecoder().decode(final.bytes)).toBe("second payload\n"); + expect(readdirSync(cwd)).toEqual([fileName]); + } finally { + rmSync(cwd, { recursive: true, force: true }); + } + }); + test.each([ { query: "implementation", diff --git a/packages/server/src/server/file-explorer/service.ts b/packages/server/src/server/file-explorer/service.ts index 5421450c24..b371a5e4af 100644 --- a/packages/server/src/server/file-explorer/service.ts +++ b/packages/server/src/server/file-explorer/service.ts @@ -489,7 +489,7 @@ export async function writeExplorerFile({ const temporaryPath = path.join( path.dirname(filePath.resolvedPath), - `.${path.basename(filePath.resolvedPath)}.paseo-${randomUUID()}.tmp`, + `.paseo-${randomUUID()}.tmp`, ); let temporaryHandle: FileHandle | null = null; try {