From a3883326cd7e6359618113dc0512064bbd3f2fe2 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 25 Apr 2024 19:12:32 +0200 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20update=20remote.crea?= =?UTF-8?q?te()=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/DemoServerRemoteHistory.ts | 16 ++++++++-------- .../__tests__/DemoServerRemoteHistory.spec.ts | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts index 412a032d..567b7f9b 100644 --- a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts +++ b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts @@ -1,5 +1,5 @@ -import type {Observable} from 'rxjs'; import {CallerToMethods, TypedRpcClient} from '../../common'; +import type {Observable} from 'rxjs'; import type {JsonJoyDemoRpcCaller} from '../../__demos__/json-crdt-server'; import type {RemoteHistory, RemoteBlockSnapshot, RemoteBlockPatch, RemoteBlock} from './types'; @@ -68,13 +68,13 @@ export class DemoServerRemoteHistory snapshot: Omit; patches: Omit[]; }> { - throw new Error('Method not implemented.'); - // await this.client.call('block.new', { - // id, - // patches: patches.map((patch) => ({ - // blob: patch.blob, - // })), - // }); + const res = await this.client.call('block.new', { + id, + patches: patches.map((patch) => ({ + blob: patch.blob, + })), + }); + return res; } public async update( diff --git a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts index 2940c395..aeb41eea 100644 --- a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts @@ -20,7 +20,7 @@ let cnt = 0; const genId = () => Math.random().toString(36).slice(2) + '-' + Date.now().toString(36) + '-' + cnt++; describe('.create()', () => { - test.skip('can create a block with a simple patch', async () => { + test('can create a block with a simple patch', async () => { const {remote, caller} = await setup(); const model = Model.withLogicalClock(); model.api.root({foo: 'bar'}); @@ -29,7 +29,6 @@ describe('.create()', () => { const id = genId(); await remote.create(id, [{blob}]); const {data} = await caller.call('block.get', {id}, {}); - // console.log(data.patches); const model2 = Model.fromBinary(data.block.snapshot.blob); expect(model2.view()).toEqual({foo: 'bar'}); }); From 04b614a9950bfccbfd2f1755a7e594196cf8fd51 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 25 Apr 2024 19:25:25 +0200 Subject: [PATCH 2/7] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20remote.delete(?= =?UTF-8?q?)=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/DemoServerRemoteHistory.spec.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts index aeb41eea..34a2fe2f 100644 --- a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts @@ -2,6 +2,8 @@ import {Model} from 'json-joy/lib/json-crdt'; import {buildE2eClient} from '../../../common/testing/buildE2eClient'; import {createCaller} from '../../../__demos__/json-crdt-server/routes'; import {DemoServerRemoteHistory} from '../DemoServerRemoteHistory'; +import {SESSION} from 'json-joy/lib/json-crdt-patch/constants'; +import {Value} from 'json-joy/lib/json-type-value/Value'; const setup = () => { const {caller, router} = createCaller(); @@ -32,4 +34,38 @@ describe('.create()', () => { const model2 = Model.fromBinary(data.block.snapshot.blob); expect(model2.view()).toEqual({foo: 'bar'}); }); + + test('can create with empty model', async () => { + const {remote, caller} = await setup(); + const id = genId(); + await remote.create(id, []); + const {data} = await caller.call('block.get', {id}, {}); + const model2 = Model.fromBinary(data.block.snapshot.blob); + expect(model2.view()).toBe(undefined); + }); + + test('empty model uses global session ID', async () => { + const {remote, caller} = await setup(); + const id = genId(); + await remote.create(id, []); + const {data} = await caller.call('block.get', {id}, {}); + const model2 = Model.fromBinary(data.block.snapshot.blob); + expect(model2.clock.sid).toBe(SESSION.GLOBAL); + }); +}); + +describe('.delete()', () => { + test('can delete an existing block', async () => { + const {remote, caller} = await setup(); + const id = genId(); + await remote.create(id, []); + const get1 = await caller.call('block.get', {id}, {}); + await remote.delete(id); + try { + const get2 = await caller.call('block.get', {id}, {}); + throw new Error('not this error'); + } catch (err) { + expect((err as Value).data.message).toBe('NOT_FOUND'); + } + }); }); From 7c5da54dc29f3c948fe535c8ff4fd91b435445a3 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 25 Apr 2024 19:39:04 +0200 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20implement=20remote.r?= =?UTF-8?q?ead()=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/DemoServerRemoteHistory.ts | 16 ++++---- .../__tests__/DemoServerRemoteHistory.spec.ts | 39 +++++++++++++++++++ src/json-crdt-repo/remote/types.ts | 4 +- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts index 567b7f9b..c084711b 100644 --- a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts +++ b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts @@ -17,14 +17,14 @@ export class DemoServerRemoteHistory constructor(protected readonly client: TypedRpcClient) {} public async read(id: string): Promise<{block: DemoServerBlock}> { - throw new Error('Method not implemented.'); const res = await this.client.call('block.get', {id}); - - // return { - // cursor: model.seq, - // model, - // patches: [], - // }; + return { + block: { + id: res.block.id, + snapshot: res.block.snapshot, + tip: [], + }, + }; } public async scanFwd(id: string, cursor: Cursor): Promise<{patches: DemoServerPatch[]}> { @@ -64,7 +64,7 @@ export class DemoServerRemoteHistory id: string, patches: Pick[], ): Promise<{ - block: Omit; + block: Omit; snapshot: Omit; patches: Omit[]; }> { diff --git a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts index 34a2fe2f..cf1c47d8 100644 --- a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts @@ -54,6 +54,45 @@ describe('.create()', () => { }); }); +describe('.read()', () => { + test('can read a block with a simple patch', async () => { + const {remote} = await setup(); + const model = Model.withLogicalClock(); + model.api.root({score: 42}); + const patch = model.api.flush(); + const blob = patch.toBinary(); + const id = genId(); + await remote.create(id, [{blob}]); + const read = await remote.read(id); + expect(read).toMatchObject({ + block: { + id, + snapshot: { + blob: expect.any(Uint8Array), + cur: 0, + ts: expect.any(Number), + }, + tip: [], + }, + }); + const model2 = Model.fromBinary(read.block.snapshot.blob); + expect(model2.view()).toEqual({score: 42}); + }); + + test('throws NOT_FOUND error on missing block', async () => { + const {remote} = await setup(); + const id = genId(); + try { + const read = await remote.read(id); + throw new Error('not this error'); + } catch (error) { + expect(error).toMatchObject({ + message: 'NOT_FOUND', + }); + } + }); +}); + describe('.delete()', () => { test('can delete an existing block', async () => { const {remote, caller} = await setup(); diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts index e68eb6ec..3da0cd5d 100644 --- a/src/json-crdt-repo/remote/types.ts +++ b/src/json-crdt-repo/remote/types.ts @@ -71,7 +71,7 @@ export interface RemoteHistory< id: string, patches: Pick[], ): Promise<{ - block: Omit; + block: Omit; snapshot: Omit; patches: Omit[]; }>; @@ -120,7 +120,7 @@ export interface RemoteBlock { /** * The latest snapshot of the block. */ - data: RemoteBlockSnapshot; + snapshot: RemoteBlockSnapshot; /** * The latest patches that have been stored, but not yet applied to the the From 49de3cc8f216f6135fbf1198ace91b3173dd8e93 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 25 Apr 2024 20:01:02 +0200 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20update=20the=20remot?= =?UTF-8?q?e.update()=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routes/block/methods/upd.ts | 2 +- .../remote/DemoServerRemoteHistory.ts | 23 +++++++----------- .../__tests__/DemoServerRemoteHistory.spec.ts | 24 +++++++++++++++++++ src/json-crdt-repo/remote/types.ts | 2 +- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/__demos__/json-crdt-server/routes/block/methods/upd.ts b/src/__demos__/json-crdt-server/routes/block/methods/upd.ts index 963647fa..921dfbf1 100644 --- a/src/__demos__/json-crdt-server/routes/block/methods/upd.ts +++ b/src/__demos__/json-crdt-server/routes/block/methods/upd.ts @@ -1,6 +1,6 @@ import {ResolveType} from 'json-joy/lib/json-type'; import type {RouteDeps, Router, RouterBase} from '../../types'; -import {BlockCurRef, BlockIdRef, BlockPatchPartialRef, BlockPatchPartialReturnRef} from '../schema'; +import {BlockIdRef, BlockPatchPartialRef, BlockPatchPartialReturnRef} from '../schema'; export const upd = ({t, services}: RouteDeps) => diff --git a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts index c084711b..4f52605c 100644 --- a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts +++ b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts @@ -79,22 +79,17 @@ export class DemoServerRemoteHistory public async update( id: string, - cursor: Cursor, patches: Pick[], ): Promise<{patches: Omit[]}> { - throw new Error('Method not implemented.'); - // const res = await this.client.call('block.upd', { - // id, - // patches: patches.map((patch, seq) => ({ - // seq, - // created: Date.now(), - // blob: patch.blob, - // })), - // }); - // return { - // cursor: res.patches.length ? res.patches[res.patches.length - 1].seq : cursor, - // patches: res.patches, - // }; + const res = await this.client.call('block.upd', { + id, + patches: patches.map((patch) => ({ + blob: patch.blob, + })), + }); + return { + patches: res.patches, + }; } public async delete(id: string): Promise { diff --git a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts index cf1c47d8..4e82372f 100644 --- a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts @@ -93,6 +93,30 @@ describe('.read()', () => { }); }); +describe('.update()', () => { + test('can apply changes to an empty document', async () => { + const {remote, caller} = await setup(); + const id = genId(); + await remote.create(id, []); + const read1 = await remote.read(id); + const model1 = Model.fromBinary(read1.block.snapshot.blob); + expect(model1.view()).toBe(undefined); + const model = Model.withLogicalClock(); + model.api.root({score: 42}); + const patch = model.api.flush(); + const blob = patch.toBinary(); + const update = await remote.update(id, [{blob}]); + expect(update).toMatchObject({ + patches: [{ + ts: expect.any(Number), + }] + }); + const read2 = await remote.read(id); + const model2 = Model.fromBinary(read2.block.snapshot.blob); + expect(model2.view()).toEqual({score: 42}); + }); +}); + describe('.delete()', () => { test('can delete an existing block', async () => { const {remote, caller} = await setup(); diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts index 3da0cd5d..3da1dcab 100644 --- a/src/json-crdt-repo/remote/types.ts +++ b/src/json-crdt-repo/remote/types.ts @@ -83,7 +83,7 @@ export interface RemoteHistory< * @param cursor The cursor of the last known model state of the block. * @param patches A list of patches to apply to the block. */ - update(id: string, cursor: Cursor, patches: Pick[]): Promise<{patches: Omit[]}>; + update(id: string, patches: Pick[]): Promise<{patches: Omit[]}>; /** * Delete the block. If not implemented, means that the protocol does not From af459c94d40e2de49611b566e9de537317f0a2ee Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 25 Apr 2024 20:17:33 +0200 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20update=20remote.scan?= =?UTF-8?q?Fwd()=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/DemoServerRemoteHistory.ts | 24 +++++----------- .../__tests__/DemoServerRemoteHistory.spec.ts | 28 ++++++++++++++++++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts index 4f52605c..440e047b 100644 --- a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts +++ b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts @@ -28,23 +28,13 @@ export class DemoServerRemoteHistory } public async scanFwd(id: string, cursor: Cursor): Promise<{patches: DemoServerPatch[]}> { - throw new Error('Method not implemented.'); - // const limit = 100; - // const res = await this.client.call('block.scan', { - // id, - // seq: cursor, - // limit: cursor + limit, - // }); - // if (res.patches.length === 0) { - // return { - // cursor, - // patches: [], - // }; - // } - // return { - // cursor: res.patches[res.patches.length - 1].seq, - // patches: res.patches, - // }; + const limit = 100; + const res = await this.client.call('block.scan', { + id, + cur: cursor, + limit, + }); + return res; } public async scanBwd( diff --git a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts index 4e82372f..c1cbf599 100644 --- a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts @@ -95,7 +95,7 @@ describe('.read()', () => { describe('.update()', () => { test('can apply changes to an empty document', async () => { - const {remote, caller} = await setup(); + const {remote} = await setup(); const id = genId(); await remote.create(id, []); const read1 = await remote.read(id); @@ -117,6 +117,32 @@ describe('.update()', () => { }); }); +describe('.scanFwd()', () => { + test('can scan patches forward', async () => { + const {remote} = await setup(); + const id = genId(); + const model1 = Model.withLogicalClock(); + model1.api.root({score: 42}); + const patch1 = model1.api.flush(); + const blob = patch1.toBinary(); + await remote.create(id, [{blob}]); + const read1 = await remote.read(id); + model1.api.obj([]).set({ + foo: 'bar', + }); + const patch2 = model1.api.flush(); + const blob2 = patch2.toBinary(); + await remote.update(id, [{blob: blob2}]); + const scan1 = await remote.scanFwd(id, read1.block.snapshot.cur + 1); + expect(scan1).toMatchObject({ + patches: [{ + blob: expect.any(Uint8Array), + ts: expect.any(Number), + }] + }); + }); +}); + describe('.delete()', () => { test('can delete an existing block', async () => { const {remote, caller} = await setup(); From 476f64f44b8a7dac4545d20aa0af10a0556a5f95 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 26 Apr 2024 08:49:10 +0200 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20update=20remote.scan?= =?UTF-8?q?Bwd()=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/blocks/BlocksServices.ts | 4 +-- .../remote/DemoServerRemoteHistory.ts | 24 ++++++++------ .../__tests__/DemoServerRemoteHistory.spec.ts | 32 ++++++++++++++++++- src/json-crdt-repo/remote/types.ts | 2 +- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts b/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts index 828d45d4..cb38c6ab 100644 --- a/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts +++ b/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts @@ -106,10 +106,10 @@ export class BlocksServices { if (!limit || Math.round(limit) !== limit) throw RpcError.badRequest('INVALID_LIMIT'); if (limit > 0) { min = Number(offset) || 0; - max = min + limit; + max = min + limit - 1; } else { max = Number(offset) || 0; - min = max - limit; + min = max - limit + 1; } if (min < 0) { min = 0; diff --git a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts index 440e047b..f8fb55d5 100644 --- a/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts +++ b/src/json-crdt-repo/remote/DemoServerRemoteHistory.ts @@ -31,7 +31,7 @@ export class DemoServerRemoteHistory const limit = 100; const res = await this.client.call('block.scan', { id, - cur: cursor, + cur: cursor + 1, limit, }); return res; @@ -40,14 +40,20 @@ export class DemoServerRemoteHistory public async scanBwd( id: string, cursor: Cursor, - ): Promise<{snapshot: DemoServerSnapshot; patches: DemoServerPatch[]}> { - throw new Error('Method not implemented.'); - // const res = await this.client.call('block.scan', { - // id, - // seq: cursor, - // limit: -100, - // model: true, - // }); + ): Promise<{snapshot?: DemoServerSnapshot; patches: DemoServerPatch[]}> { + if (cursor <= 0) { + return { + patches: [], + }; + } + const res = await this.client.call('block.scan', { + id, + cur: 0, + limit: cursor, + }); + return { + patches: res.patches, + }; } public async create( diff --git a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts index c1cbf599..88c4ec24 100644 --- a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts @@ -133,13 +133,43 @@ describe('.scanFwd()', () => { const patch2 = model1.api.flush(); const blob2 = patch2.toBinary(); await remote.update(id, [{blob: blob2}]); - const scan1 = await remote.scanFwd(id, read1.block.snapshot.cur + 1); + const scan1 = await remote.scanFwd(id, read1.block.snapshot.cur); expect(scan1).toMatchObject({ patches: [{ blob: expect.any(Uint8Array), ts: expect.any(Number), }] }); + expect(scan1.patches[0].blob).toEqual(blob2); + }); +}); + +describe('.scanBwd()', () => { + test('can scan patches backward', async () => { + const {remote} = await setup(); + const id = genId(); + const model1 = Model.withLogicalClock(); + model1.api.root({score: 42}); + const patch1 = model1.api.flush(); + const blob1 = patch1.toBinary(); + await remote.create(id, [{blob: blob1}]); + const read1 = await remote.read(id); + model1.api.obj([]).set({ + foo: 'bar', + }); + const patch2 = model1.api.flush(); + const blob2 = patch2.toBinary(); + await remote.update(id, [{blob: blob2}]); + const read2 = await remote.read(id); + const scan1 = await remote.scanBwd(id, read2.block.snapshot.cur); + expect(scan1.patches.length).toBe(1); + expect(scan1).toMatchObject({ + patches: [{ + blob: expect.any(Uint8Array), + ts: expect.any(Number), + }] + }); + expect(scan1.patches[0].blob).toEqual(blob1); }); }); diff --git a/src/json-crdt-repo/remote/types.ts b/src/json-crdt-repo/remote/types.ts index 3da1dcab..15aa2db6 100644 --- a/src/json-crdt-repo/remote/types.ts +++ b/src/json-crdt-repo/remote/types.ts @@ -59,7 +59,7 @@ export interface RemoteHistory< * @param id ID of the block. * @param cursor The cursor until which to scan. */ - scanBwd(id: string, cursor: Cursor): Promise<{snapshot: S; patches: P[]}>; + scanBwd(id: string, cursor: Cursor): Promise<{patches: P[]; snapshot?: S}>; /** * Create a new block with the given patches. From 0d6cd38103f1e4fc0df139071bdec065e283b84c Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 26 Apr 2024 08:49:49 +0200 Subject: [PATCH 7/7] =?UTF-8?q?style:=20=F0=9F=92=84=20run=20Prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/DemoServerRemoteHistory.spec.ts | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts index 88c4ec24..89ffae50 100644 --- a/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts +++ b/src/json-crdt-repo/remote/__tests__/DemoServerRemoteHistory.spec.ts @@ -107,9 +107,11 @@ describe('.update()', () => { const blob = patch.toBinary(); const update = await remote.update(id, [{blob}]); expect(update).toMatchObject({ - patches: [{ - ts: expect.any(Number), - }] + patches: [ + { + ts: expect.any(Number), + }, + ], }); const read2 = await remote.read(id); const model2 = Model.fromBinary(read2.block.snapshot.blob); @@ -135,10 +137,12 @@ describe('.scanFwd()', () => { await remote.update(id, [{blob: blob2}]); const scan1 = await remote.scanFwd(id, read1.block.snapshot.cur); expect(scan1).toMatchObject({ - patches: [{ - blob: expect.any(Uint8Array), - ts: expect.any(Number), - }] + patches: [ + { + blob: expect.any(Uint8Array), + ts: expect.any(Number), + }, + ], }); expect(scan1.patches[0].blob).toEqual(blob2); }); @@ -164,10 +168,12 @@ describe('.scanBwd()', () => { const scan1 = await remote.scanBwd(id, read2.block.snapshot.cur); expect(scan1.patches.length).toBe(1); expect(scan1).toMatchObject({ - patches: [{ - blob: expect.any(Uint8Array), - ts: expect.any(Number), - }] + patches: [ + { + blob: expect.any(Uint8Array), + ts: expect.any(Number), + }, + ], }); expect(scan1.patches[0].blob).toEqual(blob1); });