From 2d91361a1bf54f047a224ca5490771e2d044c97d Mon Sep 17 00:00:00 2001 From: Nathan Date: Fri, 22 May 2026 20:56:17 +0800 Subject: [PATCH] Fix batch sync protobuf request body --- .../http/__tests__/collab-api.test.ts | 54 +++++++++++++++++++ .../services/js-services/http/collab-api.ts | 3 ++ 2 files changed, 57 insertions(+) create mode 100644 src/application/services/js-services/http/__tests__/collab-api.test.ts diff --git a/src/application/services/js-services/http/__tests__/collab-api.test.ts b/src/application/services/js-services/http/__tests__/collab-api.test.ts new file mode 100644 index 00000000..7b4a2b60 --- /dev/null +++ b/src/application/services/js-services/http/__tests__/collab-api.test.ts @@ -0,0 +1,54 @@ +import { collab } from '@/proto/messages'; +import { getAxios } from '@/application/services/js-services/http/core'; + +import { collabFullSyncBatch } from '../collab-api'; + +jest.mock('@/application/services/js-services/device-id', () => ({ + getOrCreateDeviceId: jest.fn(() => 'test-device-id'), +})); + +jest.mock('@/application/services/js-services/http/core', () => ({ + executeAPIRequest: jest.fn(), + executeAPIVoidRequest: jest.fn(), + getAxios: jest.fn(), + parseRetryAfterSecs: jest.fn(), +})); + +const mockGetAxios = getAxios as unknown as jest.Mock; + +describe('collabFullSyncBatch', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('sends the encoded protobuf view instead of the pooled backing buffer', async () => { + const responseBody = collab.CollabBatchSyncResponse.encode( + collab.CollabBatchSyncResponse.create({ + results: [], + responseCompression: collab.PayloadCompressionType.COMPRESSION_NONE, + }) + ).finish(); + const post = jest.fn().mockResolvedValue({ + status: 200, + data: responseBody, + headers: {}, + }); + + mockGetAxios.mockReturnValue({ post }); + + await collabFullSyncBatch('workspace-id', [ + { + objectId: 'object-id', + collabType: 0, + stateVector: new Uint8Array([1]), + docState: new Uint8Array([2]), + }, + ]); + + const [, requestBody, config] = post.mock.calls[0]; + + expect(ArrayBuffer.isView(requestBody)).toBe(true); + expect(requestBody.byteLength).toBeLessThan(requestBody.buffer.byteLength); + expect(config.transformRequest[0](requestBody)).toBe(requestBody); + }); +}); diff --git a/src/application/services/js-services/http/collab-api.ts b/src/application/services/js-services/http/collab-api.ts index 56e0a326..15fa700b 100644 --- a/src/application/services/js-services/http/collab-api.ts +++ b/src/application/services/js-services/http/collab-api.ts @@ -100,6 +100,9 @@ export async function collabFullSyncBatch( 'device-id': deviceId, }, responseType: 'arraybuffer', + // Axios' default transform sends typed-array `.buffer`, which can include + // protobufjs' pooled zero padding beyond this Uint8Array view. + transformRequest: [(data: Uint8Array) => data], validateStatus: (status) => status === 200 || status === 429 || status === 503, });