@dxos/echo-pipeline 0.7.3-staging.cc8dd3e → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,8 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { describe, test } from 'vitest';
5
+ import { getRandomPort } from 'get-port-please';
6
+ import { describe, expect, onTestFinished, test } from 'vitest';
6
7
 
7
8
  import { Event } from '@dxos/async';
8
9
  import { cbor } from '@dxos/automerge/automerge-repo';
@@ -16,69 +17,111 @@ import type { Peer } from '@dxos/protocols/proto/dxos/edge/messenger';
16
17
  import { openAndClose } from '@dxos/test-utils';
17
18
 
18
19
  import { EchoEdgeReplicator } from './echo-edge-replicator';
19
- import type { EchoReplicatorContext } from '../automerge';
20
+ import type { EchoReplicatorContext, ReplicatorConnection } from '../automerge';
20
21
 
21
22
  describe('EchoEdgeReplicator', () => {
22
- test('reconnects', async ({ onTestFinished }) => {
23
- const { endpoint, cleanup, sendMessage } = await createTestEdgeWsServer(8001);
24
- onTestFinished(cleanup);
25
- const client = new EdgeClient(await createEphemeralEdgeIdentity(), { socketEndpoint: endpoint });
26
- await openAndClose(client);
23
+ test('reconnects', async () => {
24
+ const { client, server } = await createClientServer();
27
25
 
28
26
  const spaceId = SpaceId.random();
29
27
 
30
- const replicator = new EchoEdgeReplicator({ edgeConnection: client });
31
28
  const { context, connectionOpen } = createMockContext();
32
- await replicator.connect(context);
29
+ const replicator = await connectReplicator(client, context);
33
30
  await replicator.connectToSpace(spaceId);
34
31
 
35
32
  client.setIdentity(await createEphemeralEdgeIdentity());
36
33
  await connectionOpen.waitForCount(1);
37
34
 
38
- sendMessage(
39
- createForbiddenMessage(
40
- {
41
- identityKey: client.identityKey,
42
- peerKey: client.peerKey,
43
- },
44
- spaceId,
45
- ),
46
- );
35
+ const forbidden = createForbiddenMessage({ identityKey: client.identityKey, peerKey: client.peerKey }, spaceId);
36
+ server.sendMessage(forbidden);
47
37
  await connectionOpen.waitForCount(1);
48
38
 
49
39
  // Double restart to check for race conditions.
50
40
  client.setIdentity(await createEphemeralEdgeIdentity());
51
- sendMessage(
52
- createForbiddenMessage(
53
- {
54
- identityKey: client.identityKey,
55
- peerKey: client.peerKey,
56
- },
57
- spaceId,
58
- ),
59
- );
41
+ server.sendMessage(forbidden);
60
42
  await connectionOpen.waitForCount(1);
61
43
 
62
44
  await replicator.disconnect();
63
45
  });
46
+
47
+ describe('shouldAdvertise', () => {
48
+ test('true if space document belongs to connection space', async () => {
49
+ const { client } = await createClientServer();
50
+
51
+ const spaceId = SpaceId.random();
52
+ const documentId = PublicKey.random().toHex();
53
+ const { context, openConnections } = createMockContext({
54
+ documentSpaceId: { [documentId]: spaceId },
55
+ });
56
+ const replicator = await connectReplicator(client, context);
57
+ await replicator.connectToSpace(spaceId);
58
+
59
+ await expect.poll(() => openConnections.length === 1).toBeTruthy();
60
+ expect(openConnections[0].shouldAdvertise({ documentId })).toBeTruthy();
61
+ });
62
+
63
+ test('checks remote collection if space id can not be resolved', async () => {
64
+ const { client } = await createClientServer();
65
+
66
+ const spaceId = SpaceId.random();
67
+ const documentId = PublicKey.random().toHex();
68
+ const remoteCollections: { [peerId: string]: { [documentId: string]: boolean } } = {};
69
+ const { context, openConnections } = createMockContext({ remoteCollections });
70
+ const replicator = await connectReplicator(client, context);
71
+ await replicator.connectToSpace(spaceId);
72
+
73
+ await expect.poll(() => openConnections.length === 1).toBeTruthy();
74
+ const connection = openConnections[0];
75
+ expect(await connection.shouldAdvertise({ documentId })).toBeFalsy();
76
+ remoteCollections[connection.peerId] = { [documentId]: true };
77
+ expect(await connection.shouldAdvertise({ documentId })).toBeTruthy();
78
+ });
79
+ });
80
+
81
+ const connectReplicator = async (client: EdgeClient, context: EchoReplicatorContext) => {
82
+ const replicator = new EchoEdgeReplicator({ edgeConnection: client });
83
+ await replicator.connect(context);
84
+ onTestFinished(() => replicator.disconnect());
85
+ return replicator;
86
+ };
87
+
88
+ const createClientServer = async () => {
89
+ const server = await createTestEdgeWsServer(await getRandomPort());
90
+ onTestFinished(server.cleanup);
91
+ const client = new EdgeClient(await createEphemeralEdgeIdentity(), { socketEndpoint: server.endpoint });
92
+ await openAndClose(client);
93
+ return { client, server };
94
+ };
64
95
  });
65
96
 
66
- const createMockContext = () => {
97
+ const createMockContext = (args?: {
98
+ remoteCollections?: { [peerId: string]: { [documentId: string]: boolean } };
99
+ documentSpaceId?: { [documentId: string]: SpaceId };
100
+ }) => {
67
101
  const connectionOpen = new Event();
102
+ const openConnections: ReplicatorConnection[] = [];
68
103
  return {
69
104
  context: {
70
- getContainingSpaceIdForDocument: async (documentId) => null,
105
+ getContainingSpaceIdForDocument: async (documentId) => args?.documentSpaceId?.[documentId] ?? null,
71
106
  getContainingSpaceForDocument: async (documentId) => null,
72
107
  onConnectionAuthScopeChanged: (connection) => {},
73
- isDocumentInRemoteCollection: async (params) => false,
74
- onConnectionClosed: (connection) => {},
108
+ isDocumentInRemoteCollection: async (params) =>
109
+ args?.remoteCollections?.[params.peerId]?.[params.documentId] ?? false,
110
+ onConnectionClosed: (connection) => {
111
+ const idx = openConnections.indexOf(connection);
112
+ if (idx >= 0) {
113
+ openConnections.splice(idx, 1);
114
+ }
115
+ },
75
116
  onConnectionOpen: (connection) => {
117
+ openConnections.push(connection);
76
118
  connectionOpen.emit();
77
119
  },
78
120
 
79
121
  peerId: PublicKey.random().toHex(),
80
122
  } satisfies EchoReplicatorContext,
81
123
 
124
+ openConnections,
82
125
  connectionOpen,
83
126
  };
84
127
  };
@@ -267,9 +267,20 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
267
267
  }
268
268
  const spaceId = await this._context.getContainingSpaceIdForDocument(params.documentId);
269
269
  if (!spaceId) {
270
- // There's no spaceId if the document is not present locally. This means the sharePolicy check is being
271
- // performed on message reception, so spaceId check was already performed in _onMessage.
272
- return true;
270
+ const remoteDocumentExists = await this._context.isDocumentInRemoteCollection({
271
+ documentId: params.documentId,
272
+ peerId: this._remotePeerId as PeerId,
273
+ });
274
+
275
+ log.info('document not found locally for share policy check, accepting the remote document', {
276
+ documentId: params.documentId,
277
+ remoteDocumentExists,
278
+ });
279
+
280
+ // If a document is not present locally return true only if it already exists on edge.
281
+ // Simply returning true will add edge to "generous peers list" for this document which will
282
+ // start replication of the document after we receive it potentially pushing it to replicator of the wrong space.
283
+ return remoteDocumentExists;
273
284
  }
274
285
  return spaceId === this._spaceId;
275
286
  }