From 7c203bc5c9288b51c7cd320df2f60c54cd377c2c Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Wed, 1 Apr 2026 21:57:42 +0100 Subject: [PATCH] Fix mock client event triggering --- frontend/test-client/src/agent/mock-agent.ts | 41 ++---------- frontend/test-client/src/agent/mock-client.ts | 66 +++++++------------ 2 files changed, 29 insertions(+), 78 deletions(-) diff --git a/frontend/test-client/src/agent/mock-agent.ts b/frontend/test-client/src/agent/mock-agent.ts index 8d393a1c..308c5441 100644 --- a/frontend/test-client/src/agent/mock-agent.ts +++ b/frontend/test-client/src/agent/mock-agent.ts @@ -307,7 +307,7 @@ export class MockAgent extends MockClient { ); if (fileContent.split(content).length > 2) { if (this.useSlowFileEvents) { - logger.warn( + this.client.logger.warn( `Content ${content} (of ${this.name}) found more than once in '${file}'. File content:\n${fileContent}` ); } else { @@ -369,9 +369,8 @@ export class MockAgent extends MockClient { `Decided to create file ${file} with content ${content}` ); - return this.create(file, new TextEncoder().encode(` ${content} `), { - ignoreSlowFileEvents: true - }); + + return this.write(file, new TextEncoder().encode(` ${content} `),); } // Binary file creation — exercises the putBinary server path (not in mergeable_file_extensions) @@ -388,12 +387,10 @@ export class MockAgent extends MockClient { const { uuid, bytes } = this.getBinaryContent(); this.client.logger.info( - `Decided to create binary file ${file}` + `Decided to create binary file ${file}: ${uuid}` ); - return this.create(file, bytes, { - ignoreSlowFileEvents: true - }); + return this.write(file, bytes,); } private async disableSyncAction(): Promise { @@ -450,14 +447,6 @@ export class MockAgent extends MockClient { this.client.logger.info(`Renamed file: ${file} -> ${newName}`); await this.rename(file, newName); - this.executeFileOperation( - async () => - this.client.syncLocallyUpdatedFile({ - oldPath: file, - relativePath: newName - }), - true - ); } private async updateFileAction(): Promise { @@ -495,13 +484,6 @@ export class MockAgent extends MockClient { }) ); - this.executeFileOperation( - async () => - this.client.syncLocallyUpdatedFile({ - relativePath: file - }), - true - ); } private async updateBinaryFileAction(): Promise { @@ -530,13 +512,7 @@ export class MockAgent extends MockClient { this.doNotTouchWhileOffline.push(file); this.files.set(file, bytes); - this.executeFileOperation( - async () => - this.client.syncLocallyUpdatedFile({ - relativePath: file - }), - true - ); + } private async deleteFileAction(): Promise { @@ -554,10 +530,7 @@ export class MockAgent extends MockClient { `Deleting file: ${file} with:\n content '${new TextDecoder().decode(this.files.get(file))}'` ); await this.delete(file); - this.executeFileOperation( - async () => this.client.syncLocallyDeletedFile(file), - true - ); + } private getContent(): string { diff --git a/frontend/test-client/src/agent/mock-client.ts b/frontend/test-client/src/agent/mock-client.ts index 3cdceb04..145cecd0 100644 --- a/frontend/test-client/src/agent/mock-client.ts +++ b/frontend/test-client/src/agent/mock-client.ts @@ -40,37 +40,22 @@ export class MockClient extends debugging.InMemoryFileSystem { await this.client.start(); } - public async create( - path: RelativePath, - newContent: Uint8Array, - { ignoreSlowFileEvents }: { ignoreSlowFileEvents: boolean } = { - ignoreSlowFileEvents: false - } - ): Promise { - if (this.files.has(path)) { - throw new Error(`File ${path} already exists`); - } - this.client.logger.info( - `Creating file ${path} with content ${new TextDecoder().decode(newContent)}` - ); - this.files.set(path, newContent); + public override async write( + path: RelativePath, + content: Uint8Array + ): Promise { + this.files.set(path, content); this.executeFileOperation( - async () => this.client.syncLocallyCreatedFile(path), - ignoreSlowFileEvents + async () => this.client.syncLocallyUpdatedFile({ relativePath: path }), ); + } public override async atomicUpdateText( path: RelativePath, updater: (currentContent: TextWithCursors) => TextWithCursors ): Promise { - // This method is called by BOTH the sync client (for remote text - // merges) and the test agent (for user updates). We must NOT call - // executeFileOperation here because the sync-client path would - // echo remote writes back as local modifications, creating an - // infinite sync loop. The test agent calls executeFileOperation - // separately after this method returns. const file = this.files.get(path); if (!file) { throw new Error(`File ${path} does not exist`); @@ -80,38 +65,26 @@ export class MockClient extends debugging.InMemoryFileSystem { const newContentUint8Array = new TextEncoder().encode(newContent); this.files.set(path, newContentUint8Array); + this.executeFileOperation( + async () => this.client.syncLocallyUpdatedFile({ relativePath: path }), + ); + return newContent; } - public override async write( - path: RelativePath, - content: Uint8Array - ): Promise { - // This method is called by the sync client when writing files - // received from the server (remote updates). Do NOT call - // executeFileOperation here — that would echo the remote write - // back as a local modification, creating an infinite sync loop. - // User-initiated writes go through create(), atomicUpdateText(), - // or direct files.set() + executeFileOperation() in mock-agent. - this.files.set(path, content); - } + public override async delete(path: RelativePath): Promise { - // Just perform the filesystem operation. The test agent calls - // executeFileOperation separately in mock-agent.ts. Not echoing - // here prevents the sync client's remote-delete writes from - // triggering spurious local-delete sync operations. this.files.delete(path); + this.executeFileOperation( + async () => this.client.syncLocallyDeletedFile(path), + ); } public override async rename( oldPath: RelativePath, newPath: RelativePath ): Promise { - // Just perform the filesystem operation. The test agent calls - // executeFileOperation separately in mock-agent.ts. Not echoing - // here prevents the sync client's ensureClearPath / remote-rename - // writes from triggering spurious local-update sync operations. const file = this.files.get(oldPath); if (!file) { throw new Error(`File ${oldPath} does not exist`); @@ -120,13 +93,18 @@ export class MockClient extends debugging.InMemoryFileSystem { if (oldPath !== newPath) { this.files.delete(oldPath); } + this.executeFileOperation( + async () => this.client.syncLocallyUpdatedFile({ + oldPath, + relativePath: newPath + }), + ); } protected executeFileOperation( callback: () => unknown, - ignoreSlowFileEvents = false ): void { - if (this.useSlowFileEvents && !ignoreSlowFileEvents) { + if (this.useSlowFileEvents) { // we aren't the best client and it takes some time to notice changes setTimeout(callback, Math.random() * 100); } else {