UNPKG

7.38 kBJavaScriptView Raw
1/*
2Copyright 2016 OpenMarket Ltd
3Copyright 2017 Vector Creations Ltd
4Copyright 2018-2019 New Vector Ltd
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17*/
18
19"use strict";
20
21// load olm before the sdk if possible
22import './olm-loader';
23
24import sdk from '..';
25import testUtils from './test-utils';
26import MockHttpBackend from 'matrix-mock-request';
27import expect from 'expect';
28import Promise from 'bluebird';
29import LocalStorageCryptoStore from '../lib/crypto/store/localStorage-crypto-store';
30
31/**
32 * Wrapper for a MockStorageApi, MockHttpBackend and MatrixClient
33 *
34 * @constructor
35 * @param {string} userId
36 * @param {string} deviceId
37 * @param {string} accessToken
38 *
39 * @param {WebStorage=} sessionStoreBackend a web storage object to use for the
40 * session store. If undefined, we will create a MockStorageApi.
41 * @param {object} options additional options to pass to the client
42 */
43export default function TestClient(
44 userId, deviceId, accessToken, sessionStoreBackend, options,
45) {
46 this.userId = userId;
47 this.deviceId = deviceId;
48
49 if (sessionStoreBackend === undefined) {
50 sessionStoreBackend = new testUtils.MockStorageApi();
51 }
52 const sessionStore = new sdk.WebStorageSessionStore(sessionStoreBackend);
53
54 this.httpBackend = new MockHttpBackend();
55
56 options = Object.assign({
57 baseUrl: "http://" + userId + ".test.server",
58 userId: userId,
59 accessToken: accessToken,
60 deviceId: deviceId,
61 sessionStore: sessionStore,
62 request: this.httpBackend.requestFn,
63 }, options);
64 if (!options.cryptoStore) {
65 // expose this so the tests can get to it
66 this.cryptoStore = new LocalStorageCryptoStore(sessionStoreBackend);
67 options.cryptoStore = this.cryptoStore;
68 }
69 this.client = sdk.createClient(options);
70
71 this.deviceKeys = null;
72 this.oneTimeKeys = {};
73}
74
75TestClient.prototype.toString = function() {
76 return 'TestClient[' + this.userId + ']';
77};
78
79/**
80 * start the client, and wait for it to initialise.
81 *
82 * @return {Promise}
83 */
84TestClient.prototype.start = function() {
85 console.log(this + ': starting');
86 this.httpBackend.when("GET", "/pushrules").respond(200, {});
87 this.httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
88 this.expectDeviceKeyUpload();
89
90 // we let the client do a very basic initial sync, which it needs before
91 // it will upload one-time keys.
92 this.httpBackend.when("GET", "/sync").respond(200, { next_batch: 1 });
93
94 this.client.startClient({
95 // set this so that we can get hold of failed events
96 pendingEventOrdering: 'detached',
97 });
98
99 return Promise.all([
100 this.httpBackend.flushAllExpected(),
101 testUtils.syncPromise(this.client),
102 ]).then(() => {
103 console.log(this + ': started');
104 });
105};
106
107/**
108 * stop the client
109 * @return {Promise} Resolves once the mock http backend has finished all pending flushes
110 */
111TestClient.prototype.stop = function() {
112 this.client.stopClient();
113 return this.httpBackend.stop();
114};
115
116/**
117 * Set up expectations that the client will upload device keys.
118 */
119TestClient.prototype.expectDeviceKeyUpload = function() {
120 const self = this;
121 this.httpBackend.when("POST", "/keys/upload").respond(200, function(path, content) {
122 expect(content.one_time_keys).toBe(undefined);
123 expect(content.device_keys).toBeTruthy();
124
125 console.log(self + ': received device keys');
126 // we expect this to happen before any one-time keys are uploaded.
127 expect(Object.keys(self.oneTimeKeys).length).toEqual(0);
128
129 self.deviceKeys = content.device_keys;
130 return {one_time_key_counts: {signed_curve25519: 0}};
131 });
132};
133
134
135/**
136 * If one-time keys have already been uploaded, return them. Otherwise,
137 * set up an expectation that the keys will be uploaded, and wait for
138 * that to happen.
139 *
140 * @returns {Promise} for the one-time keys
141 */
142TestClient.prototype.awaitOneTimeKeyUpload = function() {
143 if (Object.keys(this.oneTimeKeys).length != 0) {
144 // already got one-time keys
145 return Promise.resolve(this.oneTimeKeys);
146 }
147
148 this.httpBackend.when("POST", "/keys/upload")
149 .respond(200, (path, content) => {
150 expect(content.device_keys).toBe(undefined);
151 expect(content.one_time_keys).toBe(undefined);
152 return {one_time_key_counts: {
153 signed_curve25519: Object.keys(this.oneTimeKeys).length,
154 }};
155 });
156
157 this.httpBackend.when("POST", "/keys/upload")
158 .respond(200, (path, content) => {
159 expect(content.device_keys).toBe(undefined);
160 expect(content.one_time_keys).toBeTruthy();
161 expect(content.one_time_keys).toNotEqual({});
162 console.log('%s: received %i one-time keys', this,
163 Object.keys(content.one_time_keys).length);
164 this.oneTimeKeys = content.one_time_keys;
165 return {one_time_key_counts: {
166 signed_curve25519: Object.keys(this.oneTimeKeys).length,
167 }};
168 });
169
170 // this can take ages
171 return this.httpBackend.flush('/keys/upload', 2, 1000).then((flushed) => {
172 expect(flushed).toEqual(2);
173 return this.oneTimeKeys;
174 });
175};
176
177/**
178 * Set up expectations that the client will query device keys.
179 *
180 * We check that the query contains each of the users in `response`.
181 *
182 * @param {Object} response response to the query.
183 */
184TestClient.prototype.expectKeyQuery = function(response) {
185 this.httpBackend.when('POST', '/keys/query').respond(
186 200, (path, content) => {
187 Object.keys(response.device_keys).forEach((userId) => {
188 expect(content.device_keys[userId]).toEqual(
189 {},
190 "Expected key query for " + userId + ", got " +
191 Object.keys(content.device_keys),
192 );
193 });
194 return response;
195 });
196};
197
198
199/**
200 * get the uploaded curve25519 device key
201 *
202 * @return {string} base64 device key
203 */
204TestClient.prototype.getDeviceKey = function() {
205 const keyId = 'curve25519:' + this.deviceId;
206 return this.deviceKeys.keys[keyId];
207};
208
209
210/**
211 * get the uploaded ed25519 device key
212 *
213 * @return {string} base64 device key
214 */
215TestClient.prototype.getSigningKey = function() {
216 const keyId = 'ed25519:' + this.deviceId;
217 return this.deviceKeys.keys[keyId];
218};
219
220/**
221 * flush a single /sync request, and wait for the syncing event
222 *
223 * @returns {Promise} promise which completes once the sync has been flushed
224 */
225TestClient.prototype.flushSync = function() {
226 console.log(`${this}: flushSync`);
227 return Promise.all([
228 this.httpBackend.flush('/sync', 1),
229 testUtils.syncPromise(this.client),
230 ]).then(() => {
231 console.log(`${this}: flushSync completed`);
232 });
233};