@dxos/messaging 0.6.8-main.3be982f → 0.6.8-staging.63bcb81
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/lib/browser/index.mjs +466 -199
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +456 -197
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/messenger.blueprint-test.d.ts +4 -0
- package/dist/types/src/messenger.blueprint-test.d.ts.map +1 -0
- package/dist/types/src/messenger.d.ts +4 -10
- package/dist/types/src/messenger.d.ts.map +1 -1
- package/dist/types/src/signal-client/signal-client.d.ts +6 -10
- package/dist/types/src/signal-client/signal-client.d.ts.map +1 -1
- package/dist/types/src/signal-client/signal-local-state.d.ts +2 -10
- package/dist/types/src/signal-client/signal-local-state.d.ts.map +1 -1
- package/dist/types/src/signal-manager/edge-signal-manager.d.ts +18 -19
- package/dist/types/src/signal-manager/edge-signal-manager.d.ts.map +1 -1
- package/dist/types/src/signal-manager/index.d.ts +1 -0
- package/dist/types/src/signal-manager/index.d.ts.map +1 -1
- package/dist/types/src/signal-manager/memory-signal-manager.d.ts +14 -25
- package/dist/types/src/signal-manager/memory-signal-manager.d.ts.map +1 -1
- package/dist/types/src/signal-manager/signal-manager.d.ts +5 -6
- package/dist/types/src/signal-manager/signal-manager.d.ts.map +1 -1
- package/dist/types/src/signal-manager/websocket-signal-manager.d.ts +14 -29
- package/dist/types/src/signal-manager/websocket-signal-manager.d.ts.map +1 -1
- package/dist/types/src/signal-methods.d.ts +29 -14
- package/dist/types/src/signal-methods.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +5 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/test-builder.d.ts +19 -0
- package/dist/types/src/testing/test-builder.d.ts.map +1 -0
- package/dist/types/src/testing/test-messages.d.ts +6 -0
- package/dist/types/src/testing/test-messages.d.ts.map +1 -0
- package/dist/types/src/testing/test-peer.d.ts +23 -0
- package/dist/types/src/testing/test-peer.d.ts.map +1 -0
- package/dist/types/src/testing/utils.d.ts +10 -0
- package/dist/types/src/testing/utils.d.ts.map +1 -0
- package/package.json +15 -14
- package/src/messenger.blueprint-test.ts +346 -0
- package/src/messenger.test.ts +26 -362
- package/src/messenger.ts +20 -18
- package/src/signal-client/signal-client-monitor.ts +3 -3
- package/src/signal-client/signal-client.test.ts +56 -52
- package/src/signal-client/signal-client.ts +30 -16
- package/src/signal-client/signal-local-state.ts +28 -10
- package/src/signal-manager/edge-signal-manager.test.ts +31 -35
- package/src/signal-manager/edge-signal-manager.ts +88 -36
- package/src/signal-manager/index.ts +1 -0
- package/src/signal-manager/memory-signal-manager.ts +36 -50
- package/src/signal-manager/signal-manager.ts +5 -7
- package/src/signal-manager/websocket-signal-manager.test.ts +27 -31
- package/src/signal-manager/websocket-signal-manager.ts +34 -65
- package/src/signal-methods.ts +33 -11
- package/src/testing/index.ts +8 -0
- package/src/testing/test-builder.ts +54 -0
- package/src/testing/test-messages.ts +24 -0
- package/src/testing/test-peer.ts +66 -0
- package/src/testing/utils.ts +50 -0
- package/dist/types/src/testing.d.ts +0 -40
- package/dist/types/src/testing.d.ts.map +0 -1
- package/src/testing.ts +0 -120
|
@@ -3,20 +3,25 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Event, sleep, synchronized } from '@dxos/async';
|
|
6
|
-
import {
|
|
7
|
-
import { Context } from '@dxos/context';
|
|
6
|
+
import { LifecycleState, Resource } from '@dxos/context';
|
|
8
7
|
import { invariant } from '@dxos/invariant';
|
|
9
8
|
import { PublicKey } from '@dxos/keys';
|
|
10
9
|
import { log } from '@dxos/log';
|
|
11
10
|
import { RateLimitExceededError, TimeoutError, trace } from '@dxos/protocols';
|
|
12
11
|
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
13
|
-
import { type SwarmEvent } from '@dxos/protocols/proto/dxos/mesh/signal';
|
|
14
12
|
import { BitField, safeAwaitAll } from '@dxos/util';
|
|
15
13
|
|
|
16
14
|
import { type SignalManager } from './signal-manager';
|
|
17
15
|
import { WebsocketSignalManagerMonitor } from './websocket-signal-manager-monitor';
|
|
18
16
|
import { SignalClient } from '../signal-client';
|
|
19
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
type PeerInfo,
|
|
19
|
+
type Message,
|
|
20
|
+
type SignalClientMethods,
|
|
21
|
+
type SignalMethods,
|
|
22
|
+
type SignalStatus,
|
|
23
|
+
type SwarmEvent,
|
|
24
|
+
} from '../signal-methods';
|
|
20
25
|
|
|
21
26
|
const MAX_SERVER_FAILURES = 5;
|
|
22
27
|
const WSS_SIGNAL_SERVER_REBOOT_DELAY = 3_000;
|
|
@@ -24,7 +29,7 @@ const WSS_SIGNAL_SERVER_REBOOT_DELAY = 3_000;
|
|
|
24
29
|
/**
|
|
25
30
|
* Manages connection to multiple Signal Servers over WebSocket
|
|
26
31
|
*/
|
|
27
|
-
export class WebsocketSignalManager implements SignalManager {
|
|
32
|
+
export class WebsocketSignalManager extends Resource implements SignalManager {
|
|
28
33
|
private readonly _servers = new Map<string, SignalClientMethods>();
|
|
29
34
|
private readonly _monitor = new WebsocketSignalManagerMonitor();
|
|
30
35
|
|
|
@@ -33,21 +38,11 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
33
38
|
*/
|
|
34
39
|
private readonly _failedServersBitfield: Uint8Array;
|
|
35
40
|
|
|
36
|
-
private _ctx!: Context;
|
|
37
|
-
private _opened = false;
|
|
38
|
-
|
|
39
41
|
readonly failureCount = new Map<string, number>();
|
|
40
42
|
readonly statusChanged = new Event<SignalStatus[]>();
|
|
41
|
-
readonly swarmEvent = new Event<
|
|
42
|
-
topic: PublicKey;
|
|
43
|
-
swarmEvent: SwarmEvent;
|
|
44
|
-
}>();
|
|
43
|
+
readonly swarmEvent = new Event<SwarmEvent>();
|
|
45
44
|
|
|
46
|
-
readonly onMessage = new Event<
|
|
47
|
-
author: PublicKey;
|
|
48
|
-
recipient: PublicKey;
|
|
49
|
-
payload: Any;
|
|
50
|
-
}>();
|
|
45
|
+
readonly onMessage = new Event<Message>();
|
|
51
46
|
|
|
52
47
|
private readonly _instanceId = PublicKey.random().toHex();
|
|
53
48
|
|
|
@@ -55,6 +50,7 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
55
50
|
private readonly _hosts: Runtime.Services.Signal[],
|
|
56
51
|
private readonly _getMetadata?: () => any,
|
|
57
52
|
) {
|
|
53
|
+
super();
|
|
58
54
|
log('Created WebsocketSignalManager', { hosts: this._hosts });
|
|
59
55
|
for (const host of this._hosts) {
|
|
60
56
|
if (this._servers.has(host.server)) {
|
|
@@ -74,35 +70,22 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
74
70
|
this._failedServersBitfield = BitField.zeros(this._hosts.length);
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
async open() {
|
|
79
|
-
if (this._opened) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
73
|
+
protected override async _open() {
|
|
82
74
|
log('open signal manager', { hosts: this._hosts });
|
|
83
75
|
log.trace('dxos.mesh.websocket-signal-manager.open', trace.begin({ id: this._instanceId }));
|
|
84
76
|
|
|
85
|
-
this._initContext();
|
|
86
|
-
|
|
87
77
|
await safeAwaitAll(this._servers.values(), (server) => server.open());
|
|
88
78
|
|
|
89
|
-
this._opened = true;
|
|
90
79
|
log.trace('dxos.mesh.websocket-signal-manager.open', trace.end({ id: this._instanceId }));
|
|
91
80
|
}
|
|
92
81
|
|
|
93
|
-
|
|
94
|
-
async close() {
|
|
95
|
-
if (!this._opened) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
this._opened = false;
|
|
99
|
-
await this._ctx.dispose();
|
|
82
|
+
protected override async _close() {
|
|
100
83
|
await safeAwaitAll(this._servers.values(), (server) => server.close());
|
|
101
84
|
}
|
|
102
85
|
|
|
103
86
|
async restartServer(serverName: string) {
|
|
104
87
|
log('restarting server', { serverName });
|
|
105
|
-
invariant(this.
|
|
88
|
+
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
106
89
|
|
|
107
90
|
const server = this._servers.get(serverName);
|
|
108
91
|
invariant(server, 'server not found');
|
|
@@ -117,30 +100,22 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
117
100
|
}
|
|
118
101
|
|
|
119
102
|
@synchronized
|
|
120
|
-
async join({ topic,
|
|
121
|
-
log('join', { topic,
|
|
122
|
-
invariant(this.
|
|
123
|
-
await this._forEachServer((server) => server.join({ topic,
|
|
103
|
+
async join({ topic, peer }: { topic: PublicKey; peer: PeerInfo }) {
|
|
104
|
+
log('join', { topic, peer });
|
|
105
|
+
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
106
|
+
await this._forEachServer((server) => server.join({ topic, peer }));
|
|
124
107
|
}
|
|
125
108
|
|
|
126
109
|
@synchronized
|
|
127
|
-
async leave({ topic,
|
|
128
|
-
log('leaving', { topic,
|
|
129
|
-
invariant(this.
|
|
130
|
-
await this._forEachServer((server) => server.leave({ topic,
|
|
110
|
+
async leave({ topic, peer }: { topic: PublicKey; peer: PeerInfo }) {
|
|
111
|
+
log('leaving', { topic, peer });
|
|
112
|
+
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
113
|
+
await this._forEachServer((server) => server.leave({ topic, peer }));
|
|
131
114
|
}
|
|
132
115
|
|
|
133
|
-
async sendMessage({
|
|
134
|
-
author,
|
|
135
|
-
recipient,
|
|
136
|
-
payload,
|
|
137
|
-
}: {
|
|
138
|
-
author: PublicKey;
|
|
139
|
-
recipient: PublicKey;
|
|
140
|
-
payload: Any;
|
|
141
|
-
}): Promise<void> {
|
|
116
|
+
async sendMessage({ author, recipient, payload }: Message): Promise<void> {
|
|
142
117
|
log('signal', { recipient });
|
|
143
|
-
invariant(this.
|
|
118
|
+
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
144
119
|
|
|
145
120
|
void this._forEachServer(async (server, serverName, index) => {
|
|
146
121
|
void server
|
|
@@ -187,24 +162,18 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
187
162
|
}
|
|
188
163
|
}
|
|
189
164
|
|
|
190
|
-
async subscribeMessages(
|
|
191
|
-
log('subscribed for message stream', {
|
|
192
|
-
invariant(this.
|
|
165
|
+
async subscribeMessages(peer: PeerInfo) {
|
|
166
|
+
log('subscribed for message stream', { peer });
|
|
167
|
+
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
193
168
|
|
|
194
|
-
await this._forEachServer(async (server) => server.subscribeMessages(
|
|
169
|
+
await this._forEachServer(async (server) => server.subscribeMessages(peer));
|
|
195
170
|
}
|
|
196
171
|
|
|
197
|
-
async unsubscribeMessages(
|
|
198
|
-
log('subscribed for message stream', {
|
|
199
|
-
invariant(this.
|
|
172
|
+
async unsubscribeMessages(peer: PeerInfo) {
|
|
173
|
+
log('subscribed for message stream', { peer });
|
|
174
|
+
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
200
175
|
|
|
201
|
-
await this._forEachServer(async (server) => server.unsubscribeMessages(
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private _initContext() {
|
|
205
|
-
this._ctx = new Context({
|
|
206
|
-
onError: (err) => log.catch(err),
|
|
207
|
-
});
|
|
176
|
+
await this._forEachServer(async (server) => server.unsubscribeMessages(peer));
|
|
208
177
|
}
|
|
209
178
|
|
|
210
179
|
private async _forEachServer<ReturnType>(
|
package/src/signal-methods.ts
CHANGED
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Event } from '@dxos/async';
|
|
6
|
+
import { type Lifecycle } from '@dxos/context';
|
|
6
7
|
import { type PublicKey } from '@dxos/keys';
|
|
7
|
-
import { type
|
|
8
|
+
import { type Peer } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
9
|
+
import { type SignalState } from '@dxos/protocols/proto/dxos/mesh/signal';
|
|
10
|
+
|
|
11
|
+
export type PeerInfo = Partial<Peer> & Required<Pick<Peer, 'peerKey'>>;
|
|
12
|
+
export const PeerInfoHash = ({ peerKey }: PeerInfo) => peerKey;
|
|
8
13
|
|
|
9
14
|
export interface Message {
|
|
10
|
-
author:
|
|
11
|
-
recipient:
|
|
15
|
+
author: PeerInfo;
|
|
16
|
+
recipient: PeerInfo;
|
|
12
17
|
payload: {
|
|
13
18
|
type_url: string;
|
|
14
19
|
value: Uint8Array;
|
|
@@ -24,6 +29,25 @@ export type SignalStatus = {
|
|
|
24
29
|
lastStateChange: Date;
|
|
25
30
|
};
|
|
26
31
|
|
|
32
|
+
export type SwarmEvent = {
|
|
33
|
+
topic: PublicKey;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The peer was announced as available on the swarm.
|
|
37
|
+
*/
|
|
38
|
+
peerAvailable?: {
|
|
39
|
+
peer: PeerInfo;
|
|
40
|
+
since: Date;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The peer left, or their announcement timed out.
|
|
45
|
+
*/
|
|
46
|
+
peerLeft?: {
|
|
47
|
+
peer: PeerInfo;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
27
51
|
/**
|
|
28
52
|
* Message routing interface.
|
|
29
53
|
*/
|
|
@@ -31,7 +55,7 @@ export interface SignalMethods {
|
|
|
31
55
|
/**
|
|
32
56
|
* Emits when other peers join or leave the swarm.
|
|
33
57
|
*/
|
|
34
|
-
swarmEvent: Event<
|
|
58
|
+
swarmEvent: Event<SwarmEvent>;
|
|
35
59
|
|
|
36
60
|
/**
|
|
37
61
|
* Emits when a message is received.
|
|
@@ -41,12 +65,12 @@ export interface SignalMethods {
|
|
|
41
65
|
/**
|
|
42
66
|
* Join topic on signal network, to be discoverable by other peers.
|
|
43
67
|
*/
|
|
44
|
-
join: (params: { topic: PublicKey;
|
|
68
|
+
join: (params: { topic: PublicKey; peer: PeerInfo }) => Promise<void>;
|
|
45
69
|
|
|
46
70
|
/**
|
|
47
71
|
* Leave topic on signal network, to stop being discoverable by other peers.
|
|
48
72
|
*/
|
|
49
|
-
leave: (params: { topic: PublicKey;
|
|
73
|
+
leave: (params: { topic: PublicKey; peer: PeerInfo }) => Promise<void>;
|
|
50
74
|
|
|
51
75
|
/**
|
|
52
76
|
* Send message to peer.
|
|
@@ -57,19 +81,17 @@ export interface SignalMethods {
|
|
|
57
81
|
* Start receiving messages from peer.
|
|
58
82
|
*/
|
|
59
83
|
// TODO(burdon): Return unsubscribe function. Encapsulate callback/routing here.
|
|
60
|
-
subscribeMessages: (
|
|
84
|
+
subscribeMessages: (peer: PeerInfo) => Promise<void>;
|
|
61
85
|
|
|
62
86
|
/**
|
|
63
87
|
* Stop receiving messages from peer.
|
|
64
88
|
*/
|
|
65
|
-
unsubscribeMessages: (
|
|
89
|
+
unsubscribeMessages: (peer: PeerInfo) => Promise<void>;
|
|
66
90
|
}
|
|
67
91
|
|
|
68
92
|
/**
|
|
69
93
|
* Signaling client.
|
|
70
94
|
*/
|
|
71
|
-
export interface SignalClientMethods extends SignalMethods {
|
|
72
|
-
open(): Promise<this>;
|
|
73
|
-
close(): Promise<this>;
|
|
95
|
+
export interface SignalClientMethods extends SignalMethods, Required<Lifecycle> {
|
|
74
96
|
getStatus(): SignalStatus;
|
|
75
97
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type PublicKey } from '@dxos/keys';
|
|
6
|
+
|
|
7
|
+
import { TestPeer } from './test-peer';
|
|
8
|
+
import { type SignalManager, MemorySignalManager, MemorySignalManagerContext } from '../signal-manager';
|
|
9
|
+
import { type Message } from '../signal-methods';
|
|
10
|
+
|
|
11
|
+
export type TestBuilderOptions = {
|
|
12
|
+
signalManagerFactory?: (identityKey: PublicKey, deviceKey: PublicKey) => Promise<SignalManager>;
|
|
13
|
+
messageDisruption?: (msg: Message) => Message[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class TestBuilder {
|
|
17
|
+
private _signalContext = new MemorySignalManagerContext();
|
|
18
|
+
private readonly _peers: TestPeer[] = [];
|
|
19
|
+
|
|
20
|
+
constructor(public options: TestBuilderOptions) {}
|
|
21
|
+
|
|
22
|
+
async createSignalManager(identityKey: PublicKey, deviceKey: PublicKey) {
|
|
23
|
+
const signalManager =
|
|
24
|
+
(await this.options.signalManagerFactory?.(identityKey, deviceKey)) ??
|
|
25
|
+
new MemorySignalManager(this._signalContext);
|
|
26
|
+
|
|
27
|
+
if (this.options.messageDisruption) {
|
|
28
|
+
// Imitates signal network disruptions (e. g. message doubling, ).
|
|
29
|
+
const trueSend = signalManager.sendMessage.bind(signalManager);
|
|
30
|
+
signalManager.sendMessage = async (message: Message) => {
|
|
31
|
+
for (const msg of this.options.messageDisruption!(message)) {
|
|
32
|
+
await trueSend(msg);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return signalManager;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async createPeer(): Promise<TestPeer> {
|
|
41
|
+
const peer = new TestPeer(this);
|
|
42
|
+
await peer.open();
|
|
43
|
+
this._peers.push(peer);
|
|
44
|
+
return peer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async createPeers(count: number): Promise<TestPeer[]> {
|
|
48
|
+
return Promise.all(Array.from({ length: count }, () => this.createPeer()));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async close() {
|
|
52
|
+
await Promise.all(this._peers.map((peer) => peer.close()));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type TaggedType } from '@dxos/codec-protobuf';
|
|
6
|
+
import { type TYPES } from '@dxos/protocols';
|
|
7
|
+
|
|
8
|
+
export const PAYLOAD_1: TaggedType<TYPES, 'google.protobuf.Any'> = {
|
|
9
|
+
'@type': 'google.protobuf.Any',
|
|
10
|
+
type_url: 'dxos.Example1',
|
|
11
|
+
value: Buffer.from('1'),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const PAYLOAD_2: TaggedType<TYPES, 'google.protobuf.Any'> = {
|
|
15
|
+
'@type': 'google.protobuf.Any',
|
|
16
|
+
type_url: 'dxos.Example2',
|
|
17
|
+
value: Buffer.from('2'),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const PAYLOAD_3: TaggedType<TYPES, 'google.protobuf.Any'> = {
|
|
21
|
+
'@type': 'google.protobuf.Any',
|
|
22
|
+
type_url: 'dxos.Example3',
|
|
23
|
+
value: Buffer.from('3'),
|
|
24
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Event } from '@dxos/async';
|
|
6
|
+
import { Resource } from '@dxos/context';
|
|
7
|
+
import { PublicKey } from '@dxos/keys';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
|
|
10
|
+
import { type TestBuilder } from './test-builder';
|
|
11
|
+
import { expectPeerAvailable, expectPeerLeft, expectReceivedMessage } from './utils';
|
|
12
|
+
import { Messenger } from '../messenger';
|
|
13
|
+
import { type SignalManager } from '../signal-manager';
|
|
14
|
+
import { type Message, type PeerInfo } from '../signal-methods';
|
|
15
|
+
|
|
16
|
+
export class TestPeer extends Resource {
|
|
17
|
+
public peerId = PublicKey.random();
|
|
18
|
+
public identityKey = PublicKey.random();
|
|
19
|
+
public signalManager!: SignalManager;
|
|
20
|
+
public messenger!: Messenger;
|
|
21
|
+
public defaultReceived = new Event<Message>();
|
|
22
|
+
|
|
23
|
+
constructor(private readonly testBuilder: TestBuilder) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get peerInfo(): PeerInfo {
|
|
28
|
+
return {
|
|
29
|
+
peerKey: this.peerId.toHex(),
|
|
30
|
+
identityKey: this.identityKey.toHex(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async waitTillReceive(message: Message) {
|
|
35
|
+
return expectReceivedMessage(this.defaultReceived, message);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async waitForPeerAvailable(topic: PublicKey, peer: PeerInfo) {
|
|
39
|
+
return expectPeerAvailable(this.signalManager, topic, peer);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async waitForPeerLeft(topic: PublicKey, peer: PeerInfo) {
|
|
43
|
+
return expectPeerLeft(this.signalManager, topic, peer);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected override async _open() {
|
|
47
|
+
this.signalManager = await this.testBuilder.createSignalManager(this.identityKey, this.peerId);
|
|
48
|
+
this.messenger = new Messenger({ signalManager: this.signalManager });
|
|
49
|
+
|
|
50
|
+
await this.signalManager.open();
|
|
51
|
+
this.messenger.open();
|
|
52
|
+
await this.messenger
|
|
53
|
+
.listen({
|
|
54
|
+
peer: this.peerInfo,
|
|
55
|
+
onMessage: async (msg) => {
|
|
56
|
+
this.defaultReceived.emit(msg);
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
.catch((err) => log.catch(err));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected override async _close() {
|
|
63
|
+
await this.messenger.close();
|
|
64
|
+
await this.signalManager.close();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { asyncTimeout, type Event } from '@dxos/async';
|
|
6
|
+
import { type Any } from '@dxos/codec-protobuf';
|
|
7
|
+
import { PublicKey } from '@dxos/keys';
|
|
8
|
+
|
|
9
|
+
import { PAYLOAD_1 } from './test-messages';
|
|
10
|
+
import { type SignalMethods, type Message, type PeerInfo } from '../signal-methods';
|
|
11
|
+
|
|
12
|
+
export const expectPeerAvailable = (client: SignalMethods, expectedTopic: PublicKey, peer: PeerInfo) =>
|
|
13
|
+
asyncTimeout(
|
|
14
|
+
client.swarmEvent.waitFor(
|
|
15
|
+
({ peerAvailable, topic }) =>
|
|
16
|
+
!!peerAvailable && peer.peerKey === peerAvailable.peer.peerKey && expectedTopic.equals(topic),
|
|
17
|
+
),
|
|
18
|
+
1000,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const expectPeerLeft = (client: SignalMethods, expectedTopic: PublicKey, peer: PeerInfo) =>
|
|
22
|
+
asyncTimeout(
|
|
23
|
+
client.swarmEvent.waitFor(
|
|
24
|
+
({ peerLeft, topic }) => !!peerLeft && peer.peerKey === peerLeft.peer.peerKey && expectedTopic.equals(topic),
|
|
25
|
+
),
|
|
26
|
+
1000,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const expectReceivedMessage = (event: Event<Message>, expectedMessage: Message) => {
|
|
30
|
+
return asyncTimeout(
|
|
31
|
+
event.waitFor(
|
|
32
|
+
(msg) =>
|
|
33
|
+
msg.author.peerKey === expectedMessage.author.peerKey &&
|
|
34
|
+
msg.recipient.peerKey === expectedMessage.recipient.peerKey &&
|
|
35
|
+
PublicKey.from(msg.payload.value).equals(expectedMessage.payload.value),
|
|
36
|
+
),
|
|
37
|
+
5000,
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const createMessage = (author: PeerInfo, recipient: PeerInfo, payload: Any = PAYLOAD_1): Message => ({
|
|
42
|
+
author,
|
|
43
|
+
recipient,
|
|
44
|
+
payload,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const messageEqual = (msg1: Message, msg2: Message) =>
|
|
48
|
+
msg1.author.peerKey === msg2.author.peerKey &&
|
|
49
|
+
msg1.recipient.peerKey === msg2.recipient.peerKey &&
|
|
50
|
+
PublicKey.from(msg1.payload.value).equals(msg2.payload.value);
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { Event } from '@dxos/async';
|
|
2
|
-
import { PublicKey } from '@dxos/keys';
|
|
3
|
-
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
4
|
-
import { Messenger } from './messenger';
|
|
5
|
-
import { type SignalManager } from './signal-manager';
|
|
6
|
-
import { type SignalMethods, type Message } from './signal-methods';
|
|
7
|
-
export type TestBuilderOptions = {
|
|
8
|
-
signalHosts?: Runtime.Services.Signal[];
|
|
9
|
-
messageDisruption?: (msg: Message) => Message[];
|
|
10
|
-
};
|
|
11
|
-
export declare class TestBuilder {
|
|
12
|
-
options: TestBuilderOptions;
|
|
13
|
-
private _signalContext;
|
|
14
|
-
private readonly _peers;
|
|
15
|
-
constructor(options: TestBuilderOptions);
|
|
16
|
-
createSignalManager(): SignalManager;
|
|
17
|
-
createPeer(): TestPeer;
|
|
18
|
-
close(): Promise<void>;
|
|
19
|
-
}
|
|
20
|
-
export declare class TestPeer {
|
|
21
|
-
private readonly testBuilder;
|
|
22
|
-
peerId: PublicKey;
|
|
23
|
-
signalManager: SignalManager;
|
|
24
|
-
messenger: Messenger;
|
|
25
|
-
defaultReceived: Event<Message>;
|
|
26
|
-
constructor(testBuilder: TestBuilder);
|
|
27
|
-
waitTillReceive(message: Message): Promise<Message>;
|
|
28
|
-
open(): Promise<void>;
|
|
29
|
-
close(): Promise<void>;
|
|
30
|
-
}
|
|
31
|
-
export declare const expectPeerAvailable: (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) => Promise<{
|
|
32
|
-
topic: PublicKey;
|
|
33
|
-
swarmEvent: import("packages/core/protocols/dist/esm/src/proto/gen/dxos/mesh/signal").SwarmEvent;
|
|
34
|
-
}>;
|
|
35
|
-
export declare const expectPeerLeft: (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) => Promise<{
|
|
36
|
-
topic: PublicKey;
|
|
37
|
-
swarmEvent: import("packages/core/protocols/dist/esm/src/proto/gen/dxos/mesh/signal").SwarmEvent;
|
|
38
|
-
}>;
|
|
39
|
-
export declare const expectReceivedMessage: (client: SignalMethods, expectedMessage: Message) => Promise<Message>;
|
|
40
|
-
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../src/testing.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IACxC,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,EAAE,CAAC;CACjD,CAAC;AAEF,qBAAa,WAAW;IAIH,OAAO,EAAE,kBAAkB;IAH9C,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAEtB,OAAO,EAAE,kBAAkB;IAE9C,mBAAmB;IAqBnB,UAAU,IAAI,QAAQ;IAMhB,KAAK;CAGZ;AAED,qBAAa,QAAQ;IAMP,OAAO,CAAC,QAAQ,CAAC,WAAW;IALjC,MAAM,YAAsB;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,iBAAwB;gBAEjB,WAAW,EAAE,WAAW;IAKrD,eAAe,CAAC,OAAO,EAAE,OAAO;IAS1B,IAAI;IAaJ,KAAK;CAIZ;AAED,eAAO,MAAM,mBAAmB,WAAY,aAAa,iBAAiB,SAAS,QAAQ,SAAS;;;EAIjG,CAAC;AAEJ,eAAO,MAAM,cAAc,WAAY,aAAa,iBAAiB,SAAS,QAAQ,SAAS;;;EAI5F,CAAC;AAEJ,eAAO,MAAM,qBAAqB,WAAY,aAAa,mBAAmB,OAAO,qBAOpF,CAAC"}
|
package/src/testing.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2022 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Event } from '@dxos/async';
|
|
6
|
-
import { PublicKey } from '@dxos/keys';
|
|
7
|
-
import { log } from '@dxos/log';
|
|
8
|
-
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
9
|
-
|
|
10
|
-
import { Messenger } from './messenger';
|
|
11
|
-
import {
|
|
12
|
-
type SignalManager,
|
|
13
|
-
MemorySignalManager,
|
|
14
|
-
MemorySignalManagerContext,
|
|
15
|
-
WebsocketSignalManager,
|
|
16
|
-
} from './signal-manager';
|
|
17
|
-
import { type SignalMethods, type Message } from './signal-methods';
|
|
18
|
-
|
|
19
|
-
export type TestBuilderOptions = {
|
|
20
|
-
signalHosts?: Runtime.Services.Signal[];
|
|
21
|
-
messageDisruption?: (msg: Message) => Message[];
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export class TestBuilder {
|
|
25
|
-
private _signalContext = new MemorySignalManagerContext();
|
|
26
|
-
private readonly _peers: TestPeer[] = [];
|
|
27
|
-
|
|
28
|
-
constructor(public options: TestBuilderOptions) {}
|
|
29
|
-
|
|
30
|
-
createSignalManager() {
|
|
31
|
-
let signalManager: SignalManager;
|
|
32
|
-
if (this.options.signalHosts) {
|
|
33
|
-
signalManager = new WebsocketSignalManager(this.options.signalHosts);
|
|
34
|
-
} else {
|
|
35
|
-
signalManager = new MemorySignalManager(this._signalContext);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (this.options.messageDisruption) {
|
|
39
|
-
// Imitates signal network disruptions (e. g. message doubling, ).
|
|
40
|
-
const trueSend = signalManager.sendMessage.bind(signalManager);
|
|
41
|
-
signalManager.sendMessage = async (message) => {
|
|
42
|
-
for (const msg of this.options.messageDisruption!(message)) {
|
|
43
|
-
await trueSend(msg);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return signalManager;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
createPeer(): TestPeer {
|
|
52
|
-
const peer = new TestPeer(this);
|
|
53
|
-
this._peers.push(peer);
|
|
54
|
-
return peer;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async close() {
|
|
58
|
-
await Promise.all(this._peers.map((peer) => peer.close()));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export class TestPeer {
|
|
63
|
-
public peerId = PublicKey.random();
|
|
64
|
-
public signalManager: SignalManager;
|
|
65
|
-
public messenger: Messenger;
|
|
66
|
-
public defaultReceived = new Event<Message>();
|
|
67
|
-
|
|
68
|
-
constructor(private readonly testBuilder: TestBuilder) {
|
|
69
|
-
this.signalManager = testBuilder.createSignalManager();
|
|
70
|
-
this.messenger = new Messenger({ signalManager: this.signalManager });
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
waitTillReceive(message: Message) {
|
|
74
|
-
return this.defaultReceived.waitFor(
|
|
75
|
-
(msg) =>
|
|
76
|
-
msg.author.equals(message.author) &&
|
|
77
|
-
msg.recipient.equals(message.recipient) &&
|
|
78
|
-
msg.payload.value.every((value, index) => value === message.payload.value[index]),
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async open() {
|
|
83
|
-
await this.signalManager.open();
|
|
84
|
-
this.messenger.open();
|
|
85
|
-
await this.messenger
|
|
86
|
-
.listen({
|
|
87
|
-
peerId: this.peerId,
|
|
88
|
-
onMessage: async (msg) => {
|
|
89
|
-
this.defaultReceived.emit(msg);
|
|
90
|
-
},
|
|
91
|
-
})
|
|
92
|
-
.catch((err) => log.catch(err));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async close() {
|
|
96
|
-
await this.messenger.close();
|
|
97
|
-
await this.signalManager.close();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const expectPeerAvailable = (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) =>
|
|
102
|
-
client.swarmEvent.waitFor(
|
|
103
|
-
({ swarmEvent, topic }) =>
|
|
104
|
-
!!swarmEvent.peerAvailable && peer.equals(swarmEvent.peerAvailable.peer) && expectedTopic.equals(topic),
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
export const expectPeerLeft = (client: SignalMethods, expectedTopic: PublicKey, peer: PublicKey) =>
|
|
108
|
-
client.swarmEvent.waitFor(
|
|
109
|
-
({ swarmEvent, topic }) =>
|
|
110
|
-
!!swarmEvent.peerLeft && peer.equals(swarmEvent.peerLeft.peer) && expectedTopic.equals(topic),
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
export const expectReceivedMessage = (client: SignalMethods, expectedMessage: Message) => {
|
|
114
|
-
return client.onMessage.waitFor(
|
|
115
|
-
(msg) =>
|
|
116
|
-
msg.author.equals(expectedMessage.author) &&
|
|
117
|
-
msg.recipient.equals(expectedMessage.recipient) &&
|
|
118
|
-
PublicKey.from(msg.payload.value).equals(expectedMessage.payload.value),
|
|
119
|
-
);
|
|
120
|
-
};
|