@dxos/network-manager 0.1.8 → 0.1.10
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.
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/network-manager.d.ts +10 -14
- package/dist/src/network-manager.d.ts.map +1 -1
- package/dist/src/network-manager.js +28 -33
- package/dist/src/network-manager.js.map +1 -1
- package/dist/src/protocol-factory.d.ts.map +1 -1
- package/dist/src/protocol-factory.js +8 -6
- package/dist/src/protocol-factory.js.map +1 -1
- package/dist/src/signal/index.d.ts +2 -2
- package/dist/src/signal/index.d.ts.map +1 -1
- package/dist/src/signal/index.js +2 -2
- package/dist/src/signal/index.js.map +1 -1
- package/dist/src/signal/message-router.d.ts +2 -2
- package/dist/src/signal/message-router.js +3 -3
- package/dist/src/signal/message-router.js.map +1 -1
- package/dist/src/signal/{signal-messaging.d.ts → signal-messenger.d.ts} +2 -2
- package/dist/src/signal/{signal-messaging.d.ts.map → signal-messenger.d.ts.map} +1 -1
- package/dist/src/signal/{signal-messaging.js → signal-messenger.js} +1 -1
- package/dist/src/signal/signal-messenger.js.map +1 -0
- package/dist/src/swarm/connection.d.ts +6 -3
- package/dist/src/swarm/connection.d.ts.map +1 -1
- package/dist/src/swarm/connection.js +28 -15
- package/dist/src/swarm/connection.js.map +1 -1
- package/dist/src/swarm/peer.d.ts +45 -0
- package/dist/src/swarm/peer.d.ts.map +1 -0
- package/dist/src/swarm/peer.js +113 -0
- package/dist/src/swarm/peer.js.map +1 -0
- package/dist/src/swarm/swarm-mapper.d.ts +2 -2
- package/dist/src/swarm/swarm-mapper.d.ts.map +1 -1
- package/dist/src/swarm/swarm-mapper.js +12 -10
- package/dist/src/swarm/swarm-mapper.js.map +1 -1
- package/dist/src/swarm/swarm.d.ts +13 -18
- package/dist/src/swarm/swarm.d.ts.map +1 -1
- package/dist/src/swarm/swarm.js +126 -97
- package/dist/src/swarm/swarm.js.map +1 -1
- package/dist/src/swarm/swarm.test.js +2 -2
- package/dist/src/swarm/swarm.test.js.map +1 -1
- package/dist/src/testing/test-builder.d.ts +31 -18
- package/dist/src/testing/test-builder.d.ts.map +1 -1
- package/dist/src/testing/test-builder.js +120 -48
- package/dist/src/testing/test-builder.js.map +1 -1
- package/dist/src/testing/test-protocol.d.ts +5 -5
- package/dist/src/testing/test-protocol.d.ts.map +1 -1
- package/dist/src/testing/test-protocol.js +28 -24
- package/dist/src/testing/test-protocol.js.map +1 -1
- package/dist/src/tests/basic-test-suite.d.ts +6 -0
- package/dist/src/tests/basic-test-suite.d.ts.map +1 -0
- package/dist/src/tests/basic-test-suite.js +142 -0
- package/dist/src/tests/basic-test-suite.js.map +1 -0
- package/dist/src/tests/memory-transport.test.d.ts +2 -0
- package/dist/src/tests/memory-transport.test.d.ts.map +1 -0
- package/dist/src/tests/memory-transport.test.js +12 -0
- package/dist/src/tests/memory-transport.test.js.map +1 -0
- package/dist/src/{presence.test.d.ts → tests/presence.test.d.ts} +0 -0
- package/dist/src/tests/presence.test.d.ts.map +1 -0
- package/dist/src/{presence.test.js → tests/presence.test.js} +21 -20
- package/dist/src/tests/presence.test.js.map +1 -0
- package/dist/src/tests/property-test-suite.d.ts +5 -0
- package/dist/src/tests/property-test-suite.d.ts.map +1 -0
- package/dist/src/tests/property-test-suite.js +203 -0
- package/dist/src/tests/property-test-suite.js.map +1 -0
- package/dist/src/tests/utils.d.ts +21 -0
- package/dist/src/tests/utils.d.ts.map +1 -0
- package/dist/src/tests/utils.js +76 -0
- package/dist/src/tests/utils.js.map +1 -0
- package/dist/src/tests/webrtc-transport.test.d.ts +2 -0
- package/dist/src/tests/webrtc-transport.test.d.ts.map +1 -0
- package/dist/src/tests/webrtc-transport.test.js +16 -0
- package/dist/src/tests/webrtc-transport.test.js.map +1 -0
- package/dist/src/topology/fully-connected-topology.d.ts +1 -1
- package/dist/src/topology/fully-connected-topology.d.ts.map +1 -1
- package/dist/src/topology/fully-connected-topology.js +4 -3
- package/dist/src/topology/fully-connected-topology.js.map +1 -1
- package/dist/src/topology/mmst-topology.js +4 -4
- package/dist/src/topology/mmst-topology.js.map +1 -1
- package/dist/src/topology/star-topology.js +4 -4
- package/dist/src/topology/star-topology.js.map +1 -1
- package/dist/src/topology/topology.d.ts +0 -5
- package/dist/src/topology/topology.d.ts.map +1 -1
- package/dist/src/transport/memory-transport.d.ts +8 -7
- package/dist/src/transport/memory-transport.d.ts.map +1 -1
- package/dist/src/transport/memory-transport.js +79 -45
- package/dist/src/transport/memory-transport.js.map +1 -1
- package/dist/src/transport/memory-transport.test.js +3 -2
- package/dist/src/transport/memory-transport.test.js.map +1 -1
- package/dist/src/transport/transport.d.ts +8 -4
- package/dist/src/transport/transport.d.ts.map +1 -1
- package/dist/src/transport/webrtc-simple-peer.test.d.ts +2 -0
- package/dist/src/transport/webrtc-simple-peer.test.d.ts.map +1 -0
- package/dist/src/transport/{wrtc-simple-peer.test.js → webrtc-simple-peer.test.js} +2 -2
- package/dist/src/transport/webrtc-simple-peer.test.js.map +1 -0
- package/dist/src/transport/webrtc-transport-proxy.d.ts +4 -4
- package/dist/src/transport/webrtc-transport-proxy.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport-proxy.js +15 -10
- package/dist/src/transport/webrtc-transport-proxy.js.map +1 -1
- package/dist/src/transport/webrtc-transport-proxy.test.js +2 -2
- package/dist/src/transport/webrtc-transport-proxy.test.js.map +1 -1
- package/dist/src/transport/webrtc-transport-service.js +2 -2
- package/dist/src/transport/webrtc-transport-service.js.map +1 -1
- package/dist/src/transport/webrtc-transport.d.ts +3 -3
- package/dist/src/transport/webrtc-transport.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport.js +10 -10
- package/dist/src/transport/webrtc-transport.js.map +1 -1
- package/dist/src/transport/webrtc-transport.test.js +3 -3
- package/dist/src/transport/webrtc-transport.test.js.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +25 -18
- package/src/index.ts +2 -2
- package/src/network-manager.ts +30 -45
- package/src/protocol-factory.ts +5 -3
- package/src/signal/index.ts +2 -2
- package/src/signal/integration.test.ts +1 -1
- package/src/signal/message-router.test.ts +1 -1
- package/src/signal/message-router.ts +2 -2
- package/src/signal/{signal-messaging.ts → signal-messenger.ts} +2 -2
- package/src/swarm/connection.ts +23 -12
- package/src/swarm/peer.ts +156 -0
- package/src/swarm/swarm-mapper.ts +21 -10
- package/src/swarm/swarm.ts +142 -111
- package/src/testing/test-builder.ts +143 -63
- package/src/testing/test-protocol.ts +31 -23
- package/src/tests/basic-test-suite.ts +168 -0
- package/src/tests/memory-transport.test.ts +11 -0
- package/src/{presence.test.ts → tests/presence.test.ts} +24 -23
- package/src/tests/property-test-suite.ts +221 -0
- package/src/tests/utils.ts +85 -0
- package/src/tests/webrtc-transport.test.ts +16 -0
- package/src/topology/fully-connected-topology.ts +5 -4
- package/src/topology/topology.ts +0 -5
- package/src/transport/memory-transport.test.ts +3 -2
- package/src/transport/memory-transport.ts +79 -49
- package/src/transport/transport.ts +9 -3
- package/src/transport/{wrtc-simple-peer.test.ts → webrtc-simple-peer.test.ts} +1 -1
- package/src/transport/webrtc-transport-proxy.test.ts +2 -2
- package/src/transport/webrtc-transport-proxy.ts +17 -8
- package/src/transport/webrtc-transport-service.ts +1 -1
- package/src/transport/webrtc-transport.test.ts +4 -6
- package/src/transport/webrtc-transport.ts +4 -4
- package/dist/src/network-manager.test.d.ts +0 -2
- package/dist/src/network-manager.test.d.ts.map +0 -1
- package/dist/src/network-manager.test.js +0 -596
- package/dist/src/network-manager.test.js.map +0 -1
- package/dist/src/presence.test.d.ts.map +0 -1
- package/dist/src/presence.test.js.map +0 -1
- package/dist/src/signal/signal-messaging.js.map +0 -1
- package/dist/src/transport/wrtc-simple-peer.test.d.ts +0 -2
- package/dist/src/transport/wrtc-simple-peer.test.d.ts.map +0 -1
- package/dist/src/transport/wrtc-simple-peer.test.js.map +0 -1
- 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 {
|
|
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
|
|
11
|
-
import {
|
|
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
|
-
|
|
26
|
+
signalHosts?: string[];
|
|
27
|
+
bridge?: boolean;
|
|
19
28
|
};
|
|
20
29
|
|
|
21
|
-
|
|
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(
|
|
36
|
+
constructor(public readonly options: TestBuilderOptions = {}) {}
|
|
26
37
|
|
|
27
38
|
createSignalManager() {
|
|
28
|
-
if (this.
|
|
29
|
-
return new WebsocketSignalManager(
|
|
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:
|
|
38
|
-
transportFactory
|
|
112
|
+
signalManager: this.testBuilder.createSignalManager(),
|
|
113
|
+
transportFactory
|
|
39
114
|
});
|
|
40
115
|
}
|
|
41
|
-
}
|
|
42
116
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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):
|
|
52
|
-
|
|
152
|
+
// TODO(burdon): Reconcile with new Swarm concept.
|
|
153
|
+
export class TestSwarmConnection {
|
|
154
|
+
plugin: TestProtocolPlugin;
|
|
53
155
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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
|
-
|
|
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('
|
|
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', {
|
|
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 '
|
|
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 '
|
|
14
|
-
import { createProtocolFactory } from '
|
|
15
|
-
import { FullyConnectedTopology } from '
|
|
16
|
-
import { MemoryTransportFactory } from '
|
|
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.
|
|
28
|
+
afterTest(() => networkManager.close());
|
|
28
29
|
|
|
29
30
|
const presencePlugin = new PresencePlugin(peerId.asBuffer());
|
|
30
31
|
afterTest(() => presencePlugin.stop());
|
|
31
32
|
|
|
32
|
-
await networkManager.
|
|
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('
|
|
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()).
|
|
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()).
|
|
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
|
-
|
|
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()).
|
|
66
|
-
[peer1
|
|
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
|
-
|
|
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
|
});
|