@dxos/network-manager 0.1.9 → 0.1.11

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.
Files changed (155) hide show
  1. package/dist/src/index.d.ts +2 -2
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +2 -2
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/network-manager.d.ts +10 -14
  6. package/dist/src/network-manager.d.ts.map +1 -1
  7. package/dist/src/network-manager.js +28 -33
  8. package/dist/src/network-manager.js.map +1 -1
  9. package/dist/src/protocol-factory.d.ts.map +1 -1
  10. package/dist/src/protocol-factory.js +8 -6
  11. package/dist/src/protocol-factory.js.map +1 -1
  12. package/dist/src/signal/index.d.ts +2 -2
  13. package/dist/src/signal/index.d.ts.map +1 -1
  14. package/dist/src/signal/index.js +2 -2
  15. package/dist/src/signal/index.js.map +1 -1
  16. package/dist/src/signal/message-router.d.ts +2 -2
  17. package/dist/src/signal/message-router.js +3 -3
  18. package/dist/src/signal/message-router.js.map +1 -1
  19. package/dist/src/signal/{signal-messaging.d.ts → signal-messenger.d.ts} +2 -2
  20. package/dist/src/signal/{signal-messaging.d.ts.map → signal-messenger.d.ts.map} +1 -1
  21. package/dist/src/signal/{signal-messaging.js → signal-messenger.js} +1 -1
  22. package/dist/src/signal/signal-messenger.js.map +1 -0
  23. package/dist/src/swarm/connection.d.ts +6 -3
  24. package/dist/src/swarm/connection.d.ts.map +1 -1
  25. package/dist/src/swarm/connection.js +28 -15
  26. package/dist/src/swarm/connection.js.map +1 -1
  27. package/dist/src/swarm/peer.d.ts +45 -0
  28. package/dist/src/swarm/peer.d.ts.map +1 -0
  29. package/dist/src/swarm/peer.js +113 -0
  30. package/dist/src/swarm/peer.js.map +1 -0
  31. package/dist/src/swarm/swarm-mapper.d.ts +2 -2
  32. package/dist/src/swarm/swarm-mapper.d.ts.map +1 -1
  33. package/dist/src/swarm/swarm-mapper.js +12 -10
  34. package/dist/src/swarm/swarm-mapper.js.map +1 -1
  35. package/dist/src/swarm/swarm.d.ts +13 -18
  36. package/dist/src/swarm/swarm.d.ts.map +1 -1
  37. package/dist/src/swarm/swarm.js +126 -97
  38. package/dist/src/swarm/swarm.js.map +1 -1
  39. package/dist/src/swarm/swarm.test.js +2 -2
  40. package/dist/src/swarm/swarm.test.js.map +1 -1
  41. package/dist/src/testing/test-builder.d.ts +31 -18
  42. package/dist/src/testing/test-builder.d.ts.map +1 -1
  43. package/dist/src/testing/test-builder.js +120 -48
  44. package/dist/src/testing/test-builder.js.map +1 -1
  45. package/dist/src/testing/test-protocol.d.ts +5 -5
  46. package/dist/src/testing/test-protocol.d.ts.map +1 -1
  47. package/dist/src/testing/test-protocol.js +28 -24
  48. package/dist/src/testing/test-protocol.js.map +1 -1
  49. package/dist/src/tests/basic-test-suite.d.ts +6 -0
  50. package/dist/src/tests/basic-test-suite.d.ts.map +1 -0
  51. package/dist/src/tests/basic-test-suite.js +142 -0
  52. package/dist/src/tests/basic-test-suite.js.map +1 -0
  53. package/dist/src/tests/memory-transport.test.d.ts +2 -0
  54. package/dist/src/tests/memory-transport.test.d.ts.map +1 -0
  55. package/dist/src/tests/memory-transport.test.js +12 -0
  56. package/dist/src/tests/memory-transport.test.js.map +1 -0
  57. package/dist/src/{presence.test.d.ts → tests/presence.test.d.ts} +0 -0
  58. package/dist/src/tests/presence.test.d.ts.map +1 -0
  59. package/dist/src/{presence.test.js → tests/presence.test.js} +21 -20
  60. package/dist/src/tests/presence.test.js.map +1 -0
  61. package/dist/src/tests/property-test-suite.d.ts +5 -0
  62. package/dist/src/tests/property-test-suite.d.ts.map +1 -0
  63. package/dist/src/tests/property-test-suite.js +203 -0
  64. package/dist/src/tests/property-test-suite.js.map +1 -0
  65. package/dist/src/tests/utils.d.ts +21 -0
  66. package/dist/src/tests/utils.d.ts.map +1 -0
  67. package/dist/src/tests/utils.js +76 -0
  68. package/dist/src/tests/utils.js.map +1 -0
  69. package/dist/src/tests/webrtc-transport.test.d.ts +2 -0
  70. package/dist/src/tests/webrtc-transport.test.d.ts.map +1 -0
  71. package/dist/src/tests/webrtc-transport.test.js +16 -0
  72. package/dist/src/tests/webrtc-transport.test.js.map +1 -0
  73. package/dist/src/topology/fully-connected-topology.d.ts +1 -1
  74. package/dist/src/topology/fully-connected-topology.d.ts.map +1 -1
  75. package/dist/src/topology/fully-connected-topology.js +4 -3
  76. package/dist/src/topology/fully-connected-topology.js.map +1 -1
  77. package/dist/src/topology/mmst-topology.js +4 -4
  78. package/dist/src/topology/mmst-topology.js.map +1 -1
  79. package/dist/src/topology/star-topology.js +4 -4
  80. package/dist/src/topology/star-topology.js.map +1 -1
  81. package/dist/src/topology/topology.d.ts +0 -5
  82. package/dist/src/topology/topology.d.ts.map +1 -1
  83. package/dist/src/transport/memory-transport.d.ts +8 -7
  84. package/dist/src/transport/memory-transport.d.ts.map +1 -1
  85. package/dist/src/transport/memory-transport.js +79 -45
  86. package/dist/src/transport/memory-transport.js.map +1 -1
  87. package/dist/src/transport/memory-transport.test.js +3 -2
  88. package/dist/src/transport/memory-transport.test.js.map +1 -1
  89. package/dist/src/transport/transport.d.ts +8 -4
  90. package/dist/src/transport/transport.d.ts.map +1 -1
  91. package/dist/src/transport/webrtc-simple-peer.test.d.ts +2 -0
  92. package/dist/src/transport/webrtc-simple-peer.test.d.ts.map +1 -0
  93. package/dist/src/transport/{wrtc-simple-peer.test.js → webrtc-simple-peer.test.js} +2 -2
  94. package/dist/src/transport/webrtc-simple-peer.test.js.map +1 -0
  95. package/dist/src/transport/webrtc-transport-proxy.d.ts +4 -4
  96. package/dist/src/transport/webrtc-transport-proxy.d.ts.map +1 -1
  97. package/dist/src/transport/webrtc-transport-proxy.js +15 -10
  98. package/dist/src/transport/webrtc-transport-proxy.js.map +1 -1
  99. package/dist/src/transport/webrtc-transport-proxy.test.js +2 -2
  100. package/dist/src/transport/webrtc-transport-proxy.test.js.map +1 -1
  101. package/dist/src/transport/webrtc-transport-service.js +2 -2
  102. package/dist/src/transport/webrtc-transport-service.js.map +1 -1
  103. package/dist/src/transport/webrtc-transport.d.ts +3 -3
  104. package/dist/src/transport/webrtc-transport.d.ts.map +1 -1
  105. package/dist/src/transport/webrtc-transport.js +10 -10
  106. package/dist/src/transport/webrtc-transport.js.map +1 -1
  107. package/dist/src/transport/webrtc-transport.test.js +3 -3
  108. package/dist/src/transport/webrtc-transport.test.js.map +1 -1
  109. package/dist/src/types.d.ts +1 -1
  110. package/dist/src/types.d.ts.map +1 -1
  111. package/package.json +27 -22
  112. package/src/index.ts +2 -2
  113. package/src/network-manager.ts +31 -46
  114. package/src/protocol-factory.ts +5 -3
  115. package/src/signal/index.ts +2 -2
  116. package/src/signal/integration.test.ts +1 -1
  117. package/src/signal/message-router.test.ts +1 -1
  118. package/src/signal/message-router.ts +2 -2
  119. package/src/signal/{signal-messaging.ts → signal-messenger.ts} +2 -2
  120. package/src/swarm/connection.ts +23 -12
  121. package/src/swarm/peer.ts +156 -0
  122. package/src/swarm/swarm-mapper.ts +21 -10
  123. package/src/swarm/swarm.ts +142 -111
  124. package/src/testing/test-builder.ts +143 -63
  125. package/src/testing/test-protocol.ts +31 -23
  126. package/src/tests/basic-test-suite.ts +168 -0
  127. package/src/tests/memory-transport.test.ts +11 -0
  128. package/src/{presence.test.ts → tests/presence.test.ts} +24 -23
  129. package/src/tests/property-test-suite.ts +221 -0
  130. package/src/tests/utils.ts +85 -0
  131. package/src/tests/webrtc-transport.test.ts +16 -0
  132. package/src/topology/fully-connected-topology.ts +5 -4
  133. package/src/topology/topology.ts +0 -5
  134. package/src/transport/memory-transport.test.ts +3 -2
  135. package/src/transport/memory-transport.ts +79 -49
  136. package/src/transport/transport.ts +9 -3
  137. package/src/transport/{wrtc-simple-peer.test.ts → webrtc-simple-peer.test.ts} +1 -1
  138. package/src/transport/webrtc-transport-proxy.test.ts +2 -2
  139. package/src/transport/webrtc-transport-proxy.ts +17 -8
  140. package/src/transport/webrtc-transport-service.ts +1 -1
  141. package/src/transport/webrtc-transport.test.ts +4 -6
  142. package/src/transport/webrtc-transport.ts +4 -4
  143. package/testing.d.ts +11 -0
  144. package/testing.js +5 -0
  145. package/dist/src/network-manager.test.d.ts +0 -2
  146. package/dist/src/network-manager.test.d.ts.map +0 -1
  147. package/dist/src/network-manager.test.js +0 -596
  148. package/dist/src/network-manager.test.js.map +0 -1
  149. package/dist/src/presence.test.d.ts.map +0 -1
  150. package/dist/src/presence.test.js.map +0 -1
  151. package/dist/src/signal/signal-messaging.js.map +0 -1
  152. package/dist/src/transport/wrtc-simple-peer.test.d.ts +0 -2
  153. package/dist/src/transport/wrtc-simple-peer.test.d.ts.map +0 -1
  154. package/dist/src/transport/wrtc-simple-peer.test.js.map +0 -1
  155. package/src/network-manager.test.ts +0 -707
