@dxos/client-services 0.1.30-next.7c60cd3 → 0.1.31-next.ea8f0bc
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-JLLUQQXD.mjs → chunk-ZIQUYEJS.mjs} +898 -1132
- package/dist/lib/browser/chunk-ZIQUYEJS.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -41
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +163 -50
- package/dist/lib/browser/packlets/testing/index.mjs.map +4 -4
- package/dist/lib/node/index.cjs +785 -1043
- 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 +2812 -2686
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts +1 -2
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts +1 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/index.d.ts +0 -6
- package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +2 -3
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts +2 -4
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts +2 -3
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/client-rpc-server.d.ts +22 -0
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -0
- package/dist/types/src/packlets/services/index.d.ts +2 -3
- package/dist/types/src/packlets/services/index.d.ts.map +1 -1
- package/dist/types/src/packlets/services/local-client-services.d.ts +3 -2
- package/dist/types/src/packlets/services/local-client-services.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +22 -8
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-registry.d.ts +3 -1
- package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/packlets/services/utils.d.ts +7 -0
- package/dist/types/src/packlets/services/utils.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/index.d.ts +2 -0
- package/dist/types/src/packlets/spaces/index.d.ts.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +2 -2
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/host-test-builder.d.ts +43 -0
- package/dist/types/src/packlets/testing/host-test-builder.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/index.d.ts +1 -0
- package/dist/types/src/packlets/testing/index.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +30 -34
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/tests/client-services.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/client-services.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/client.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/client.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/halo-credentials.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/halo-credentials.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/halo-persistent.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/halo-persistent.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/halo-profile.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/halo-profile.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/shared-worker.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/shared-worker.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/spaces-invitations.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/spaces-invitations.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/spaces.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/spaces.test.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +0 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/index.d.ts +0 -2
- package/dist/types/src/packlets/vault/index.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts +4 -4
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/package.json +30 -30
- package/src/packlets/invitations/device-invitations-handler.ts +10 -9
- package/src/packlets/invitations/device-invitations-service.test.ts +1 -1
- package/src/packlets/invitations/device-invitations-service.ts +1 -1
- package/src/packlets/invitations/index.ts +0 -6
- package/src/packlets/invitations/invitations-service.ts +7 -2
- package/src/packlets/invitations/space-invitations-handler.test.ts +3 -3
- package/src/packlets/invitations/space-invitations-handler.ts +11 -11
- package/src/packlets/invitations/space-invitations-service.test.ts +1 -1
- package/src/packlets/invitations/space-invitations-service.ts +2 -3
- package/src/packlets/services/client-rpc-server.ts +91 -0
- package/src/packlets/services/index.ts +2 -3
- package/src/packlets/services/local-client-services.ts +5 -1
- package/src/packlets/services/service-context.test.ts +4 -4
- package/src/packlets/services/service-context.ts +1 -1
- package/src/packlets/services/service-host.ts +52 -19
- package/src/packlets/services/service-registry.test.ts +2 -2
- package/src/packlets/services/service-registry.ts +9 -1
- package/src/packlets/services/utils.ts +53 -0
- package/src/packlets/spaces/data-space-manager.test.ts +4 -4
- package/src/packlets/spaces/index.ts +2 -0
- package/src/packlets/system/system-service.ts +3 -3
- package/src/packlets/testing/host-test-builder.ts +163 -0
- package/src/packlets/testing/index.ts +1 -0
- package/src/packlets/testing/invitation-utils.ts +1 -2
- package/src/packlets/testing/test-builder.ts +118 -140
- package/src/packlets/tests/client-services.test.ts +252 -0
- package/src/packlets/tests/client.test.ts +143 -0
- package/src/packlets/tests/halo-credentials.test.ts +94 -0
- package/src/packlets/tests/halo-persistent.test.ts +53 -0
- package/src/packlets/tests/halo-profile.test.ts +74 -0
- package/src/packlets/tests/shared-worker.test.ts +93 -0
- package/src/packlets/tests/spaces-invitations.test.ts +24 -0
- package/src/packlets/tests/spaces.test.ts +68 -0
- package/src/packlets/vault/iframe-host-runtime.ts +26 -34
- package/src/packlets/vault/iframe-proxy-runtime.ts +1 -1
- package/src/packlets/vault/index.ts +0 -2
- package/src/packlets/vault/shell-runtime.ts +1 -2
- package/src/packlets/vault/worker-runtime.ts +5 -15
- package/src/packlets/vault/worker-session.ts +39 -23
- package/dist/lib/browser/chunk-JLLUQQXD.mjs.map +0 -7
- package/dist/types/src/packlets/invitations/device-invitations-proxy.d.ts +0 -8
- package/dist/types/src/packlets/invitations/device-invitations-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/encoder.d.ts +0 -9
- package/dist/types/src/packlets/invitations/encoder.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/encoder.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/encoder.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +0 -56
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/invitations-proxy.d.ts +0 -18
- package/dist/types/src/packlets/invitations/invitations-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts +0 -55
- package/dist/types/src/packlets/invitations/invitations.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-proxy.d.ts +0 -11
- package/dist/types/src/packlets/invitations/space-invitations-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/services/service-definitions.d.ts +0 -30
- package/dist/types/src/packlets/services/service-definitions.d.ts.map +0 -1
- package/dist/types/src/packlets/services/service-proxy.d.ts +0 -17
- package/dist/types/src/packlets/services/service-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/vault/services.d.ts +0 -21
- package/dist/types/src/packlets/vault/services.d.ts.map +0 -1
- package/dist/types/src/packlets/vault/shell-controller.d.ts +0 -18
- package/dist/types/src/packlets/vault/shell-controller.d.ts.map +0 -1
- package/src/packlets/invitations/device-invitations-proxy.ts +0 -14
- package/src/packlets/invitations/encoder.test.ts +0 -23
- package/src/packlets/invitations/encoder.ts +0 -26
- package/src/packlets/invitations/invitations-handler.ts +0 -68
- package/src/packlets/invitations/invitations-proxy.ts +0 -159
- package/src/packlets/invitations/invitations.ts +0 -113
- package/src/packlets/invitations/space-invitations-proxy.ts +0 -16
- package/src/packlets/services/service-definitions.ts +0 -68
- package/src/packlets/services/service-proxy.ts +0 -54
- package/src/packlets/vault/services.ts +0 -42
- package/src/packlets/vault/shell-controller.ts +0 -56
|
@@ -1,162 +1,140 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import { createCredentialSignerWithChain, CredentialGenerator } from '@dxos/credentials';
|
|
7
|
-
import {
|
|
8
|
-
SnapshotStore,
|
|
9
|
-
DataPipelineControllerImpl,
|
|
10
|
-
MetadataStore,
|
|
11
|
-
SigningContext,
|
|
12
|
-
SpaceManager,
|
|
13
|
-
valueEncoding
|
|
14
|
-
} from '@dxos/echo-pipeline';
|
|
15
|
-
import { testLocalDatabase } from '@dxos/echo-pipeline/testing';
|
|
16
|
-
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
17
|
-
import { Keyring } from '@dxos/keyring';
|
|
18
|
-
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
19
|
-
import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
|
|
20
|
-
import { createStorage, Storage, StorageType } from '@dxos/random-access-storage';
|
|
21
|
-
|
|
22
|
-
import { createDefaultModelFactory, ClientServicesHost, ServiceContext } from '../services';
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
// TODO(burdon): Replace with test builder.
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
export const createServiceHost = (config: Config, signalManagerContext: MemorySignalManagerContext) => {
|
|
29
|
-
const networkManager = new NetworkManager({
|
|
30
|
-
signalManager: new MemorySignalManager(signalManagerContext),
|
|
31
|
-
transportFactory: MemoryTransportFactory
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return new ClientServicesHost({
|
|
35
|
-
config,
|
|
36
|
-
networkManager
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const createServiceContext = ({
|
|
41
|
-
signalContext = new MemorySignalManagerContext(),
|
|
42
|
-
storage = createStorage({ type: StorageType.RAM })
|
|
43
|
-
}: {
|
|
44
|
-
signalContext?: MemorySignalManagerContext;
|
|
45
|
-
storage?: Storage;
|
|
46
|
-
} = {}) => {
|
|
47
|
-
const networkManager = new NetworkManager({
|
|
48
|
-
signalManager: new MemorySignalManager(signalContext),
|
|
49
|
-
transportFactory: MemoryTransportFactory
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const modelFactory = createDefaultModelFactory();
|
|
53
|
-
return new ServiceContext(storage, networkManager, modelFactory);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const createPeers = async (numPeers: number) => {
|
|
57
|
-
const signalContext = new MemorySignalManagerContext();
|
|
58
|
-
|
|
59
|
-
return await Promise.all(
|
|
60
|
-
Array.from(Array(numPeers)).map(async () => {
|
|
61
|
-
const peer = createServiceContext({ signalContext });
|
|
62
|
-
await peer.open();
|
|
63
|
-
return peer;
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const createIdentity = async (peer: ServiceContext) => {
|
|
69
|
-
await peer.createIdentity();
|
|
70
|
-
return peer;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// TODO(burdon): Remove @dxos/client-testing.
|
|
74
|
-
// TODO(burdon): Create builder and make configurable.
|
|
75
|
-
export const syncItems = async (db1: DataPipelineControllerImpl, db2: DataPipelineControllerImpl) => {
|
|
76
|
-
await testLocalDatabase(db1, db2);
|
|
77
|
-
await testLocalDatabase(db2, db1);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export class TestBuilder {
|
|
81
|
-
public readonly signalContext = new MemorySignalManagerContext();
|
|
5
|
+
import assert from 'node:assert';
|
|
82
6
|
|
|
83
|
-
|
|
84
|
-
|
|
7
|
+
import { asyncTimeout } from '@dxos/async';
|
|
8
|
+
import { Client, ClientServices, ClientServicesProxy, createDefaultModelFactory } from '@dxos/client';
|
|
9
|
+
import { Config } from '@dxos/config';
|
|
10
|
+
import { DocumentModel } from '@dxos/document-model';
|
|
11
|
+
import { DatabaseBackendProxy, genesisMutation } from '@dxos/echo-db';
|
|
12
|
+
import { PublicKey } from '@dxos/keys';
|
|
13
|
+
import { MemorySignalManager, MemorySignalManagerContext, WebsocketSignalManager } from '@dxos/messaging';
|
|
14
|
+
import { createWebRTCTransportFactory, MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
|
|
15
|
+
import { Storage } from '@dxos/random-access-storage';
|
|
16
|
+
import { createLinkedPorts, createProtoRpcPeer, ProtoRpcPeer } from '@dxos/rpc';
|
|
17
|
+
|
|
18
|
+
import { ClientServicesHost, LocalClientServices } from '../services';
|
|
19
|
+
|
|
20
|
+
export const testConfigWithLocalSignal = new Config({
|
|
21
|
+
version: 1,
|
|
22
|
+
runtime: {
|
|
23
|
+
services: {
|
|
24
|
+
signal: {
|
|
25
|
+
// TODO(burdon): Port numbers and global consts?
|
|
26
|
+
server: 'ws://localhost:4000/.well-known/dx/signal'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
85
29
|
}
|
|
86
|
-
}
|
|
30
|
+
});
|
|
87
31
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
networkManager?: NetworkManager;
|
|
94
|
-
spaceManager?: SpaceManager;
|
|
95
|
-
snapshotStore?: SnapshotStore;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export class TestPeer {
|
|
99
|
-
private _props: TestPeerProps = {};
|
|
32
|
+
/**
|
|
33
|
+
* Client builder supports different configurations, incl. signaling, transports, storage.
|
|
34
|
+
*/
|
|
35
|
+
export class TestBuilder {
|
|
36
|
+
private readonly _config: Config;
|
|
100
37
|
|
|
101
|
-
|
|
38
|
+
public storage?: Storage;
|
|
102
39
|
|
|
103
|
-
|
|
104
|
-
|
|
40
|
+
// prettier-ignore
|
|
41
|
+
constructor (
|
|
42
|
+
config?: Config,
|
|
43
|
+
private readonly _modelFactory = createDefaultModelFactory(),
|
|
44
|
+
private readonly _signalManagerContext = new MemorySignalManagerContext()
|
|
45
|
+
) {
|
|
46
|
+
this._config = config ?? new Config();
|
|
105
47
|
}
|
|
106
48
|
|
|
107
|
-
get
|
|
108
|
-
return
|
|
49
|
+
get config(): Config {
|
|
50
|
+
return this._config;
|
|
109
51
|
}
|
|
110
52
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Get network manager using local shared memory or remote signal manager.
|
|
55
|
+
*/
|
|
56
|
+
get networkManager() {
|
|
57
|
+
const signalServer = this._config.get('runtime.services.signal.server');
|
|
58
|
+
if (signalServer) {
|
|
59
|
+
return new NetworkManager({
|
|
60
|
+
log: true,
|
|
61
|
+
signalManager: new WebsocketSignalManager([signalServer]),
|
|
62
|
+
transportFactory: createWebRTCTransportFactory({
|
|
63
|
+
iceServers: this._config.get('runtime.services.ice')
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Memory transport with shared context.
|
|
69
|
+
return new NetworkManager({
|
|
70
|
+
log: true,
|
|
71
|
+
signalManager: new MemorySignalManager(this._signalManagerContext),
|
|
72
|
+
transportFactory: MemoryTransportFactory
|
|
73
|
+
});
|
|
121
74
|
}
|
|
122
75
|
|
|
123
|
-
|
|
124
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Create backend service handlers.
|
|
78
|
+
*/
|
|
79
|
+
createClientServicesHost() {
|
|
80
|
+
return new ClientServicesHost({
|
|
81
|
+
config: this._config,
|
|
82
|
+
modelFactory: this._modelFactory,
|
|
83
|
+
networkManager: this.networkManager,
|
|
84
|
+
storage: this.storage
|
|
85
|
+
});
|
|
125
86
|
}
|
|
126
87
|
|
|
127
|
-
|
|
128
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Create local services host.
|
|
90
|
+
*/
|
|
91
|
+
createLocal() {
|
|
92
|
+
return new LocalClientServices({
|
|
93
|
+
config: this._config,
|
|
94
|
+
modelFactory: this._modelFactory,
|
|
95
|
+
networkManager: this.networkManager,
|
|
96
|
+
storage: this.storage
|
|
97
|
+
});
|
|
129
98
|
}
|
|
130
99
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Create client/server.
|
|
102
|
+
*/
|
|
103
|
+
createClientServer(host: ClientServicesHost = this.createClientServicesHost()): [Client, ProtoRpcPeer<{}>] {
|
|
104
|
+
const [proxyPort, hostPort] = createLinkedPorts();
|
|
105
|
+
const server = createProtoRpcPeer({
|
|
106
|
+
exposed: host.descriptors,
|
|
107
|
+
handlers: host.services as ClientServices,
|
|
108
|
+
port: hostPort
|
|
109
|
+
});
|
|
110
|
+
// TODO(dmaretskyi): Refactor.
|
|
111
|
+
|
|
112
|
+
const client = new Client({ services: new ClientServicesProxy(proxyPort) });
|
|
113
|
+
return [client, server];
|
|
143
114
|
}
|
|
144
115
|
}
|
|
145
116
|
|
|
146
|
-
export const
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
117
|
+
export const testSpace = async (create: DatabaseBackendProxy, check: DatabaseBackendProxy = create) => {
|
|
118
|
+
const objectId = PublicKey.random().toHex();
|
|
119
|
+
|
|
120
|
+
const result = create.mutate(genesisMutation(objectId, DocumentModel.meta.type));
|
|
121
|
+
|
|
122
|
+
await result.getReceipt();
|
|
123
|
+
// TODO(dmaretskiy): await result.waitToBeProcessed()
|
|
124
|
+
assert(create._itemManager.entities.has(result.objectsCreated[0].id));
|
|
125
|
+
|
|
126
|
+
await asyncTimeout(
|
|
127
|
+
check._itemManager.update.waitForCondition(() => check._itemManager.entities.has(objectId)),
|
|
128
|
+
1000
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return result;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const syncItems = async (db1: DatabaseBackendProxy, db2: DatabaseBackendProxy) => {
|
|
135
|
+
// Check item replicated from 1 => 2.
|
|
136
|
+
await testSpace(db1, db2);
|
|
137
|
+
|
|
138
|
+
// Check item replicated from 2 => 1.
|
|
139
|
+
await testSpace(db2, db1);
|
|
162
140
|
};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import assert from 'node:assert';
|
|
7
|
+
import waitForExpect from 'wait-for-expect';
|
|
8
|
+
|
|
9
|
+
import { Trigger } from '@dxos/async';
|
|
10
|
+
import { Space } from '@dxos/client';
|
|
11
|
+
import { raise } from '@dxos/debug';
|
|
12
|
+
import { log } from '@dxos/log';
|
|
13
|
+
import { Invitation, SpaceMember } from '@dxos/protocols/proto/dxos/client/services';
|
|
14
|
+
import { describe, test, afterTest } from '@dxos/test';
|
|
15
|
+
|
|
16
|
+
import { syncItems, TestBuilder } from '../testing';
|
|
17
|
+
|
|
18
|
+
// TODO(burdon): Use as set-up for test suite.
|
|
19
|
+
// TODO(burdon): Timeouts and progress callback/events.
|
|
20
|
+
|
|
21
|
+
describe('Client services', () => {
|
|
22
|
+
test('creates client with remote server', async () => {
|
|
23
|
+
const testBuilder = new TestBuilder();
|
|
24
|
+
|
|
25
|
+
const peer = testBuilder.createClientServicesHost();
|
|
26
|
+
await peer.open();
|
|
27
|
+
afterTest(() => peer.close());
|
|
28
|
+
|
|
29
|
+
const [client, server] = testBuilder.createClientServer(peer);
|
|
30
|
+
void server.open();
|
|
31
|
+
afterTest(() => server.close());
|
|
32
|
+
|
|
33
|
+
await client.initialize();
|
|
34
|
+
afterTest(() => client.destroy());
|
|
35
|
+
expect(client.initialized).to.be.true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('creates clients with multiple peers connected via memory transport', async () => {
|
|
39
|
+
const testBuilder = new TestBuilder();
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
const peer1 = testBuilder.createClientServicesHost();
|
|
43
|
+
await peer1.open();
|
|
44
|
+
afterTest(() => peer1.close());
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
const [client1a, server1a] = testBuilder.createClientServer(peer1);
|
|
48
|
+
void server1a.open();
|
|
49
|
+
await client1a.initialize();
|
|
50
|
+
afterTest(() => Promise.all([client1a.destroy(), server1a.close()]));
|
|
51
|
+
expect(client1a.initialized).to.be.true;
|
|
52
|
+
|
|
53
|
+
await client1a.halo.createIdentity();
|
|
54
|
+
}
|
|
55
|
+
{
|
|
56
|
+
const [client1b, server1b] = testBuilder.createClientServer(peer1);
|
|
57
|
+
void server1b.open();
|
|
58
|
+
await client1b.initialize();
|
|
59
|
+
afterTest(() => Promise.all([client1b.destroy(), server1b.close()]));
|
|
60
|
+
expect(client1b.initialized).to.be.true;
|
|
61
|
+
|
|
62
|
+
// TODO(burdon): Test profile is available.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
const peer2 = testBuilder.createClientServicesHost();
|
|
68
|
+
await peer2.open();
|
|
69
|
+
afterTest(() => peer2.close());
|
|
70
|
+
|
|
71
|
+
{
|
|
72
|
+
const [client2a, server2a] = testBuilder.createClientServer(peer2);
|
|
73
|
+
void server2a.open();
|
|
74
|
+
await client2a.initialize();
|
|
75
|
+
afterTest(() => Promise.all([client2a.destroy(), server2a.close()]));
|
|
76
|
+
expect(client2a.initialized).to.be.true;
|
|
77
|
+
|
|
78
|
+
await client2a.halo.createIdentity();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('creates identity and invites peer', async () => {
|
|
84
|
+
const testBuilder = new TestBuilder();
|
|
85
|
+
|
|
86
|
+
const peer1 = testBuilder.createClientServicesHost();
|
|
87
|
+
const peer2 = testBuilder.createClientServicesHost();
|
|
88
|
+
|
|
89
|
+
await peer1.open();
|
|
90
|
+
await peer2.open();
|
|
91
|
+
|
|
92
|
+
const [client1, server1] = testBuilder.createClientServer(peer1);
|
|
93
|
+
const [client2, server2] = testBuilder.createClientServer(peer2);
|
|
94
|
+
|
|
95
|
+
// Don't wait (otherwise will block).
|
|
96
|
+
{
|
|
97
|
+
void server1.open();
|
|
98
|
+
void server2.open();
|
|
99
|
+
|
|
100
|
+
await client1.initialize();
|
|
101
|
+
await client2.initialize();
|
|
102
|
+
|
|
103
|
+
await client1.halo.createIdentity();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const success1 = new Trigger<Invitation>();
|
|
107
|
+
const success2 = new Trigger<Invitation>();
|
|
108
|
+
|
|
109
|
+
const authenticationCode = new Trigger<string>();
|
|
110
|
+
|
|
111
|
+
{
|
|
112
|
+
const observable1 = client1.halo.createInvitation();
|
|
113
|
+
observable1.subscribe({
|
|
114
|
+
onConnecting: (invitation) => {
|
|
115
|
+
const observable2 = client2.halo.acceptInvitation(invitation);
|
|
116
|
+
observable2.subscribe({
|
|
117
|
+
onAuthenticating: async () => {
|
|
118
|
+
await observable2.authenticate(await authenticationCode.wait());
|
|
119
|
+
},
|
|
120
|
+
onSuccess: (invitation: Invitation) => {
|
|
121
|
+
// TODO(burdon): No device.
|
|
122
|
+
// expect(guest.identityManager.identity!.authorizedDeviceKeys.size).to.eq(1);
|
|
123
|
+
success2.wake(invitation);
|
|
124
|
+
},
|
|
125
|
+
onError: (err: Error) => raise(new Error(err.message))
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
onConnected: (invitation: Invitation) => {
|
|
129
|
+
assert(invitation.authenticationCode);
|
|
130
|
+
authenticationCode.wake(invitation.authenticationCode);
|
|
131
|
+
},
|
|
132
|
+
onSuccess: (invitation: Invitation) => {
|
|
133
|
+
success1.wake(invitation);
|
|
134
|
+
},
|
|
135
|
+
onCancelled: () => raise(new Error()),
|
|
136
|
+
onTimeout: (err: Error) => raise(new Error(err.message)),
|
|
137
|
+
onError: (err: Error) => raise(new Error(err.message))
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check same identity.
|
|
142
|
+
const [invitation1, invitation2] = await Promise.all([success1.wait(), success2.wait()]);
|
|
143
|
+
expect(invitation1.identityKey).not.to.exist;
|
|
144
|
+
expect(invitation2.identityKey).to.deep.eq(client1.halo.identity!.identityKey);
|
|
145
|
+
expect(invitation2.identityKey).to.deep.eq(client2.halo.identity!.identityKey);
|
|
146
|
+
expect(invitation1.state).to.eq(Invitation.State.SUCCESS);
|
|
147
|
+
expect(invitation2.state).to.eq(Invitation.State.SUCCESS);
|
|
148
|
+
|
|
149
|
+
// Check devices.
|
|
150
|
+
// TODO(burdon): Incorrect number of devices.
|
|
151
|
+
await waitForExpect(async () => {
|
|
152
|
+
expect(client1.halo.getDevices()).to.have.lengthOf(2);
|
|
153
|
+
expect(client2.halo.getDevices()).to.have.lengthOf(2);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('synchronizes data between two spaces after completing invitation', async () => {
|
|
158
|
+
const testBuilder = new TestBuilder();
|
|
159
|
+
|
|
160
|
+
const peer1 = testBuilder.createClientServicesHost();
|
|
161
|
+
const peer2 = testBuilder.createClientServicesHost();
|
|
162
|
+
|
|
163
|
+
await peer1.open();
|
|
164
|
+
await peer2.open();
|
|
165
|
+
|
|
166
|
+
const [client1, server1] = testBuilder.createClientServer(peer1);
|
|
167
|
+
const [client2, server2] = testBuilder.createClientServer(peer2);
|
|
168
|
+
|
|
169
|
+
// Don't wait (otherwise will block).
|
|
170
|
+
{
|
|
171
|
+
void server1.open();
|
|
172
|
+
void server2.open();
|
|
173
|
+
|
|
174
|
+
await client1.initialize();
|
|
175
|
+
await client2.initialize();
|
|
176
|
+
await client1.halo.createIdentity({ displayName: 'Peer 1' });
|
|
177
|
+
await client2.halo.createIdentity({ displayName: 'Peer 2' });
|
|
178
|
+
}
|
|
179
|
+
log('initialized');
|
|
180
|
+
|
|
181
|
+
afterTest(() => Promise.all([client1.destroy(), server1.close(), peer1.close()]));
|
|
182
|
+
afterTest(() => Promise.all([client2.destroy(), server2.close(), peer2.close()]));
|
|
183
|
+
|
|
184
|
+
const success1 = new Trigger<Invitation>();
|
|
185
|
+
const success2 = new Trigger<Invitation>();
|
|
186
|
+
|
|
187
|
+
const space1 = await client1.echo.createSpace();
|
|
188
|
+
log('createSpace', { key: space1.key });
|
|
189
|
+
const observable1 = space1.createInvitation({ type: Invitation.Type.INTERACTIVE_TESTING });
|
|
190
|
+
|
|
191
|
+
observable1.subscribe({
|
|
192
|
+
onConnecting: (invitation) => {
|
|
193
|
+
const observable2 = client2.echo.acceptInvitation(invitation);
|
|
194
|
+
observable2.subscribe({
|
|
195
|
+
onSuccess: (invitation: Invitation) => {
|
|
196
|
+
success2.wake(invitation);
|
|
197
|
+
},
|
|
198
|
+
onError: (err: Error) => raise(err)
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
onSuccess: (invitation) => {
|
|
202
|
+
log('onSuccess');
|
|
203
|
+
success1.wake(invitation);
|
|
204
|
+
},
|
|
205
|
+
onError: (err) => raise(err)
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const [invitation1, invitation2] = await Promise.all([success1.wait(), success2.wait()]);
|
|
209
|
+
expect(invitation1.spaceKey).to.deep.eq(invitation2.spaceKey);
|
|
210
|
+
expect(invitation1.state).to.eq(Invitation.State.SUCCESS);
|
|
211
|
+
|
|
212
|
+
log('Invitation complete');
|
|
213
|
+
|
|
214
|
+
// TODO(burdon): Space should now be available?
|
|
215
|
+
const trigger = new Trigger<Space>();
|
|
216
|
+
await waitForExpect(() => {
|
|
217
|
+
const space2 = client2.echo.getSpace(invitation2.spaceKey!);
|
|
218
|
+
assert(space2);
|
|
219
|
+
expect(space2).to.exist;
|
|
220
|
+
trigger.wake(space2);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const space2 = await trigger.wait();
|
|
224
|
+
|
|
225
|
+
for (const space of [space1, space2]) {
|
|
226
|
+
await waitForExpect(() => {
|
|
227
|
+
expect(space.getMembers()).to.deep.equal([
|
|
228
|
+
{
|
|
229
|
+
identity: {
|
|
230
|
+
identityKey: client1.halo.identity!.identityKey,
|
|
231
|
+
profile: {
|
|
232
|
+
displayName: 'Peer 1'
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
presence: SpaceMember.PresenceState.ONLINE
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
identity: {
|
|
239
|
+
identityKey: client2.halo.identity!.identityKey,
|
|
240
|
+
profile: {
|
|
241
|
+
displayName: 'Peer 2'
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
presence: SpaceMember.PresenceState.ONLINE
|
|
245
|
+
}
|
|
246
|
+
]);
|
|
247
|
+
}, 3_000);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
await syncItems(space1.internal.db, space2.internal.db);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import chai, { expect } from 'chai';
|
|
6
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
7
|
+
|
|
8
|
+
import { Client } from '@dxos/client';
|
|
9
|
+
import { Config } from '@dxos/config';
|
|
10
|
+
import { Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
11
|
+
import { createStorage, StorageType } from '@dxos/random-access-storage';
|
|
12
|
+
import { describe, test, afterTest } from '@dxos/test';
|
|
13
|
+
|
|
14
|
+
import { TestBuilder } from '../testing';
|
|
15
|
+
|
|
16
|
+
chai.use(chaiAsPromised);
|
|
17
|
+
|
|
18
|
+
describe('Client', () => {
|
|
19
|
+
test('creates client with embedded services', async () => {
|
|
20
|
+
const testBuilder = new TestBuilder();
|
|
21
|
+
|
|
22
|
+
const client = new Client({ services: testBuilder.createLocal() });
|
|
23
|
+
await client.initialize();
|
|
24
|
+
afterTest(() => client.destroy());
|
|
25
|
+
expect(client.initialized).to.be.true;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('initialize and destroy multiple times', async () => {
|
|
29
|
+
const testBuilder = new TestBuilder();
|
|
30
|
+
|
|
31
|
+
const client = new Client({ services: testBuilder.createLocal() });
|
|
32
|
+
await client.initialize();
|
|
33
|
+
await client.initialize();
|
|
34
|
+
expect(client.initialized).to.be.true;
|
|
35
|
+
|
|
36
|
+
await client.destroy();
|
|
37
|
+
await client.destroy();
|
|
38
|
+
expect(client.initialized).to.be.false;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('closes and reopens', async () => {
|
|
42
|
+
const testBuilder = new TestBuilder();
|
|
43
|
+
|
|
44
|
+
const client = new Client({ services: testBuilder.createLocal() });
|
|
45
|
+
await client.initialize();
|
|
46
|
+
expect(client.initialized).to.be.true;
|
|
47
|
+
|
|
48
|
+
await client.destroy();
|
|
49
|
+
expect(client.initialized).to.be.false;
|
|
50
|
+
|
|
51
|
+
await client.initialize();
|
|
52
|
+
expect(client.initialized).to.be.true;
|
|
53
|
+
|
|
54
|
+
await client.destroy();
|
|
55
|
+
expect(client.initialized).to.be.false;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('create space before identity', async () => {
|
|
59
|
+
const testBuilder = new TestBuilder();
|
|
60
|
+
|
|
61
|
+
const client = new Client({ services: testBuilder.createLocal() });
|
|
62
|
+
await client.initialize();
|
|
63
|
+
afterTest(() => client.destroy());
|
|
64
|
+
await expect(client.echo.createSpace()).to.eventually.be.rejectedWith(
|
|
65
|
+
'This device has no HALO identity available.'
|
|
66
|
+
);
|
|
67
|
+
}).timeout(1_000);
|
|
68
|
+
|
|
69
|
+
// TODO(burdon): Memory store is reset on close (feed store is closed).
|
|
70
|
+
test.skip('creates identity then resets the memory storage', async () => {
|
|
71
|
+
const config = new Config();
|
|
72
|
+
const testBuilder = new TestBuilder(config);
|
|
73
|
+
await runTest(testBuilder);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('creates identity then resets the node storage', async () => {
|
|
77
|
+
const config = await getNodeConfig(true);
|
|
78
|
+
const testBuilder = new TestBuilder(config);
|
|
79
|
+
await runTest(testBuilder);
|
|
80
|
+
}).onlyEnvironments('nodejs');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// TODO(burdon): Factor out.
|
|
84
|
+
const getNodeConfig = async (reset = false) => {
|
|
85
|
+
const path = '/tmp/dxos/client/test/packlets/test';
|
|
86
|
+
if (reset) {
|
|
87
|
+
// TODO(burdon): Reconcile StorageType and ConfigProto types.
|
|
88
|
+
const storage = createStorage({ type: StorageType.NODE, root: path });
|
|
89
|
+
await storage.reset();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// TODO(burdon): Update API.
|
|
93
|
+
// TODO(burdon): Config not required if remote.
|
|
94
|
+
return new Config({
|
|
95
|
+
version: 1,
|
|
96
|
+
runtime: {
|
|
97
|
+
client: {
|
|
98
|
+
storage: {
|
|
99
|
+
persistent: true,
|
|
100
|
+
storageType: Runtime.Client.Storage.StorageDriver.NODE,
|
|
101
|
+
path // TODO(burdon): Change to root.
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// TODO(burdon): Develop test-suite.
|
|
109
|
+
const runTest = async (testBuilder: TestBuilder) => {
|
|
110
|
+
const client = new Client({ services: testBuilder.createLocal() });
|
|
111
|
+
const displayName = 'test-user';
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
// Create identity.
|
|
115
|
+
await client.initialize();
|
|
116
|
+
expect(client.halo.identity).not.to.exist;
|
|
117
|
+
const identity = await client.halo.createIdentity({ displayName });
|
|
118
|
+
expect(client.halo.identity).to.deep.eq(identity);
|
|
119
|
+
await client.destroy();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
{
|
|
123
|
+
// Should throw trying to create another.
|
|
124
|
+
await client.initialize();
|
|
125
|
+
expect(client.halo.identity).to.exist;
|
|
126
|
+
// TODO(burdon): Error type.
|
|
127
|
+
await expect(client.halo.createIdentity({ displayName })).to.be.rejected;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
// Reset storage.
|
|
132
|
+
// TODO(burdon): Reset doesn't work in memory (create store test).
|
|
133
|
+
await client.reset();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
{
|
|
137
|
+
// Start again.
|
|
138
|
+
await client.initialize();
|
|
139
|
+
await client.halo.createIdentity({ displayName });
|
|
140
|
+
expect(client.halo.identity).to.exist;
|
|
141
|
+
await client.destroy();
|
|
142
|
+
}
|
|
143
|
+
};
|