@dxos/client-services 0.1.35 → 0.1.36
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/{chunk-H5IXSSI7.mjs → chunk-2AYYVPWN.mjs} +835 -950
- package/dist/lib/browser/chunk-2AYYVPWN.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +9 -11
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +212 -74
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +942 -1058
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +1089 -1067
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/identity/identity-manager.d.ts +1 -0
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +3 -3
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +18 -0
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/device-invitation-protocol.test.d.ts +2 -0
- package/dist/types/src/packlets/invitations/device-invitation-protocol.test.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/index.d.ts +4 -4
- package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +12 -0
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +38 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +13 -15
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +22 -0
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/space-invitation-protocol.test.d.ts +2 -0
- package/dist/types/src/packlets/invitations/space-invitation-protocol.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-context.d.ts +6 -4
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +2 -0
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +31 -2
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +3 -3
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/tests/invitations.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/invitations.test.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts +2 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/package.json +30 -30
- package/src/packlets/identity/identity-manager.ts +2 -1
- package/src/packlets/identity/identity.ts +3 -3
- package/src/packlets/invitations/{device-invitations-handler.test.ts → device-invitation-protocol.test.ts} +3 -3
- package/src/packlets/invitations/device-invitation-protocol.ts +83 -0
- package/src/packlets/invitations/index.ts +4 -4
- package/src/packlets/invitations/invitation-protocol.ts +21 -0
- package/src/packlets/invitations/invitations-handler.ts +462 -0
- package/src/packlets/invitations/invitations-service.ts +127 -104
- package/src/packlets/invitations/space-invitation-protocol.test.ts +221 -0
- package/src/packlets/invitations/space-invitation-protocol.ts +117 -0
- package/src/packlets/services/service-context.test.ts +11 -4
- package/src/packlets/services/service-context.ts +40 -19
- package/src/packlets/services/service-host.ts +4 -12
- package/src/packlets/services/service-registry.test.ts +23 -25
- package/src/packlets/spaces/data-space-manager.ts +5 -0
- package/src/packlets/spaces/spaces-service.ts +7 -2
- package/src/packlets/storage/storage.ts +2 -0
- package/src/packlets/testing/invitation-utils.ts +219 -31
- package/src/packlets/testing/test-builder.ts +33 -22
- package/src/packlets/tests/client-services.test.ts +33 -76
- package/src/packlets/tests/halo-profile.test.ts +2 -28
- package/src/packlets/tests/invitations.test.ts +389 -0
- package/src/packlets/tests/spaces-invitations.test.ts +12 -30
- package/src/packlets/tests/spaces.test.ts +30 -28
- package/src/packlets/vault/worker-runtime.ts +4 -0
- package/dist/lib/browser/chunk-H5IXSSI7.mjs.map +0 -7
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts +0 -30
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/device-invitations-handler.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/device-invitations-handler.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts +0 -13
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/device-invitations-service.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts +0 -26
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/space-invitations-handler.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts +0 -16
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-service.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/space-invitations-service.test.d.ts.map +0 -1
- package/src/packlets/invitations/device-invitations-handler.ts +0 -370
- package/src/packlets/invitations/device-invitations-service.test.ts +0 -96
- package/src/packlets/invitations/device-invitations-service.ts +0 -24
- package/src/packlets/invitations/space-invitations-handler.test.ts +0 -246
- package/src/packlets/invitations/space-invitations-handler.ts +0 -463
- package/src/packlets/invitations/space-invitations-service.test.ts +0 -129
- package/src/packlets/invitations/space-invitations-service.ts +0 -35
|
@@ -7,13 +7,12 @@ import assert from 'node:assert';
|
|
|
7
7
|
import waitForExpect from 'wait-for-expect';
|
|
8
8
|
|
|
9
9
|
import { Trigger } from '@dxos/async';
|
|
10
|
-
import { Client, Space } from '@dxos/client';
|
|
11
|
-
import { raise } from '@dxos/debug';
|
|
10
|
+
import { Client, Space, SpaceProxy } from '@dxos/client';
|
|
12
11
|
import { log } from '@dxos/log';
|
|
13
12
|
import { Invitation, SpaceMember } from '@dxos/protocols/proto/dxos/client/services';
|
|
14
13
|
import { describe, test, afterTest } from '@dxos/test';
|
|
15
14
|
|
|
16
|
-
import { syncItems, TestBuilder } from '../testing';
|
|
15
|
+
import { performInvitation, syncItems, TestBuilder } from '../testing';
|
|
17
16
|
|
|
18
17
|
// TODO(burdon): Use as set-up for test suite.
|
|
19
18
|
// TODO(burdon): Timeouts and progress callback/events.
|
|
@@ -116,48 +115,20 @@ describe('Client services', () => {
|
|
|
116
115
|
await client1.halo.createIdentity();
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
observable1.subscribe({
|
|
127
|
-
onConnecting: (invitation) => {
|
|
128
|
-
const observable2 = client2.halo.acceptInvitation(invitation);
|
|
129
|
-
observable2.subscribe({
|
|
130
|
-
onAuthenticating: async () => {
|
|
131
|
-
await observable2.authenticate(await authenticationCode.wait());
|
|
132
|
-
},
|
|
133
|
-
onSuccess: (invitation: Invitation) => {
|
|
134
|
-
// TODO(burdon): No device.
|
|
135
|
-
// expect(guest.identityManager.identity!.authorizedDeviceKeys.size).to.eq(1);
|
|
136
|
-
success2.wake(invitation);
|
|
137
|
-
},
|
|
138
|
-
onError: (err: Error) => raise(new Error(err.message))
|
|
139
|
-
});
|
|
140
|
-
},
|
|
141
|
-
onConnected: (invitation: Invitation) => {
|
|
142
|
-
assert(invitation.authenticationCode);
|
|
143
|
-
authenticationCode.wake(invitation.authenticationCode);
|
|
144
|
-
},
|
|
145
|
-
onSuccess: (invitation: Invitation) => {
|
|
146
|
-
success1.wake(invitation);
|
|
147
|
-
},
|
|
148
|
-
onCancelled: () => raise(new Error()),
|
|
149
|
-
onTimeout: (err: Error) => raise(new Error(err.message)),
|
|
150
|
-
onError: (err: Error) => raise(new Error(err.message))
|
|
151
|
-
});
|
|
152
|
-
}
|
|
118
|
+
const [{ invitation: hostInvitation }, { invitation: guestInvitation }] = await Promise.all(
|
|
119
|
+
performInvitation({
|
|
120
|
+
host: client1.halo,
|
|
121
|
+
guest: client2.halo,
|
|
122
|
+
options: { authMethod: Invitation.AuthMethod.SHARED_SECRET }
|
|
123
|
+
})
|
|
124
|
+
);
|
|
153
125
|
|
|
154
126
|
// Check same identity.
|
|
155
|
-
|
|
156
|
-
expect(
|
|
157
|
-
expect(
|
|
158
|
-
expect(
|
|
159
|
-
expect(
|
|
160
|
-
expect(invitation2.state).to.eq(Invitation.State.SUCCESS);
|
|
127
|
+
expect(hostInvitation!.identityKey).not.to.exist;
|
|
128
|
+
expect(guestInvitation?.identityKey).to.deep.eq(client1.halo.identity.get()!.identityKey);
|
|
129
|
+
expect(guestInvitation?.identityKey).to.deep.eq(client2.halo.identity.get()!.identityKey);
|
|
130
|
+
expect(hostInvitation?.state).to.eq(Invitation.State.SUCCESS);
|
|
131
|
+
expect(guestInvitation?.state).to.eq(Invitation.State.SUCCESS);
|
|
161
132
|
|
|
162
133
|
// Check devices.
|
|
163
134
|
// TODO(burdon): Incorrect number of devices.
|
|
@@ -194,48 +165,34 @@ describe('Client services', () => {
|
|
|
194
165
|
afterTest(() => Promise.all([client1.destroy(), server1.close(), peer1.close()]));
|
|
195
166
|
afterTest(() => Promise.all([client2.destroy(), server2.close(), peer2.close()]));
|
|
196
167
|
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const observable2 = client2.acceptInvitation(invitation);
|
|
207
|
-
observable2.subscribe({
|
|
208
|
-
onSuccess: (invitation: Invitation) => {
|
|
209
|
-
success2.wake(invitation);
|
|
210
|
-
},
|
|
211
|
-
onError: (err: Error) => raise(err)
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
onSuccess: (invitation) => {
|
|
215
|
-
log('onSuccess');
|
|
216
|
-
success1.wake(invitation);
|
|
217
|
-
},
|
|
218
|
-
onError: (err) => raise(err)
|
|
219
|
-
});
|
|
168
|
+
const hostSpace = await client1.createSpace();
|
|
169
|
+
log('createSpace', { key: hostSpace.key });
|
|
170
|
+
const [{ invitation: hostInvitation }, { invitation: guestInvitation }] = await Promise.all(
|
|
171
|
+
performInvitation({
|
|
172
|
+
host: hostSpace as SpaceProxy,
|
|
173
|
+
guest: client2,
|
|
174
|
+
options: { authMethod: Invitation.AuthMethod.SHARED_SECRET }
|
|
175
|
+
})
|
|
176
|
+
);
|
|
220
177
|
|
|
221
|
-
|
|
222
|
-
expect(
|
|
223
|
-
expect(
|
|
178
|
+
expect(guestInvitation?.spaceKey).to.deep.eq(hostSpace.key);
|
|
179
|
+
expect(hostInvitation?.spaceKey).to.deep.eq(guestInvitation?.spaceKey);
|
|
180
|
+
expect(hostInvitation?.state).to.eq(Invitation.State.SUCCESS);
|
|
224
181
|
|
|
225
182
|
log('Invitation complete');
|
|
226
183
|
|
|
227
184
|
// TODO(burdon): Space should now be available?
|
|
228
185
|
const trigger = new Trigger<Space>();
|
|
229
186
|
await waitForExpect(() => {
|
|
230
|
-
const
|
|
231
|
-
assert(
|
|
232
|
-
expect(
|
|
233
|
-
trigger.wake(
|
|
187
|
+
const guestSpace = client2.getSpace(guestInvitation!.spaceKey!);
|
|
188
|
+
assert(guestSpace);
|
|
189
|
+
expect(guestSpace).to.exist;
|
|
190
|
+
trigger.wake(guestSpace);
|
|
234
191
|
});
|
|
235
192
|
|
|
236
|
-
const
|
|
193
|
+
const guestSpace = await trigger.wait();
|
|
237
194
|
|
|
238
|
-
for (const space of [
|
|
195
|
+
for (const space of [hostSpace, guestSpace]) {
|
|
239
196
|
await waitForExpect(() => {
|
|
240
197
|
expect(space.members.get()).to.deep.equal([
|
|
241
198
|
{
|
|
@@ -260,6 +217,6 @@ describe('Client services', () => {
|
|
|
260
217
|
}, 3_000);
|
|
261
218
|
}
|
|
262
219
|
|
|
263
|
-
await syncItems(
|
|
220
|
+
await syncItems(hostSpace.internal.db, guestSpace.internal.db);
|
|
264
221
|
});
|
|
265
222
|
});
|
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
|
-
import { Trigger } from '@dxos/async';
|
|
8
7
|
import { Client } from '@dxos/client';
|
|
9
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
8
|
import { describe, test, afterTest } from '@dxos/test';
|
|
11
9
|
|
|
12
|
-
import { TestBuilder } from '../testing';
|
|
10
|
+
import { performInvitation, TestBuilder } from '../testing';
|
|
13
11
|
|
|
14
12
|
describe('Halo', () => {
|
|
15
13
|
test('creates a identity', async () => {
|
|
@@ -42,31 +40,7 @@ describe('Halo', () => {
|
|
|
42
40
|
afterTest(() => client2.destroy());
|
|
43
41
|
await client2.initialize();
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
const done2 = new Trigger();
|
|
47
|
-
const invitation = client1.halo.createInvitation({ type: Invitation.Type.INTERACTIVE_TESTING });
|
|
48
|
-
invitation.subscribe({
|
|
49
|
-
onConnecting: (invitation) => {
|
|
50
|
-
const invitation2 = client2.halo.acceptInvitation(invitation, { type: Invitation.Type.INTERACTIVE_TESTING });
|
|
51
|
-
invitation2.subscribe({
|
|
52
|
-
onSuccess: () => {
|
|
53
|
-
done2.wake();
|
|
54
|
-
},
|
|
55
|
-
onError: (error) => {
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
onSuccess: async (invitation) => {
|
|
61
|
-
done1.wake();
|
|
62
|
-
},
|
|
63
|
-
onError: (error) => {
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
await done1.wait();
|
|
69
|
-
await done2.wait();
|
|
43
|
+
await Promise.all(performInvitation({ host: client1.halo, guest: client2.halo }));
|
|
70
44
|
|
|
71
45
|
expect(await client1.halo.devices.get()).to.have.lengthOf(2);
|
|
72
46
|
expect(await client2.halo.devices.get()).to.have.lengthOf(2);
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2021 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import chai, { expect } from 'chai';
|
|
6
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import waitForExpect from 'wait-for-expect';
|
|
9
|
+
|
|
10
|
+
import { asyncChain, asyncTimeout } from '@dxos/async';
|
|
11
|
+
import { Client, InvitationsProxy, Space, SpaceProxy } from '@dxos/client';
|
|
12
|
+
import { ConnectionState, Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
13
|
+
import { afterTest, describe, test } from '@dxos/test';
|
|
14
|
+
|
|
15
|
+
import { InvitationsServiceImpl } from '../invitations';
|
|
16
|
+
import { LocalClientServices, ServiceContext } from '../services';
|
|
17
|
+
import { DataSpace } from '../spaces';
|
|
18
|
+
import {
|
|
19
|
+
createIdentity,
|
|
20
|
+
createPeers,
|
|
21
|
+
performInvitation,
|
|
22
|
+
PerformInvitationParams,
|
|
23
|
+
Result,
|
|
24
|
+
TestBuilder
|
|
25
|
+
} from '../testing';
|
|
26
|
+
|
|
27
|
+
chai.use(chaiAsPromised);
|
|
28
|
+
|
|
29
|
+
const closeAfterTest = async (peer: ServiceContext) => {
|
|
30
|
+
afterTest(() => peer.close());
|
|
31
|
+
return peer;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const successfulInvitation = async ({
|
|
35
|
+
host,
|
|
36
|
+
guest,
|
|
37
|
+
hostResult: { invitation: hostInvitation, error: hostError },
|
|
38
|
+
guestResult: { invitation: guestInvitation, error: guestError }
|
|
39
|
+
}: {
|
|
40
|
+
host: ServiceContext;
|
|
41
|
+
guest: ServiceContext;
|
|
42
|
+
hostResult: Result;
|
|
43
|
+
guestResult: Result;
|
|
44
|
+
}) => {
|
|
45
|
+
expect(hostError).to.be.undefined;
|
|
46
|
+
expect(guestError).to.be.undefined;
|
|
47
|
+
expect(hostInvitation?.state).to.eq(Invitation.State.SUCCESS);
|
|
48
|
+
expect(guestInvitation?.state).to.eq(Invitation.State.SUCCESS);
|
|
49
|
+
|
|
50
|
+
switch (hostInvitation!.kind) {
|
|
51
|
+
case Invitation.Kind.SPACE:
|
|
52
|
+
expect(guestInvitation!.spaceKey).to.exist;
|
|
53
|
+
expect(hostInvitation!.spaceKey).to.deep.eq(guestInvitation!.spaceKey);
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case Invitation.Kind.DEVICE:
|
|
57
|
+
expect(hostInvitation!.identityKey).not.to.exist;
|
|
58
|
+
expect(guestInvitation!.identityKey).to.deep.eq(host.identityManager.identity!.identityKey);
|
|
59
|
+
expect(guestInvitation!.identityKey).to.deep.eq(guest.identityManager.identity!.identityKey);
|
|
60
|
+
|
|
61
|
+
// Check devices.
|
|
62
|
+
// TODO(burdon): Incorrect number of devices.
|
|
63
|
+
await waitForExpect(() => {
|
|
64
|
+
expect(host.identityManager.identity!.authorizedDeviceKeys.size).to.eq(2);
|
|
65
|
+
expect(guest.identityManager.identity!.authorizedDeviceKeys.size).to.eq(2);
|
|
66
|
+
});
|
|
67
|
+
// console.log(host.identityManager.identity!.authorizedDeviceKeys.size);
|
|
68
|
+
// console.log(guest.identityManager.identity!.authorizedDeviceKeys.size);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const testSuite = (getParams: () => PerformInvitationParams, getPeers: () => [ServiceContext, ServiceContext]) => {
|
|
74
|
+
test('no auth', async () => {
|
|
75
|
+
const [host, guest] = getPeers();
|
|
76
|
+
const [hostResult, guestResult] = await Promise.all(performInvitation(getParams()));
|
|
77
|
+
await successfulInvitation({ host, guest, hostResult, guestResult });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('with shared secret', async () => {
|
|
81
|
+
const [host, guest] = getPeers();
|
|
82
|
+
const params = getParams();
|
|
83
|
+
const [hostResult, guestResult] = await Promise.all(
|
|
84
|
+
performInvitation({
|
|
85
|
+
...params,
|
|
86
|
+
options: { ...params.options, authMethod: Invitation.AuthMethod.SHARED_SECRET }
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
await successfulInvitation({ host, guest, hostResult, guestResult });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('invalid auth code', async () => {
|
|
94
|
+
const [host, guest] = getPeers();
|
|
95
|
+
const params = getParams();
|
|
96
|
+
let attempt = 1;
|
|
97
|
+
const [hostResult, guestResult] = await Promise.all(
|
|
98
|
+
performInvitation({
|
|
99
|
+
...params,
|
|
100
|
+
options: { ...params.options, authMethod: Invitation.AuthMethod.SHARED_SECRET },
|
|
101
|
+
hooks: {
|
|
102
|
+
guest: {
|
|
103
|
+
onReady: (invitation) => {
|
|
104
|
+
if (attempt === 0) {
|
|
105
|
+
// Force retry.
|
|
106
|
+
void invitation.authenticate('000000');
|
|
107
|
+
attempt++;
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(attempt).to.eq(1);
|
|
119
|
+
await successfulInvitation({ host, guest, hostResult, guestResult });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('max auth code retries', async () => {
|
|
123
|
+
const params = getParams();
|
|
124
|
+
let attempt = 0;
|
|
125
|
+
const [hostPromise, guestPromise] = performInvitation({
|
|
126
|
+
...params,
|
|
127
|
+
options: { ...params.options, authMethod: Invitation.AuthMethod.SHARED_SECRET },
|
|
128
|
+
hooks: {
|
|
129
|
+
guest: {
|
|
130
|
+
onReady: (invitation) => {
|
|
131
|
+
// Force retry.
|
|
132
|
+
void invitation.authenticate('000000');
|
|
133
|
+
attempt++;
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const guestResult = await guestPromise;
|
|
140
|
+
|
|
141
|
+
expect(attempt).to.eq(3);
|
|
142
|
+
expect(guestResult.error).to.exist;
|
|
143
|
+
await expect(asyncTimeout(hostPromise, 100)).to.be.rejected;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('invitation timeout', async () => {
|
|
147
|
+
const params = getParams();
|
|
148
|
+
const [hostResult, guestResult] = await Promise.all(
|
|
149
|
+
performInvitation({
|
|
150
|
+
...params,
|
|
151
|
+
options: { ...params.options, authMethod: Invitation.AuthMethod.SHARED_SECRET, timeout: 1 }
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
expect(hostResult.invitation?.state).to.eq(Invitation.State.TIMEOUT);
|
|
156
|
+
expect(guestResult.invitation?.state).to.eq(Invitation.State.TIMEOUT);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('host cancels invitation', async () => {
|
|
160
|
+
const params = getParams();
|
|
161
|
+
const [hostResult, guestResult] = await Promise.all(
|
|
162
|
+
performInvitation({
|
|
163
|
+
...params,
|
|
164
|
+
options: { ...params.options, authMethod: Invitation.AuthMethod.SHARED_SECRET },
|
|
165
|
+
hooks: {
|
|
166
|
+
host: {
|
|
167
|
+
onConnected: (invitation) => {
|
|
168
|
+
void invitation.cancel();
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(hostResult.invitation?.state).to.eq(Invitation.State.CANCELLED);
|
|
177
|
+
expect(guestResult.error).to.exist;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('guest cancels invitation', async () => {
|
|
181
|
+
const params = getParams();
|
|
182
|
+
const [hostPromise, guestPromise] = performInvitation({
|
|
183
|
+
...params,
|
|
184
|
+
options: { ...params.options, authMethod: Invitation.AuthMethod.SHARED_SECRET },
|
|
185
|
+
hooks: {
|
|
186
|
+
guest: {
|
|
187
|
+
onConnected: (invitation) => {
|
|
188
|
+
void invitation.cancel();
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const guestResult = await guestPromise;
|
|
195
|
+
|
|
196
|
+
expect(guestResult.invitation?.state).to.eq(Invitation.State.CANCELLED);
|
|
197
|
+
await expect(asyncTimeout(hostPromise, 100)).to.be.rejected;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('network error', async () => {
|
|
201
|
+
const [, guest] = getPeers();
|
|
202
|
+
const params = getParams();
|
|
203
|
+
const [hostPromise, guestPromise] = performInvitation({
|
|
204
|
+
...params,
|
|
205
|
+
options: { ...params.options, authMethod: Invitation.AuthMethod.SHARED_SECRET },
|
|
206
|
+
hooks: {
|
|
207
|
+
guest: {
|
|
208
|
+
onConnected: () => {
|
|
209
|
+
void guest.networkManager.setConnectionState(ConnectionState.OFFLINE);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
const guestResult = await guestPromise;
|
|
216
|
+
|
|
217
|
+
expect(guestResult.error).to.exist;
|
|
218
|
+
await expect(asyncTimeout(hostPromise, 100)).to.be.rejected;
|
|
219
|
+
|
|
220
|
+
// Test cleanup fails if the guest is offline.
|
|
221
|
+
await guest.networkManager.setConnectionState(ConnectionState.ONLINE);
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
describe('Invitations', () => {
|
|
226
|
+
describe('ServiceContext', () => {
|
|
227
|
+
describe('space', () => {
|
|
228
|
+
let host: ServiceContext;
|
|
229
|
+
let guest: ServiceContext;
|
|
230
|
+
let space: DataSpace;
|
|
231
|
+
|
|
232
|
+
beforeEach(async () => {
|
|
233
|
+
const peers = await asyncChain<ServiceContext>([createIdentity, closeAfterTest])(createPeers(2));
|
|
234
|
+
host = peers[0];
|
|
235
|
+
guest = peers[1];
|
|
236
|
+
space = await host.dataSpaceManager!.createSpace();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
testSuite(
|
|
240
|
+
() => ({
|
|
241
|
+
host,
|
|
242
|
+
guest,
|
|
243
|
+
options: { kind: Invitation.Kind.SPACE, spaceKey: space.key }
|
|
244
|
+
}),
|
|
245
|
+
() => [host, guest]
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('device', () => {
|
|
250
|
+
let host: ServiceContext;
|
|
251
|
+
let guest: ServiceContext;
|
|
252
|
+
|
|
253
|
+
beforeEach(async () => {
|
|
254
|
+
const peers = await asyncChain<ServiceContext>([closeAfterTest])(createPeers(2));
|
|
255
|
+
host = peers[0];
|
|
256
|
+
guest = peers[1];
|
|
257
|
+
await host.createIdentity();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
testSuite(
|
|
261
|
+
() => ({ host, guest, options: { kind: Invitation.Kind.DEVICE } }),
|
|
262
|
+
() => [host, guest]
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('InvitationsProxy', () => {
|
|
268
|
+
describe('space', () => {
|
|
269
|
+
let hostContext: ServiceContext;
|
|
270
|
+
let guestContext: ServiceContext;
|
|
271
|
+
let host: InvitationsProxy;
|
|
272
|
+
let guest: InvitationsProxy;
|
|
273
|
+
let space: DataSpace;
|
|
274
|
+
|
|
275
|
+
beforeEach(async () => {
|
|
276
|
+
const peers = await asyncChain<ServiceContext>([createIdentity, closeAfterTest])(createPeers(2));
|
|
277
|
+
hostContext = peers[0];
|
|
278
|
+
guestContext = peers[1];
|
|
279
|
+
assert(hostContext.dataSpaceManager);
|
|
280
|
+
assert(guestContext.dataSpaceManager);
|
|
281
|
+
|
|
282
|
+
const hostService = new InvitationsServiceImpl(hostContext.invitations, (invitation) =>
|
|
283
|
+
hostContext.getInvitationHandler(invitation)
|
|
284
|
+
);
|
|
285
|
+
const guestService = new InvitationsServiceImpl(guestContext.invitations, (invitation) =>
|
|
286
|
+
guestContext.getInvitationHandler(invitation)
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
space = await hostContext.dataSpaceManager.createSpace();
|
|
290
|
+
host = new InvitationsProxy(hostService, () => ({ kind: Invitation.Kind.SPACE, spaceKey: space.key }));
|
|
291
|
+
guest = new InvitationsProxy(guestService, () => ({ kind: Invitation.Kind.SPACE }));
|
|
292
|
+
|
|
293
|
+
afterTest(() => space.close());
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
testSuite(
|
|
297
|
+
() => ({ host, guest }),
|
|
298
|
+
() => [hostContext, guestContext]
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('device', () => {
|
|
303
|
+
let hostContext: ServiceContext;
|
|
304
|
+
let guestContext: ServiceContext;
|
|
305
|
+
let host: InvitationsProxy;
|
|
306
|
+
let guest: InvitationsProxy;
|
|
307
|
+
|
|
308
|
+
beforeEach(async () => {
|
|
309
|
+
const peers = await asyncChain<ServiceContext>([closeAfterTest])(createPeers(2));
|
|
310
|
+
hostContext = peers[0];
|
|
311
|
+
guestContext = peers[1];
|
|
312
|
+
|
|
313
|
+
await hostContext.identityManager.createIdentity();
|
|
314
|
+
|
|
315
|
+
const hostService = new InvitationsServiceImpl(hostContext.invitations, (invitation) =>
|
|
316
|
+
hostContext.getInvitationHandler(invitation)
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
const guestService = new InvitationsServiceImpl(guestContext.invitations, (invitation) =>
|
|
320
|
+
guestContext.getInvitationHandler(invitation)
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
host = new InvitationsProxy(hostService, () => ({ kind: Invitation.Kind.DEVICE }));
|
|
324
|
+
guest = new InvitationsProxy(guestService, () => ({ kind: Invitation.Kind.DEVICE }));
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
testSuite(
|
|
328
|
+
() => ({ host, guest }),
|
|
329
|
+
() => [hostContext, guestContext]
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe('HaloProxy', () => {
|
|
335
|
+
let host: Client;
|
|
336
|
+
let guest: Client;
|
|
337
|
+
|
|
338
|
+
beforeEach(async () => {
|
|
339
|
+
const testBuilder = new TestBuilder();
|
|
340
|
+
|
|
341
|
+
host = new Client({ services: testBuilder.createLocal() });
|
|
342
|
+
guest = new Client({ services: testBuilder.createLocal() });
|
|
343
|
+
await host.initialize();
|
|
344
|
+
await guest.initialize();
|
|
345
|
+
|
|
346
|
+
await host.halo.createIdentity({ displayName: 'Peer' });
|
|
347
|
+
|
|
348
|
+
afterTest(() => Promise.all([host.destroy()]));
|
|
349
|
+
afterTest(() => Promise.all([guest.destroy()]));
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
testSuite(
|
|
353
|
+
() => ({ host: host.halo, guest: guest.halo }),
|
|
354
|
+
() => [
|
|
355
|
+
(host.services as LocalClientServices).host._serviceContext,
|
|
356
|
+
(guest.services as LocalClientServices).host._serviceContext
|
|
357
|
+
]
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe('EchoProxy', () => {
|
|
362
|
+
let host: Client;
|
|
363
|
+
let guest: Client;
|
|
364
|
+
let space: Space;
|
|
365
|
+
beforeEach(async () => {
|
|
366
|
+
const testBuilder = new TestBuilder();
|
|
367
|
+
|
|
368
|
+
host = new Client({ services: testBuilder.createLocal() });
|
|
369
|
+
guest = new Client({ services: testBuilder.createLocal() });
|
|
370
|
+
await host.initialize();
|
|
371
|
+
await guest.initialize();
|
|
372
|
+
await host.halo.createIdentity({ displayName: 'Peer 1' });
|
|
373
|
+
await guest.halo.createIdentity({ displayName: 'Peer 2' });
|
|
374
|
+
|
|
375
|
+
afterTest(() => Promise.all([host.destroy()]));
|
|
376
|
+
afterTest(() => Promise.all([guest.destroy()]));
|
|
377
|
+
|
|
378
|
+
space = await host.createSpace();
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
testSuite(
|
|
382
|
+
() => ({ host: space as SpaceProxy, guest }),
|
|
383
|
+
() => [
|
|
384
|
+
(host.services as LocalClientServices).host._serviceContext,
|
|
385
|
+
(guest.services as LocalClientServices).host._serviceContext
|
|
386
|
+
]
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import { Client, Invitation } from '@dxos/client';
|
|
9
|
-
import { raise } from '@dxos/debug';
|
|
7
|
+
import { Client, Invitation, SpaceProxy } from '@dxos/client';
|
|
10
8
|
import { log } from '@dxos/log';
|
|
11
9
|
import { afterTest, describe, test } from '@dxos/test';
|
|
12
10
|
|
|
13
|
-
import { TestBuilder, testSpace } from '../testing';
|
|
11
|
+
import { performInvitation, TestBuilder, testSpace } from '../testing';
|
|
14
12
|
|
|
15
13
|
describe('Spaces/invitations', () => {
|
|
16
14
|
test('creates a space and invites a peer', async () => {
|
|
@@ -28,36 +26,20 @@ describe('Spaces/invitations', () => {
|
|
|
28
26
|
afterTest(() => Promise.all([client1.destroy()]));
|
|
29
27
|
afterTest(() => Promise.all([client2.destroy()]));
|
|
30
28
|
|
|
31
|
-
const success1 = new Trigger<Invitation>();
|
|
32
|
-
const success2 = new Trigger<Invitation>();
|
|
33
|
-
|
|
34
29
|
const space1 = await client1.createSpace();
|
|
35
30
|
log('createSpace', { key: space1.key });
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
onError: (err: Error) => raise(err)
|
|
46
|
-
});
|
|
47
|
-
},
|
|
48
|
-
onSuccess: (invitation) => {
|
|
49
|
-
log('onSuccess');
|
|
50
|
-
success1.wake(invitation);
|
|
51
|
-
},
|
|
52
|
-
onError: (err) => raise(err)
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const [invitation1, invitation2] = await Promise.all([success1.wait(), success2.wait()]);
|
|
56
|
-
expect(invitation1.spaceKey).to.deep.eq(invitation2.spaceKey);
|
|
57
|
-
expect(invitation1.state).to.eq(Invitation.State.SUCCESS);
|
|
31
|
+
const [{ invitation: hostInvitation }, { invitation: guestInvitation }] = await Promise.all(
|
|
32
|
+
performInvitation({
|
|
33
|
+
host: space1 as SpaceProxy,
|
|
34
|
+
guest: client2
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
expect(guestInvitation?.spaceKey).to.deep.eq(space1.key);
|
|
38
|
+
expect(hostInvitation?.spaceKey).to.deep.eq(guestInvitation?.spaceKey);
|
|
39
|
+
expect(hostInvitation?.state).to.eq(Invitation.State.SUCCESS);
|
|
58
40
|
|
|
59
41
|
{
|
|
60
|
-
const space = await client2.getSpace(
|
|
42
|
+
const space = await client2.getSpace(guestInvitation!.spaceKey!)!.waitUntilReady();
|
|
61
43
|
await testSpace(space.internal.db);
|
|
62
44
|
}
|
|
63
45
|
});
|