@@ -4,96 +4,176 @@
4
4
 
5
5
  import { PublicKey } from '@dxos/keys';
6
6
  import { MemorySignalManager, MemorySignalManagerContext, WebsocketSignalManager } from '@dxos/messaging';
7
- import { afterTest } from '@dxos/testutils';
7
+ import { schema } from '@dxos/protocols';
8
+ import { createLinkedPorts, createProtoRpcPeer, ProtoRpcPeer } from '@dxos/rpc';
9
+ import { ComplexMap } from '@dxos/util';
8
10
 
9
11
  import { NetworkManager } from '../network-manager';
10
- import { FullyConnectedTopology, Topology } from '../topology';
11
- import { MemoryTransportFactory, TransportFactory } from '../transport';
12
+ import { FullyConnectedTopology } from '../topology';
13
+ import {
14
+ MemoryTransportFactory,
15
+ TransportFactory,
16
+ WebRTCTransport,
17
+ WebRTCTransportProxyFactory,
18
+ WebRTCTransportService
19
+ } from '../transport';
12
20
  import { TestProtocolPlugin, testProtocolProvider } from './test-protocol';
13
21
 
14
22
  // Signal server will be started by the setup script.
15
23
  export const TEST_SIGNAL_URL = 'ws://localhost:4000/.well-known/dx/signal';
