UNPKG

14.9 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright (c) 2018, salesforce.com, inc.
4 * All rights reserved.
5 * SPDX-License-Identifier: BSD-3-Clause
6 * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7 */
8Object.defineProperty(exports, "__esModule", { value: true });
9const kit_1 = require("@salesforce/kit");
10const ts_sinon_1 = require("@salesforce/ts-sinon");
11const ts_types_1 = require("@salesforce/ts-types");
12const crypto_1 = require("crypto");
13const events_1 = require("events");
14const os_1 = require("os");
15const path_1 = require("path");
16const configFile_1 = require("./config/configFile");
17const connection_1 = require("./connection");
18const crypto_2 = require("./crypto");
19const logger_1 = require("./logger");
20const messages_1 = require("./messages");
21const sfdxError_1 = require("./sfdxError");
22const streamingClient_1 = require("./status/streamingClient");
23const _uniqid = () => {
24 return crypto_1.randomBytes(16).toString('hex');
25};
26function getTestLocalPath(uid) {
27 return Promise.resolve(path_1.join(os_1.tmpdir(), uid, 'sfdx_core', 'local'));
28}
29function getTestGlobalPath(uid) {
30 return Promise.resolve(path_1.join(os_1.tmpdir(), uid, 'sfdx_core', 'global'));
31}
32async function retrieveRootPath(isGlobal, uid = _uniqid()) {
33 return isGlobal ? await getTestGlobalPath(uid) : await getTestLocalPath(uid);
34}
35function defaultFakeConnectionRequest() {
36 return Promise.resolve(ts_types_1.ensureAnyJson({ records: [] }));
37}
38// tslint:disable-next-line:no-any
39const _testSetup = (sinon) => {
40 if (!sinon) {
41 try {
42 sinon = require('sinon');
43 }
44 catch (e) {
45 throw new Error('The package sinon was not found. Add it to your package.json and pass it in to testSetup(sinon.sandbox)');
46 }
47 }
48 // Import all the messages files in the sfdx-core messages dir.
49 // Messages.importMessagesDirectory(pathJoin(__dirname, '..', '..'));
50 messages_1.Messages.importMessagesDirectory(path_1.join(__dirname));
51 // Create a global sinon sandbox and a test logger instance for use within tests.
52 const defaultSandbox = sinon.createSandbox();
53 const testContext = {
54 SANDBOX: defaultSandbox,
55 SANDBOXES: {
56 DEFAULT: defaultSandbox,
57 CONFIG: sinon.createSandbox(),
58 CRYPTO: sinon.createSandbox(),
59 CONNECTION: sinon.createSandbox()
60 },
61 TEST_LOGGER: new logger_1.Logger({
62 name: 'SFDX_Core_Test_Logger'
63 }).useMemoryLogging(),
64 id: _uniqid(),
65 uniqid: _uniqid,
66 configStubs: {},
67 localPathRetriever: getTestLocalPath,
68 globalPathRetriever: getTestGlobalPath,
69 rootPathRetriever: retrieveRootPath,
70 fakeConnectionRequest: defaultFakeConnectionRequest,
71 getConfigStubContents(name, group) {
72 const stub = this.configStubs[name];
73 if (stub && stub.contents) {
74 if (group && stub.contents[group]) {
75 return ts_types_1.ensureJsonMap(stub.contents[group]);
76 }
77 else {
78 return stub.contents;
79 }
80 }
81 return {};
82 },
83 setConfigStubContents(name, value) {
84 if (ts_types_1.ensureString(name) && ts_types_1.isJsonMap(value)) {
85 this.configStubs[name] = value;
86 }
87 }
88 };
89 beforeEach(() => {
90 // Most core files create a child logger so stub this to return our test logger.
91 ts_sinon_1.stubMethod(testContext.SANDBOX, logger_1.Logger, 'child').returns(Promise.resolve(testContext.TEST_LOGGER));
92 testContext.SANDBOXES.CONFIG.stub(configFile_1.ConfigFile, 'resolveRootFolder').callsFake((isGlobal) => testContext.rootPathRetriever(isGlobal, testContext.id));
93 // Mock out all config file IO for all tests. They can restore individually if they need original functionality.
94 testContext.SANDBOXES.CONFIG.stub(configFile_1.ConfigFile.prototype, 'read').callsFake(async function () {
95 const stub = testContext.configStubs[this.constructor.name] || {};
96 if (stub.readFn) {
97 return await stub.readFn.call(this);
98 }
99 let contents = stub.contents || {};
100 if (stub.retrieveContents) {
101 contents = await stub.retrieveContents.call(this);
102 }
103 this.setContentsFromObject(contents);
104 return Promise.resolve(this.getContents());
105 });
106 testContext.SANDBOXES.CONFIG.stub(configFile_1.ConfigFile.prototype, 'write').callsFake(async function (newContents) {
107 if (!testContext.configStubs[this.constructor.name]) {
108 testContext.configStubs[this.constructor.name] = {};
109 }
110 const stub = testContext.configStubs[this.constructor.name];
111 if (!stub)
112 return;
113 if (stub.writeFn) {
114 return await stub.writeFn.call(this, newContents);
115 }
116 let contents = newContents || this.getContents();
117 if (stub.updateContents) {
118 contents = await stub.updateContents.call(this);
119 }
120 this.setContents(contents);
121 stub.contents = this.toObject();
122 });
123 testContext.SANDBOXES.CRYPTO.stub(crypto_2.Crypto.prototype, 'getKeyChain').callsFake(() => Promise.resolve({
124 setPassword: () => Promise.resolve(),
125 getPassword: (data, cb) => cb(undefined, '12345678901234567890123456789012')
126 }));
127 testContext.SANDBOXES.CONNECTION.stub(connection_1.Connection.prototype, 'request').callsFake(function (request, options) {
128 if (request === `${this.instanceUrl}/services/data`) {
129 return Promise.resolve([{ version: '42.0' }]);
130 }
131 return testContext.fakeConnectionRequest.call(this, request, options);
132 });
133 // Always start with the default and tests beforeEach or it methods can override it.
134 testContext.fakeConnectionRequest = defaultFakeConnectionRequest;
135 });
136 afterEach(() => {
137 testContext.SANDBOX.restore();
138 Object.values(testContext.SANDBOXES).forEach(theSandbox => theSandbox.restore());
139 testContext.configStubs = {};
140 });
141 return testContext;
142};
143/**
144 * Use to mock out different pieces of sfdx-core to make testing easier. This will mock out
145 * logging to a file, config file reading and writing, local and global path resolution, and
146 * *http request using connection (soon)*.
147 *
148 * ```
149 * // In a mocha tests
150 * import testSetup from '@salesforce/core/lib/testSetup';
151 *
152 * const $$ = testSetup();
153 *
154 * describe(() => {
155 * it('test', () => {
156 * // Stub out your own method
157 * $$.SANDBOX.stub(MyClass.prototype, 'myMethod').returnsFake(() => {});
158 *
159 * // Set the contents that is used when aliases are read. Same for all config files.
160 * $$.configStubs.Aliases = { contents: { 'myTestAlias': 'user@company.com' } };
161 *
162 * // Will use the contents set above.
163 * const username = Aliases.fetch('myTestAlias');
164 * expect(username).to.equal('user@company.com');
165 * });
166 * });
167 * ```
168 */
169exports.testSetup = kit_1.once(_testSetup);
170/**
171 * A pre-canned error for try/catch testing.
172 *
173 * **See** {@link shouldThrow}
174 */
175exports.unexpectedResult = new sfdxError_1.SfdxError('This code was expected to fail', 'UnexpectedResult');
176/**
177 * Use for this testing pattern:
178 * ```
179 * try {
180 * await call()
181 * assert.fail('this should never happen');
182 * } catch (e) {
183 * ...
184 * }
185 *
186 * Just do this
187 *
188 * try {
189 * await shouldThrow(call()); // If this succeeds unexpectedResultError is thrown.
190 * } catch(e) {
191 * ...
192 * }
193 * ```
194 * @param f The async function that is expected to throw.
195 */
196async function shouldThrow(f) {
197 await f;
198 throw exports.unexpectedResult;
199}
200exports.shouldThrow = shouldThrow;
201/**
202 * A helper to determine if a subscription will use callback or errorback.
203 * Enable errback to simulate a subscription failure.
204 */
205var StreamingMockSubscriptionCall;
206(function (StreamingMockSubscriptionCall) {
207 StreamingMockSubscriptionCall[StreamingMockSubscriptionCall["CALLBACK"] = 0] = "CALLBACK";
208 StreamingMockSubscriptionCall[StreamingMockSubscriptionCall["ERRORBACK"] = 1] = "ERRORBACK";
209})(StreamingMockSubscriptionCall = exports.StreamingMockSubscriptionCall || (exports.StreamingMockSubscriptionCall = {}));
210/**
211 * Simulates a comet subscription to a streaming channel.
212 */
213class StreamingMockCometSubscription extends events_1.EventEmitter {
214 constructor(options) {
215 super();
216 this.options = options;
217 }
218 /**
219 * Sets up a streaming subscription callback to occur after the setTimeout event loop phase.
220 * @param callback The function to invoke.
221 */
222 callback(callback) {
223 if (this.options.subscriptionCall === StreamingMockSubscriptionCall.CALLBACK) {
224 setTimeout(() => {
225 callback();
226 super.emit(StreamingMockCometSubscription.SUBSCRIPTION_COMPLETE);
227 }, 0);
228 }
229 }
230 /**
231 * Sets up a streaming subscription errback to occur after the setTimeout event loop phase.
232 * @param callback The function to invoke.
233 */
234 errback(callback) {
235 if (this.options.subscriptionCall === StreamingMockSubscriptionCall.ERRORBACK) {
236 const error = this.options.subscriptionErrbackError;
237 if (!error)
238 return;
239 setTimeout(() => {
240 callback(error);
241 super.emit(StreamingMockCometSubscription.SUBSCRIPTION_FAILED);
242 }, 0);
243 }
244 }
245}
246StreamingMockCometSubscription.SUBSCRIPTION_COMPLETE = 'subscriptionComplete';
247StreamingMockCometSubscription.SUBSCRIPTION_FAILED = 'subscriptionFailed';
248exports.StreamingMockCometSubscription = StreamingMockCometSubscription;
249/**
250 * Simulates a comet client. To the core streaming client this mocks the internal comet impl.
251 * The uses setTimeout(0ms) event loop phase just so the client can simulate actual streaming without the response
252 * latency.
253 */
254class StreamingMockCometClient extends streamingClient_1.CometClient {
255 /**
256 * Constructor
257 * @param {StreamingMockCometSubscriptionOptions} options Extends the StreamingClient options.
258 */
259 constructor(options) {
260 super();
261 this.options = options;
262 if (!this.options.messagePlaylist) {
263 this.options.messagePlaylist = [{ id: this.options.id }];
264 }
265 }
266 /**
267 * Fake addExtension. Does nothing.
268 */
269 addExtension(extension) { }
270 /**
271 * Fake disable. Does nothing.
272 */
273 disable(label) { }
274 /**
275 * Fake handshake that invoke callback after the setTimeout event phase.
276 * @param callback The function to invoke.
277 */
278 handshake(callback) {
279 setTimeout(() => {
280 callback();
281 }, 0);
282 }
283 /**
284 * Fake setHeader. Does nothing,
285 */
286 setHeader(name, value) { }
287 /**
288 * Fake subscription that completed after the setTimout event phase.
289 * @param channel The streaming channel.
290 * @param callback The function to invoke after the subscription completes.
291 */
292 subscribe(channel, callback) {
293 const subscription = new StreamingMockCometSubscription(this.options);
294 subscription.on('subscriptionComplete', () => {
295 if (!this.options.messagePlaylist)
296 return;
297 Object.values(this.options.messagePlaylist).forEach(message => {
298 setTimeout(() => {
299 callback(message);
300 }, 0);
301 });
302 });
303 return subscription;
304 }
305 /**
306 * Fake disconnect. Does Nothing.
307 */
308 disconnect() {
309 return Promise.resolve();
310 }
311}
312exports.StreamingMockCometClient = StreamingMockCometClient;
313/**
314 * Mock class for OrgData.
315 */
316class MockTestOrgData {
317 constructor(id = _uniqid()) {
318 this.testId = id;
319 this.userId = `user_id_${this.testId}`;
320 this.orgId = `${this.testId}`;
321 this.username = `admin_${this.testId}@gb.org`;
322 this.loginUrl = `http://login.${this.testId}.salesforce.com`;
323 this.instanceUrl = `http://instance.${this.testId}.salesforce.com`;
324 this.clientId = `${this.testId}/client_id`;
325 this.clientSecret = `${this.testId}/client_secret`;
326 this.authcode = `${this.testId}/authcode`;
327 this.accessToken = `${this.testId}/accessToken`;
328 this.refreshToken = `${this.testId}/refreshToken`;
329 }
330 createDevHubUsername(username) {
331 this.devHubUsername = username;
332 }
333 makeDevHub() {
334 kit_1.set(this, 'isDevHub', true);
335 }
336 createUser(user) {
337 const userMock = new MockTestOrgData();
338 userMock.username = user;
339 userMock.alias = this.alias;
340 userMock.devHubUsername = this.devHubUsername;
341 userMock.orgId = this.orgId;
342 userMock.loginUrl = this.loginUrl;
343 userMock.instanceUrl = this.instanceUrl;
344 userMock.clientId = this.clientId;
345 userMock.clientSecret = this.clientSecret;
346 return userMock;
347 }
348 getMockUserInfo() {
349 return {
350 Id: this.userId,
351 Username: this.username,
352 LastName: `user_lastname_${this.testId}`,
353 Alias: this.alias || 'user_alias_blah',
354 TimeZoneSidKey: `user_timezonesidkey_${this.testId}`,
355 LocaleSidKey: `user_localesidkey_${this.testId}`,
356 EmailEncodingKey: `user_emailencodingkey_${this.testId}`,
357 ProfileId: `user_profileid_${this.testId}`,
358 LanguageLocaleKey: `user_languagelocalekey_${this.testId}`,
359 Email: `user_email@${this.testId}.com`
360 };
361 }
362 async getConfig() {
363 const crypto = await crypto_2.Crypto.create();
364 const config = {};
365 config.orgId = this.orgId;
366 const accessToken = crypto.encrypt(this.accessToken);
367 if (accessToken) {
368 config.accessToken = accessToken;
369 }
370 const refreshToken = crypto.encrypt(this.refreshToken);
371 if (refreshToken) {
372 config.refreshToken = refreshToken;
373 }
374 config.instanceUrl = this.instanceUrl;
375 config.loginUrl = this.loginUrl;
376 config.username = this.username;
377 config.createdOrgInstance = 'CS1';
378 config.created = '1519163543003';
379 config.userId = this.userId;
380 // config.devHubUsername = 'tn@su-blitz.org';
381 if (this.devHubUsername) {
382 config.devHubUsername = this.devHubUsername;
383 }
384 const isDevHub = ts_types_1.getBoolean(this, 'isDevHub');
385 if (isDevHub) {
386 config.isDevHub = isDevHub;
387 }
388 return config;
389 }
390}
391exports.MockTestOrgData = MockTestOrgData;
392//# sourceMappingURL=testSetup.js.map
\No newline at end of file