16
24
 
17
25
  export type TestBuilderOptions = {
18
- signalUrl?: string;
26
+ signalHosts?: string[];
27
+ bridge?: boolean;
19
28
  };
20
29
 
21
- // TODO(burdon): Builder pattern (see feed-store).
30
+ /**
31
+ * Builder used to construct networks and peers.
32
+ */
22
33
  export class TestBuilder {
23
34
  private _signalContext = new MemorySignalManagerContext();
24
35
 
25
- constructor(private _options: TestBuilderOptions = {}) {}
36
+ constructor(public readonly options: TestBuilderOptions = {}) {}
26
37
 
27
38
  createSignalManager() {
28
- if (this._options.signalUrl) {
29
- return new WebsocketSignalManager([this._options.signalUrl]);
39
+ if (this.options.signalHosts) {
40
+ return new WebsocketSignalManager(this.options.signalHosts);
30
41
  }
31
42
 
32
43
  return new MemorySignalManager(this._signalContext);
33
44
  }
34
45
 
46
+ createPeer() {
47
+ return new TestPeer(this);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Testing network peer.
53
+ */
54
+ export class TestPeer {
55
+ readonly peerId = PublicKey.random();
56
+
57
+ private readonly _swarms = new ComplexMap<PublicKey, TestSwarmConnection>(PublicKey.hash);
58
+
59
+ /**
60
+ * @internal
61
+ */
62
+ readonly _networkManager: NetworkManager;
63
+
64
+ private _proxy?: ProtoRpcPeer<any>;
65
+ private _service?: ProtoRpcPeer<any>;
66
+
67
+ constructor(private readonly testBuilder: TestBuilder) {
68
+ this._networkManager = this.createNetworkManager();
69
+ }
70
+
71
+ // TODO(burdon): Move to TestBuilder.
35
72
  createNetworkManager() {
73
+ let transportFactory: TransportFactory = MemoryTransportFactory;
74
+
75
+ if (this.testBuilder.options.signalHosts) {
76
+ if (this.testBuilder.options.bridge) {
77
+ // Simulates bridge to shared worker.
78
+ const [proxyPort, servicePort] = createLinkedPorts();
79
+
80
+ this._proxy = createProtoRpcPeer({
81
+ port: proxyPort,
82
+ requested: {
83
+ BridgeService: schema.getService('dxos.mesh.bridge.BridgeService')
84
+ },
85
+ noHandshake: true,
86
+ encodingOptions: {
87
+ preserveAny: true
88
+ }
89
+ });
90
+
91
+ this._service = createProtoRpcPeer({
92
+ port: servicePort,
93
+ exposed: {
94
+ BridgeService: schema.getService('dxos.mesh.bridge.BridgeService')
95
+ },
96
+ handlers: { BridgeService: new WebRTCTransportService() },
97
+ noHandshake: true,
98
+ encodingOptions: {
99
+ preserveAny: true
100
+ }
101
+ });
102
+
103
+ transportFactory = new WebRTCTransportProxyFactory().setBridgeService(this._proxy.rpc.BridgeService);
104
+ } else {
105
+ transportFactory = {
106
+ createTransport: (params) => new WebRTCTransport(params)
107
+ };
108
+ }
109
+ }
110
+
36
111
  return new NetworkManager({
37
- signalManager: new MemorySignalManager(this._signalContext),
38
- transportFactory: MemoryTransportFactory
112
+ signalManager: this.testBuilder.createSignalManager(),
113
+ transportFactory
39
114
  });
40
115
  }
41
- }
42
116
 
43
- export interface CreatePeerOptions {
44
- topic: PublicKey;
45
- peerId: PublicKey;
46
- topology?: Topology;
47
- signalHosts?: string[];
48
- transportFactory: TransportFactory;
117
+ async open() {
118
+ await this._proxy?.open();
119
+ await this._service?.open();
120
+ }
121
+
122
+ async close() {
123
+ await Promise.all(Array.from(this._swarms.values()).map((swarm) => swarm.leave()));
124
+ this._swarms.clear();
125
+
126
+ await this._proxy?.close();
127
+ await this._service?.close();
128
+ await this._networkManager.close();
129
+ }
130
+
131
+ getSwarm(topic: PublicKey): TestSwarmConnection {
132
+ const swarm = this._swarms.get(topic);
133
+ if (!swarm) {
134
+ throw new Error(`Swarm not found for topic: ${topic}`);
135
+ }
136
+
137
+ return swarm;
138
+ }
139
+
140
+ createSwarm(topic: PublicKey): TestSwarmConnection {
141
+ // TODO(burdon): Multiple.
142
+ // if (this._swarms.get(topic)) {
143
+ // throw new Error(`Swarm already exists for topic: ${topic.truncate()}`);
144
+ // }
145
+
146
+ const swarm = new TestSwarmConnection(this, topic);
147
+ this._swarms.set(topic, swarm);
148
+ return swarm;
149
+ }
49
150
  }
50
151
 
51
- // TODO(burdon): Remove.
52
- const signalContext = new MemorySignalManagerContext();
152
+ // TODO(burdon): Reconcile with new Swarm concept.
153
+ export class TestSwarmConnection {
154
+ plugin: TestProtocolPlugin;
53
155
 
54
- /**
55
- * @deprecated
56
- */
57
- export const createPeer = async ({
58
- topic,
59
- peerId,
60
- topology = new FullyConnectedTopology(),
61
- signalHosts,
62
- transportFactory
63
- }: CreatePeerOptions) => {
64
- const signalManager = signalHosts ? new WebsocketSignalManager(signalHosts!) : new MemorySignalManager(signalContext);
65
- await signalManager.subscribeMessages(peerId);
66
- const networkManager = new NetworkManager({
67
- signalManager,
68
- transportFactory
69
- });
70
-
71
- afterTest(() => networkManager.destroy());
72
-
73
- // TODO(burdon): Use keys everywhere.
74
- const plugin = new TestProtocolPlugin(peerId.asBuffer());
75
- const protocolProvider = testProtocolProvider(topic.asBuffer(), peerId, plugin);
76
- await networkManager.openSwarmConnection({ topic, peerId, protocol: protocolProvider, topology });
77
-
78
- return {
79
- networkManager,
80
- plugin
81
- };
82
- };
156
+ constructor(readonly peer: TestPeer, readonly topic: PublicKey) {
157
+ // TODO(burdon): Configure plugins.
158
+ // TODO(burdon): Prevent reuse?
159
+ this.plugin = new TestProtocolPlugin(this.peer.peerId.asBuffer()); // TODO(burdon): PublicKey.
160
+ }
83
161
 
84
- // const createMemoryNetworkManager = (signalContext: MemorySignalManagerContext) => {
85
- // return new NetworkManager({
86
- // signalManager: new MemorySignalManager(signalContext),
87
- // transportFactory: MemoryTransportFactory
88
- // });
89
- // };
162
+ // TODO(burdon): Need to create new plugin instance per swarm?
163
+ // If so, then perhaps joinSwarm should return swarm object with access to plugins.
164
+ async join(topology = new FullyConnectedTopology()) {
165
+ await this.peer._networkManager.joinSwarm({
166
+ topic: this.topic,
167
+ peerId: this.peer.peerId,
168
+ protocol: testProtocolProvider(this.topic.asBuffer(), this.peer.peerId, this.plugin),
169
+ topology
170
+ });
90
171
 
91
- // const createWebRTCNetworkManager = async (config: any, peerId: PublicKey) => {
92
- // const signalManager = new WebsocketSignalManager(signalHosts!);
93
- // await signalManager.subscribeMessages(peerId);
94
- //
95
- // return new NetworkManager({
96
- // signalManager,
97
- // transportFactory: createWebRTCTransportFactory(config)
98
- // });
99
- // };
172
+ return this;
173
+ }
174
+
175
+ async leave() {
176
+ await this.peer._networkManager.leaveSwarm(this.topic);
177
+ return this;
178
+ }
179
+ }
@@ -15,15 +15,31 @@ import { protocolFactory } from '../protocol-factory';
15
15
 
16
16
  const EXTENSION_NAME = 'test';
17
17
 
18
+ // TODO(burdon): PublicKey.
18
19
  // TODO(dboreham): This method should be added to Protocol (and one for "my ID"?).
19
20
  export const getPeerId = (protocol: Protocol) => {
20
21
  const { peerId } = protocol.getSession() ?? {};
21
22
  if (!peerId) {
22
23
  return undefined;
23
24
  }
25
+
24
26
  return PublicKey.bufferize(peerId);
25
27
  };
26
28
 
29
+ /**
30
+ * @deprecated
31
+ */
32
+ // TODO(burdon): Reconcile with transportProtocolProvider.
33
+ export const testProtocolProvider = (topic: Buffer, peerId: PublicKey, protocolPlugin: any) => {
34
+ log('creating protocol-factory', { topic: PublicKey.from(topic), peerId });
35
+
36
+ return protocolFactory({
37
+ getTopics: () => [topic],
38
+ session: { peerId: peerId.toHex() }, // TODO(burdon): PublicKey.
39
+ plugins: [protocolPlugin]
40
+ });
41
+ };
42
+
27
43
  /**
28
44
  * Object Implementing a p2p protocol (interaction across a set of peers using a common protocol)
29
45
  * and an associated transport connection protocol (interaction with each individual peer over a single network
@@ -81,9 +97,13 @@ export class TestProtocolPlugin extends EventEmitter {
81
97
  .setCloseHandler(this._onPeerDisconnect.bind(this));
82
98
  }
83
99
 
84
- // Methods below are per-peer-connection.
100
+ // Methods below are per-peer connection.
85
101
  // TODO(dboreham): Why are these here and not on Protocol?
86
102
 
103
+ private async _init(protocol: Protocol) {
104
+ this.initCalled = true;
105
+ }
106
+
87
107
  /**
88
108
  * Send/Receive messages with peer when initiating a request/response interaction.
89
109
  * @param peerId Must be the value passed to the constructor on the responding node.
@@ -92,38 +112,38 @@ export class TestProtocolPlugin extends EventEmitter {
92
112
  */
93
113
  async send(peerId: Buffer, payload: string): Promise<string> {
94
114
  assert(Buffer.isBuffer(peerId));
95
- const peerIdStr = PublicKey.stringify(peerId);
115
+ const peerIdStr = PublicKey.stringify(peerId); // TODO(burdon): PublicKey.
96
116
  const peer = this._peers.get(peerIdStr);
97
- // TODO(dboreham): Throw fatal error if peer not found.
117
+ if (!peer) {
118
+ throw new Error(`peer not found: ${PublicKey.from(peerId).truncate()}`);
119
+ }
120
+
98
121
  const extension = peer.getExtension(EXTENSION_NAME);
99
122
  const encoded = Buffer.from(payload);
100
123
 
101
- log('sent', { peerIdStr, payload });
124
+ log('sending', { peerId: PublicKey.from(peerId), payload });
102
125
  return await extension.send(encoded, { oneway: true });
103
126
  }
104
127
 
105
- private async _init(protocol: Protocol) {
106
- this.initCalled = true;
107
- }
108
-
109
128
  async _receive(protocol: Protocol, data: any) {
110
129
  const peerId = getPeerId(protocol);
111
130
  assert(peerId !== undefined);
112
- const peerIdStr = PublicKey.stringify(peerId);
113
131
  let payload = data.data.toString();
114
132
  if (this._uppercase) {
115
133
  payload = payload.toUpperCase();
116
134
  }
117
135
 
118
- log('received', { peerIdStr, payload });
136
+ log('received', { peerId: PublicKey.from(peerId), payload });
119
137
  this.emit('receive', protocol, payload);
120
138
  }
121
139
 
122
140
  async _onPeerConnect(protocol: Protocol) {
141
+ log('connecting', { id: PublicKey.from(protocol.id) });
123
142
  const peerId = getPeerId(protocol);
124
143
  if (peerId === undefined) {
125
144
  return;
126
145
  }
146
+
127
147
  const peerIdStr = PublicKey.stringify(peerId);
128
148
  if (this._peers.has(peerIdStr)) {
129
149
  return;
@@ -134,6 +154,7 @@ export class TestProtocolPlugin extends EventEmitter {
134
154
  }
135
155
 
136
156
  async _onPeerDisconnect(protocol: Protocol) {
157
+ log('disconnecting', { id: PublicKey.from(protocol.id) });
137
158
  const peerId = getPeerId(protocol);
138
159
  if (peerId === undefined) {
139
160
  return;
@@ -143,16 +164,3 @@ export class TestProtocolPlugin extends EventEmitter {
143
164
  this.emit('disconnect', peerId);
144
165
  }
145
166
  }
146
-
147
- /**
148
- * @return {ProtocolProvider}
149
- */
150
- // TODO(dboreham): Try to encapsulate swarm_key, nodeId.
151
- export const testProtocolProvider = (swarmKey: Buffer, peerId: PublicKey, protocolPlugin: any) => {
152
- log('creating protocol factory', { swarmKey: PublicKey.from(swarmKey), peerId });
153
- return protocolFactory({
154
- getTopics: () => [swarmKey],
155
- session: { peerId: peerId.toHex() },
156
- plugins: [protocolPlugin]
157
- });
158
- };
@@ -0,0 +1,168 @@
1
+ //
2
+ // Copyright 2021 DXOS.org
3
+ //
4
+
5
+ import { expect } from 'chai';
6
+ import waitForExpect from 'wait-for-expect';
7
+
8
+ import { latch } from '@dxos/async';
9
+ import { PublicKey } from '@dxos/keys';
10
+ import { log } from '@dxos/log';
11
+ import { Protocol } from '@dxos/mesh-protocol';
12
+ import { range } from '@dxos/util';
13
+
14
+ import { TestBuilder } from '../testing';
15
+ import { FullyConnectedTopology, StarTopology } from '../topology';
16
+ import { exchangeMessages, joinSwarm, leaveSwarm, openAndCloseAfterTest } from './utils';
17
+
18
+ // TODO(burdon): Use PublicKey throughout (remove conversion to strings, from buffers, etc.)
19
+
20
+ /**
21
+ * Basic swarm tests.
22
+ */
23
+ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
24
+ if (runTests) {
25
+ return;
26
+ }
27
+
28
+ it('joins swarm, sends messages, and cleanly exits', async () => {
29
+ const peer1 = testBuilder.createPeer();
30
+ const peer2 = testBuilder.createPeer();
31
+ await openAndCloseAfterTest([peer1, peer2]);
32
+
33
+ const topic = PublicKey.random();
34
+ const [swarm1, swarm2] = await joinSwarm([peer1, peer2], topic, () => new FullyConnectedTopology());
35
+ await exchangeMessages(swarm1, swarm2);
36
+ await leaveSwarm([peer1, peer2], topic);
37
+ });
38
+
39
+ // TODO(burdon): Test with more peers (configure and test messaging).
40
+ it('joins swarm with star topology', async () => {
41
+ const peer1 = testBuilder.createPeer();
42
+ const peer2 = testBuilder.createPeer();
43
+ await openAndCloseAfterTest([peer1, peer2]);
44
+
45
+ const topic = PublicKey.random();
46
+ const [swarm1, swarm2] = await joinSwarm([peer1, peer2], topic, () => new StarTopology(peer1.peerId)); // NOTE: Same peer.
47
+ await exchangeMessages(swarm1, swarm2);
48
+ await leaveSwarm([peer1, peer2], topic);
49
+ });
50
+
51
+ // TODO(burdon): Fails when trying to reconnect to same topic.
52
+ it('joins swarm multiple times', async () => {
53
+ const peer1 = testBuilder.createPeer();
54
+ const peer2 = testBuilder.createPeer();
55
+ await openAndCloseAfterTest([peer1, peer2]);
56
+
57
+ const topic1 = PublicKey.random();
58
+
59
+ {
60
+ const [swarm1, swarm2] = await joinSwarm([peer1, peer2], topic1);
61
+ await exchangeMessages(swarm1, swarm2);
62
+ await leaveSwarm([peer1, peer2], topic1);
63
+ }
64
+
65
+ // TODO(burdon): Add log marker like this to logging lib. Auto add between tests?
66
+ // TODO(burdon): Enable new Error to take second context obj: new Error('msg', {}).
67
+ log('————————————————————————————————————————');
68
+
69
+ const topic2 = PublicKey.random();
70
+
71
+ {
72
+ const [swarm1, swarm2] = await joinSwarm([peer1, peer2], topic2);
73
+ await exchangeMessages(swarm1, swarm2);
74
+ await leaveSwarm([peer1, peer2], topic2);
75
+ }
76
+ });
77
+
78
+ it.skip('joins multiple swarms', async () => {
79
+ // TODO(burdon): N peers.
80
+ // TODO(burdon): Merge with test below.
81
+ const peer1 = testBuilder.createPeer();
82
+ const peer2 = testBuilder.createPeer();
83
+ await openAndCloseAfterTest([peer1, peer2]);
84
+
85
+ const numSwarms = 5;
86
+ const topics = Array.from(Array(numSwarms)).map(() => PublicKey.random());
87
+ expect(topics).to.have.length(numSwarms);
88
+ });
89
+
90
+ // TODO(burdon): Fails with WebRTC.
91
+ // TODO(burdon): This just tests multiple swarms (not peers in the same swarm)?
92
+ // Generalize for n swarms and factor into other tests; configure message activity.
93
+ it.skip('joins multiple swarms concurrently', async () => {
94
+ const createSwarm = async (messages: any[], label: string) => {
95
+ const topicA = PublicKey.random();
96
+ const peer1a = testBuilder.createPeer();
97
+ const peer2a = testBuilder.createPeer();
98
+
99
+ const swarm1a = await peer1a.createSwarm(topicA).join();
100
+ const swarm2a = await peer2a.createSwarm(topicA).join();
101
+
102
+ swarm1a.plugin.on('receive', (_, msg: string) => {
103
+ messages.push(msg);
104
+ });
105
+ swarm2a.plugin.on('connect', async () => {
106
+ await swarm2a.plugin.send(peer1a.peerId.asBuffer(), label);
107
+ });
108
+ };
109
+
110
+ const receivedA: any[] = [];
111
+ const receivedB: any[] = [];
112
+
113
+ void createSwarm(receivedA, 'Swarm A');
114
+ void createSwarm(receivedB, 'Swarm B');
115
+
116
+ // TODO(burdon): Replace with triggers.
117
+ await waitForExpect(() => {
118
+ expect(receivedA).to.have.length(1);
119
+ expect(receivedB).to.have.length(1);
120
+
121
+ expect(receivedA[0]).to.eq('Swarm A');
122
+ expect(receivedB[0]).to.eq('Swarm B');
123
+ });
124
+ });
125
+
126
+ // TODO(burdon): Fails with WebRTC.
127
+ // TODO(burdon): Factor out components of test.
128
+ it.skip('many peers and connections', async () => {
129
+ const numTopics = 5;
130
+ const peersPerTopic = 5;
131
+
132
+ await Promise.all(
133
+ range(numTopics).map(async () => {
134
+ const topic = PublicKey.random();
135
+
136
+ await Promise.all(
137
+ range(peersPerTopic).map(async () => {
138
+ const peer = testBuilder.createPeer();
139
+ const swarm = await peer.createSwarm(topic).join();
140
+
141
+ const [done, pongReceived] = latch({ count: peersPerTopic - 1 });
142
+
143
+ swarm.plugin.on('connect', async (protocol: Protocol) => {
144
+ const { peerId } = protocol.getSession() ?? {};
145
+ const remoteId = PublicKey.from(peerId);
146
+ await swarm.plugin.send(remoteId.asBuffer(), 'ping');
147
+ });
148
+
149
+ swarm.plugin.on('receive', async (protocol: Protocol, data: any) => {
150
+ const { peerId } = protocol.getSession() ?? {};
151
+ const remoteId = PublicKey.from(peerId);
152
+
153
+ if (data === 'ping') {
154
+ await swarm.plugin.send(remoteId.asBuffer(), 'pong');
155
+ } else if (data === 'pong') {
156
+ pongReceived();
157
+ } else {
158
+ throw new Error(`Invalid message: ${data}`);
159
+ }
160
+ });
161
+
162
+ await done();
163
+ })
164
+ );
165
+ })
166
+ );
167
+ });
168
+ };
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2021 DXOS.org
3
+ //
4
+
5
+ import { TestBuilder } from '../testing';
6
+ import { basicTestSuite } from './basic-test-suite';
7
+
8
+ describe('Memory transport', function () {
9
+ const testBuilder = new TestBuilder();
10
+ basicTestSuite(testBuilder);
11
+ });
@@ -2,7 +2,7 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import expect from 'expect';
5
+ import { expect } from 'chai';
6
6
  import waitForExpect from 'wait-for-expect';
7
7
 
8
8
  import { PublicKey } from '@dxos/keys';
@@ -10,13 +10,14 @@ import { MemorySignalManagerContext, MemorySignalManager } from '@dxos/messaging
10
10
  import { PresencePlugin } from '@dxos/protocol-plugin-presence';
11
11
  import { afterTest } from '@dxos/testutils';
12
12
 
13
- import { NetworkManager } from './network-manager';
14
- import { createProtocolFactory } from './protocol-factory';
15
- import { FullyConnectedTopology } from './topology';
16
- import { MemoryTransportFactory } from './transport';
13
+ import { NetworkManager } from '../network-manager';
14
+ import { createProtocolFactory } from '../protocol-factory';
15
+ import { FullyConnectedTopology } from '../topology';
16
+ import { MemoryTransportFactory } from '../transport';
17
17
 
18
18
  const signalContext = new MemorySignalManagerContext();
19
19
 
20
+ // TODO(burdon): Move to TestBuilder (configure plugins).
20
21
  const createPeer = async (topic: PublicKey) => {
21
22
  const peerId = PublicKey.random();
22
23
 
@@ -24,12 +25,12 @@ const createPeer = async (topic: PublicKey) => {
24
25
  signalManager: new MemorySignalManager(signalContext),
25
26
  transportFactory: MemoryTransportFactory
26
27
  });
27
- afterTest(() => networkManager.destroy());
28
+ afterTest(() => networkManager.close());
28
29
 
29
30
  const presencePlugin = new PresencePlugin(peerId.asBuffer());
30
31
  afterTest(() => presencePlugin.stop());
31
32
 
32
- await networkManager.openSwarmConnection({
33
+ await networkManager.joinSwarm({
33
34
  peerId,
34
35
  protocol: createProtocolFactory(topic, peerId, [presencePlugin]),
35
36
  topic,
@@ -40,38 +41,38 @@ const createPeer = async (topic: PublicKey) => {
40
41
  };
41
42
 
42
43
  describe('Presence', function () {
43
- it('sees connected peers', async function () {
44
+ it('detects connected peers', async function () {
45
+ // TODO(burdon): Configure plugins.
46
+ // const testBuilder = new TestBuilder();
47
+ // const peer1 = testBuilder.createPeer();
48
+ // const peer2 = testBuilder.createPeer();
49
+
44
50
  const topic = PublicKey.random();
45
51
  const peer1 = await createPeer(topic);
46
52
  const peer2 = await createPeer(topic);
53
+ // await peer1.joinSwarm(topic);
54
+ // await peer2.joinSwarm(topic);
47
55
 
56
+ // TODO(burdon): Use triggers instead of waitForExpect.
48
57
  await waitForExpect(() => {
49
- expect(peer1.presence.peers.map((key) => key.toString('hex')).sort()).toEqual(
58
+ expect(peer1.presence.peers.map((key) => key.toString('hex')).sort()).to.deep.eq(
50
59
  [peer1, peer2].map((key) => key.peerId.toHex()).sort()
51
60
  );
52
61
 
53
- expect(peer2.presence.peers.map((key) => key.toString('hex')).sort()).toEqual(
62
+ expect(peer2.presence.peers.map((key) => key.toString('hex')).sort()).to.deep.eq(
54
63
  [peer1, peer2].map((key) => key.peerId.toHex()).sort()
55
64
  );
56
65
  });
57
- });
58
66
 
59
- it('removes disconnected peers', async function () {
60
- const topic = PublicKey.random();
61
- const peer1 = await createPeer(topic);
62
- const peer2 = await createPeer(topic);
67
+ await peer2.networkManager.leaveSwarm(topic);
63
68
 
64
69
  await waitForExpect(() => {
65
- expect(peer1.presence.peers.map((key) => key.toString('hex')).sort()).toEqual(
66
- [peer1, peer2].map((key) => key.peerId.toHex()).sort()
70
+ expect(peer1.presence.peers.map((key) => key.toString('hex')).sort()).to.deep.eq(
71
+ [peer1].map((key) => key.peerId.toHex()).sort()
67
72
  );
68
- });
69
73
 
70
- await peer2.networkManager.closeSwarmConnection(topic);
71
-
72
- await waitForExpect(() => {
73
- expect(peer1.presence.peers.map((key) => key.toString('hex')).sort()).toEqual(
74
- [peer1].map((x) => x.peerId.toHex()).sort()
74
+ expect(peer2.presence.peers.map((key) => key.toString('hex')).sort()).to.deep.eq(
75
+ [peer2].map((key) => key.peerId.toHex()).sort()
75
76
  );
76
77
  });
77
78
  });