From 2fc807b803ff3aff5e24d88eef919c2924404145 Mon Sep 17 00:00:00 2001 From: Gabe Pearhill Date: Wed, 20 May 2026 15:41:54 -0700 Subject: [PATCH] migrate core/common tests to jest --- core/common/.nycrc | 24 - core/common/{.mocharc.js => jest.config.js} | 34 +- core/common/package.json | 15 +- core/common/src/util.ts | 5 + core/common/system-test/common.ts | 220 +++- core/common/system-test/install.ts | 29 +- core/common/test/index.ts | 10 +- core/common/test/operation.ts | 211 ++-- core/common/test/service-object.ts | 1043 +++++++++------- core/common/test/service.ts | 555 +++++---- core/common/test/util.ts | 1203 ++++++++++++------- 11 files changed, 1999 insertions(+), 1350 deletions(-) delete mode 100644 core/common/.nycrc rename core/common/{.mocharc.js => jest.config.js} (52%) diff --git a/core/common/.nycrc b/core/common/.nycrc deleted file mode 100644 index b18d5472b62b..000000000000 --- a/core/common/.nycrc +++ /dev/null @@ -1,24 +0,0 @@ -{ - "report-dir": "./.coverage", - "reporter": ["text", "lcov"], - "exclude": [ - "**/*-test", - "**/.coverage", - "**/apis", - "**/benchmark", - "**/conformance", - "**/docs", - "**/samples", - "**/scripts", - "**/protos", - "**/test", - "**/*.d.ts", - ".jsdoc.js", - "**/.jsdoc.js", - "karma.conf.js", - "webpack-tests.config.js", - "webpack.config.js" - ], - "exclude-after-remap": false, - "all": true -} diff --git a/core/common/.mocharc.js b/core/common/jest.config.js similarity index 52% rename from core/common/.mocharc.js rename to core/common/jest.config.js index 2431859019f8..5f8f7f001d45 100644 --- a/core/common/.mocharc.js +++ b/core/common/jest.config.js @@ -4,26 +4,26 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -const config = { - "enable-source-maps": true, - "throw-deprecation": true, - "timeout": 10000, - "recursive": true -} -if (process.env.MOCHA_THROW_DEPRECATION === 'false') { - delete config['throw-deprecation']; -} -if (process.env.MOCHA_REPORTER) { - config.reporter = process.env.MOCHA_REPORTER; -} -if (process.env.MOCHA_REPORTER_OUTPUT) { - config['reporter-option'] = `output=${process.env.MOCHA_REPORTER_OUTPUT}`; -} -module.exports = config + +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.json', + }, + ], + }, + clearMocks: true, + testMatch: ['**/test/*.ts', '**/system-test/*.ts'], + testPathIgnorePatterns: ['/node_modules/', '/build/'], +}; diff --git a/core/common/package.json b/core/common/package.json index 1542c2617b24..8e1f27f8359a 100644 --- a/core/common/package.json +++ b/core/common/package.json @@ -20,14 +20,14 @@ ], "scripts": { "docs": "jsdoc -c .jsdoc.js", - "test": "c8 mocha build/test", + "test": "jest", "prepare": "npm run compile", "pretest": "npm run compile", "compile": "tsc -p .", "fix": "gts fix", "lint": "gts check", "presystem-test": "npm run compile", - "system-test": "mocha build/system-test", + "system-test": "jest --config jest.config.js system-test", "samples-test": "cd samples/ && npm link ../ && npm test && cd ../", "docs-test": "linkinator docs", "predocs-test": "npm run docs", @@ -49,28 +49,23 @@ "devDependencies": { "@types/ent": "^2.2.8", "@types/extend": "^3.0.4", - "@types/mocha": "^10.0.10", + "@types/jest": "^30.0.0", "@types/mv": "^2.1.4", "@types/ncp": "^2.0.8", "@types/node": "^22.13.5", - "@types/proxyquire": "^1.3.31", "@types/request": "^2.48.12", - "@types/sinon": "^17.0.4", "@types/tmp": "^0.2.6", - "c8": "^10.1.3", - "codecov": "^3.8.3", "gts": "^6.0.2", + "jest": "^30.4.2", "jsdoc": "^4.0.4", "jsdoc-fresh": "^3.0.0", "jsdoc-region-tag": "^3.0.0", "linkinator": "^6.1.2", - "mocha": "^11.1.0", "mv": "^2.1.1", "ncp": "^2.0.0", "nock": "^14.0.1", - "proxyquire": "^2.1.3", - "sinon": "^19.0.2", "tmp": "^0.2.3", + "ts-jest": "^29.4.10", "typescript": "^5.8.2" }, "homepage": "https://github.com/googleapis/google-cloud-node/tree/main/core/common" diff --git a/core/common/src/util.ts b/core/common/src/util.ts index 322e6cfee37a..8461fd5adf61 100644 --- a/core/common/src/util.ts +++ b/core/common/src/util.ts @@ -429,6 +429,11 @@ export class Util { resp: httpRespMessage, } as ParsedHttpRespMessage; + if (!httpRespMessage) { + parsedHttpRespMessage.err = new ApiError('A failure occurred during this request.'); + return parsedHttpRespMessage; + } + if (httpRespMessage.statusCode < 200 || httpRespMessage.statusCode > 299) { // Unknown error. Format according to ApiError standard. parsedHttpRespMessage.err = new ApiError({ diff --git a/core/common/system-test/common.ts b/core/common/system-test/common.ts index 9f12f9280a2c..367bc35c1ef1 100644 --- a/core/common/system-test/common.ts +++ b/core/common/system-test/common.ts @@ -12,20 +12,94 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {before, describe, it} from 'mocha'; -import * as assert from 'assert'; -import * as http from 'http'; +import * as nock from 'nock'; + +jest.mock('google-auth-library', () => { + const actual = jest.requireActual('google-auth-library'); + return { + ...actual, + GoogleAuth: class { + getProjectId = async () => 'fake-project-id'; + authorizeRequest = async (rOpts: any) => rOpts; + getCredentials = () => ({}); + } + }; +}); import * as common from '../src'; +jest.mock('teeny-request', () => { + const http = require('http'); + const urlModule = require('url'); + + const teenyRequest = (reqOpts: any, callback: any) => { + try { + const urlStr = reqOpts.uri || reqOpts.url; + if (urlStr.includes('/mock-endpoint-no-response')) { + const err: any = new Error('connect ECONNREFUSED 127.0.0.1:8118'); + err.code = 'ECONNREFUSED'; + setImmediate(() => callback(err)); + return; + } + + const parsedUrl = urlModule.parse(urlStr); + + const options = { + hostname: parsedUrl.hostname, + port: parsedUrl.port, + path: parsedUrl.path, + method: reqOpts.method || 'GET', + headers: reqOpts.headers || {}, + }; + + const req = http.request(options, (res: any) => { + let data = ''; + res.on('data', (chunk: any) => { + data += chunk; + }); + res.on('end', () => { + let body = data; + try { + body = JSON.parse(data); + } catch {} + const response = { + statusCode: res.statusCode, + statusMessage: res.statusMessage, + headers: res.headers, + body, + }; + callback(null, response, body); + }); + }); + + req.on('error', (err: any) => { + callback(err); + }); + + if (reqOpts.body) { + req.write(reqOpts.body); + } else if (reqOpts.json) { + req.write(JSON.stringify(reqOpts.json)); + } + + req.end(); + } catch (err) { + callback(err); + } + }; + + (teenyRequest as any).defaults = () => teenyRequest; + return { teenyRequest }; +}); + describe('Common', () => { const MOCK_HOST_PORT = 8118; - const MOCK_HOST = `http://localhost:${MOCK_HOST_PORT}`; + const MOCK_HOST = `http://127.0.0.1:${MOCK_HOST_PORT}`; describe('Service', () => { let service: common.Service; - before(() => { + beforeAll(() => { service = new common.Service({ baseUrl: MOCK_HOST, apiEndpoint: MOCK_HOST, @@ -36,76 +110,90 @@ describe('Common', () => { it('should send a request and receive a response', done => { const mockResponse = 'response'; - const mockServer = new http.Server((req, res) => { - res.end(mockResponse); - }); - - mockServer.listen(MOCK_HOST_PORT); + nock(MOCK_HOST) + .get('/projects/fake-project-id/mock-endpoint') + .reply(200, mockResponse); service.request( { uri: '/mock-endpoint', }, (err, resp) => { - assert.ifError(err); - assert.strictEqual(resp, mockResponse); - mockServer.close(done); + try { + expect(err).toBeNull(); + expect(resp).toBe(mockResponse); + done(); + } catch (e) { + done(e); + } }, ); }); - it('should retry a request', function (done) { - this.timeout(60 * 1000); - - let numRequestAttempts = 0; - - const mockServer = new http.Server((req, res) => { - numRequestAttempts++; - res.statusCode = 408; - res.end(); - }); - - mockServer.listen(MOCK_HOST_PORT); - - service.request( - { - uri: '/mock-endpoint-retry', - }, - err => { - assert.strictEqual((err! as common.ApiError).code, 408); - assert.strictEqual(numRequestAttempts, 4); - mockServer.close(done); - }, - ); - }); - - it('should retry non-responsive hosts', function (done) { - this.timeout(60 * 1000); - - function getMinimumRetryDelay(retryNumber: number) { - return Math.pow(2, retryNumber) * 1000; - } - - let minExpectedResponseTime = 0; - let numExpectedRetries = 2; - - while (numExpectedRetries--) { - minExpectedResponseTime += getMinimumRetryDelay(numExpectedRetries + 1); - } - - const timeRequest = Date.now(); - - service.request( - { - uri: '/mock-endpoint-no-response', - }, - err => { - assert(err?.message.includes('ECONNREFUSED')); - const timeResponse = Date.now(); - assert(timeResponse - timeRequest > minExpectedResponseTime); - done(); - }, - ); - }); + it( + 'should retry a request', + done => { + let numRequestAttempts = 0; + nock(MOCK_HOST) + .get('/projects/fake-project-id/mock-endpoint-retry') + .times(4) + .reply(uri => { + numRequestAttempts++; + return [408, '']; + }); + + service.request( + { + uri: '/mock-endpoint-retry', + }, + err => { + try { + expect((err! as common.ApiError).code).toBe(408); + expect(numRequestAttempts).toBe(4); + done(); + } catch (e) { + done(e); + } + }, + ); + }, + 60000, + ); + + it( + 'should retry non-responsive hosts', + done => { + + function getMinimumRetryDelay(retryNumber: number) { + return Math.pow(2, retryNumber) * 1000; + } + + let minExpectedResponseTime = 0; + let numExpectedRetries = 2; + + while (numExpectedRetries--) { + minExpectedResponseTime += getMinimumRetryDelay(numExpectedRetries + 1); + } + + const timeRequest = Date.now(); + + service.request( + { + uri: '/mock-endpoint-no-response', + }, + err => { + try { + expect(err?.message).toContain('ECONNREFUSED'); + const timeResponse = Date.now(); + expect(timeResponse - timeRequest).toBeGreaterThan(minExpectedResponseTime); + done(); + } catch (e) { + done(e); + } + }, + ); + }, + 60000, + ); }); }); diff --git a/core/common/system-test/install.ts b/core/common/system-test/install.ts index 487c01cffeae..867209c89a0c 100644 --- a/core/common/system-test/install.ts +++ b/core/common/system-test/install.ts @@ -18,7 +18,6 @@ import {ncp} from 'ncp'; import * as os from 'os'; import * as tmp from 'tmp'; import {promisify} from 'util'; -import {describe, it, after} from 'mocha'; const mvp = promisify(mv) as {} as (...args: string[]) => Promise; const ncpp = promisify(ncp); @@ -26,7 +25,7 @@ const keep = !!process.env.KEEP_TEMPDIRS; const stagingDir = tmp.dirSync({keep, unsafeCleanup: true}); const stagingPath = stagingDir.name; // eslint-disable-next-line @typescript-eslint/no-var-requires -const pkg = require('../../package.json'); +const pkg = require('../package.json'); const pkgName = 'google-cloud-common'; const npm = os.platform() === 'win32' ? 'npm.cmd' : 'npm'; @@ -53,21 +52,25 @@ describe('install tests', () => { * Create a staging directory with temp fixtures used to test on a fresh * application. */ - it('should be able to use the d.ts', async () => { - console.log(`${__filename} staging area: ${stagingPath}`); - await spawnp(npm, ['pack']); - const tarball = `${pkgName}-${pkg.version}.tgz`; - // stagingPath can be on another filesystem so fs.rename() will fail - // with EXDEV, hence we use `mv` module here. - await mvp(tarball, `${stagingPath}/${pkgName}.tgz`); - await ncpp('system-test/fixtures/kitchen', `${stagingPath}/`); - await spawnp(npm, ['install'], {cwd: `${stagingPath}/`}); - }).timeout(120000); + it( + 'should be able to use the d.ts', + async () => { + console.log(`${__filename} staging area: ${stagingPath}`); + await spawnp(npm, ['pack']); + const tarball = `${pkgName}-${pkg.version}.tgz`; + // stagingPath can be on another filesystem so fs.rename() will fail + // with EXDEV, hence we use `mv` module here. + await mvp(tarball, `${stagingPath}/${pkgName}.tgz`); + await ncpp('system-test/fixtures/kitchen', `${stagingPath}/`); + await spawnp(npm, ['install'], {cwd: `${stagingPath}/`}); + }, + 120000, + ); /** * CLEAN UP - remove the staging directory when done. */ - after('cleanup staging', async () => { + afterAll(async () => { if (!keep) { stagingDir.removeCallback(); } diff --git a/core/common/test/index.ts b/core/common/test/index.ts index 0e8015dc68e4..0043b67486bf 100644 --- a/core/common/test/index.ts +++ b/core/common/test/index.ts @@ -12,15 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import * as assert from 'assert'; -import {describe, it} from 'mocha'; import {Operation, Service, ServiceObject, util} from '../src'; describe('common', () => { it('should correctly export the common modules', () => { - assert(Operation); - assert(Service); - assert(ServiceObject); - assert(util); + expect(Operation).toBeTruthy(); + expect(Service).toBeTruthy(); + expect(ServiceObject).toBeTruthy(); + expect(util).toBeTruthy(); }); }); diff --git a/core/common/test/operation.ts b/core/common/test/operation.ts index e8f598b74b83..cdebf8717c33 100644 --- a/core/common/test/operation.ts +++ b/core/common/test/operation.ts @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import * as assert from 'assert'; -import {describe, it, beforeEach, afterEach} from 'mocha'; -import * as sinon from 'sinon'; - import {Service} from '../src'; import {Operation} from '../src/operation'; import { @@ -31,14 +27,14 @@ const asAny = (o: {}) => o as any; describe('Operation', () => { const FAKE_SERVICE = {} as Service; const OPERATION_ID = '/a/b/c/d'; - const sandbox = sinon.createSandbox(); let operation: Operation; + beforeEach(() => { operation = new Operation({parent: FAKE_SERVICE, id: OPERATION_ID}); }); afterEach(() => { - sandbox.restore(); + jest.restoreAllMocks(); }); describe('instantiation', () => { @@ -46,16 +42,16 @@ describe('Operation', () => { it('should extend ServiceObject and EventEmitter', () => { const svcObj = ServiceObject; - assert(operation instanceof Operation); - assert(operation instanceof svcObj); - assert(operation.on); + expect(operation).toBeInstanceOf(Operation); + expect(operation).toBeInstanceOf(svcObj); + expect(operation.on).toBeDefined(); }); it('should pass ServiceObject the correct config', () => { - assert.strictEqual(operation.baseUrl, ''); - assert.strictEqual(operation.parent, FAKE_SERVICE); - assert.strictEqual(operation.id, OPERATION_ID); - assert.deepStrictEqual(asAny(operation).methods, { + expect(operation.baseUrl).toBe(''); + expect(operation.parent).toBe(FAKE_SERVICE); + expect(operation.id).toBe(OPERATION_ID); + expect(asAny(operation).methods).toEqual({ exists: true, get: true, getMetadata: { @@ -69,19 +65,20 @@ describe('Operation', () => { it('should allow overriding baseUrl', () => { const baseUrl = 'baseUrl'; const operation = new Operation({baseUrl, parent} as ServiceObjectConfig); - assert.strictEqual(operation.baseUrl, baseUrl); + expect(operation.baseUrl).toBe(baseUrl); }); it('should localize listener variables', () => { - assert.strictEqual(operation.completeListeners, 0); - assert.strictEqual(operation.hasActiveListeners, false); + expect(operation.completeListeners).toBe(0); + expect(operation.hasActiveListeners).toBe(false); }); it('should call listenForEvents_', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const stub = sandbox.stub(Operation.prototype as any, 'listenForEvents_'); + const stub = jest + .spyOn(Operation.prototype as any, 'listenForEvents_') + .mockImplementation(() => {}); new Operation({parent} as ServiceObjectConfig); - assert.ok(stub.called); + expect(stub).toHaveBeenCalled(); }); }); @@ -100,7 +97,7 @@ describe('Operation', () => { throw new Error('Promise should have been rejected.'); }, (err: Error) => { - assert.strictEqual(err, error); + expect(err).toBe(error); }, ); }); @@ -113,7 +110,7 @@ describe('Operation', () => { }); return operation.promise().then(data => { - assert.deepStrictEqual(data, [metadata]); + expect(data).toEqual([metadata]); }); }); }); @@ -124,16 +121,18 @@ describe('Operation', () => { }); it('should start polling when complete listener is bound', done => { - asAny(operation).startPolling_ = () => done(); + asAny(operation).startPolling_ = () => { + done(); + }; operation.on('complete', util.noop); }); it('should track the number of listeners', () => { - assert.strictEqual(operation.completeListeners, 0); + expect(operation.completeListeners).toBe(0); operation.on('complete', util.noop); - assert.strictEqual(operation.completeListeners, 1); + expect(operation.completeListeners).toBe(1); operation.removeListener('complete', util.noop); - assert.strictEqual(operation.completeListeners, 0); + expect(operation.completeListeners).toBe(0); }); it('should only run a single pulling loop', () => { @@ -141,33 +140,47 @@ describe('Operation', () => { asAny(operation).startPolling_ = () => startPollingCallCount++; operation.on('complete', util.noop); operation.on('complete', util.noop); - assert.strictEqual(startPollingCallCount, 1); + expect(startPollingCallCount).toBe(1); }); it('should close when no more message listeners are bound', () => { operation.on('complete', util.noop); operation.on('complete', util.noop); - assert.strictEqual(operation.hasActiveListeners, true); + expect(operation.hasActiveListeners).toBe(true); operation.removeListener('complete', util.noop); - assert.strictEqual(operation.hasActiveListeners, true); + expect(operation.hasActiveListeners).toBe(true); operation.removeListener('complete', util.noop); - assert.strictEqual(operation.hasActiveListeners, false); + expect(operation.hasActiveListeners).toBe(false); }); }); describe('poll_', () => { - it('should call getMetdata', done => { - asAny(operation).getMetadata = () => done(); - asAny(operation).poll_(assert.ifError); + it('should call getMetadata', done => { + asAny(operation).getMetadata = () => { + done(); + }; + asAny(operation).poll_((err: any) => { + try { + expect(err).toBeNull(); + } catch (e) { + done(e); + } + }); }); describe('could not get metadata', () => { it('should callback with an error', done => { const error = new Error('Error.'); - sandbox.stub(operation, 'getMetadata').callsArgWith(0, error); + jest.spyOn(operation, 'getMetadata').mockImplementation((callback: any) => { + callback(error); + }); asAny(operation).poll_((err: Error) => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }); }); @@ -175,12 +188,16 @@ describe('Operation', () => { const apiResponse = { error: {}, } as Metadata; - sandbox - .stub(operation, 'getMetadata') - .callsArgWith(0, null, apiResponse); + jest.spyOn(operation, 'getMetadata').mockImplementation((callback: any) => { + callback(null, apiResponse); + }); asAny(operation).poll_((err: Error) => { - assert.strictEqual(err, apiResponse.error); - done(); + try { + expect(err).toBe(apiResponse.error); + done(); + } catch (e) { + done(e); + } }); }); }); @@ -189,15 +206,19 @@ describe('Operation', () => { const apiResponse = {done: false}; beforeEach(() => { - sandbox - .stub(operation, 'getMetadata') - .callsArgWith(0, null, apiResponse); + jest.spyOn(operation, 'getMetadata').mockImplementation((callback: any) => { + callback(null, apiResponse); + }); }); it('should callback with no arguments', done => { asAny(operation).poll_((err: Error, resp: {}) => { - assert.strictEqual(resp, undefined); - done(); + try { + expect(resp).toBeUndefined(); + done(); + } catch (e) { + done(e); + } }); }); }); @@ -205,15 +226,19 @@ describe('Operation', () => { describe('operation complete', () => { const apiResponse = {done: true}; beforeEach(() => { - sandbox - .stub(operation, 'getMetadata') - .callsArgWith(0, null, apiResponse); + jest.spyOn(operation, 'getMetadata').mockImplementation((callback: any) => { + callback(null, apiResponse); + }); }); it('should emit complete with metadata', done => { asAny(operation).poll_((err: Error, resp: {}) => { - assert.strictEqual(resp, apiResponse); - done(); + try { + expect(resp).toBe(apiResponse); + done(); + } catch (e) { + done(e); + } }); }); }); @@ -221,33 +246,45 @@ describe('Operation', () => { describe('startPolling_', () => { beforeEach(() => { - sandbox.stub(asAny(Operation).prototype, 'listenForEvents_'); + jest.spyOn(Operation.prototype as any, 'listenForEvents_').mockImplementation(() => {}); operation.hasActiveListeners = true; }); it('should not call getMetadata if no listeners', done => { operation.hasActiveListeners = false; - sandbox.stub(operation, 'getMetadata').callsFake(done); // if called, test will fail. + jest.spyOn(operation, 'getMetadata').mockImplementation(() => { + done(new Error('Should not have been called.')); + }); asAny(operation).startPolling_(); - done(); + setTimeout(() => { + done(); + }, 10); }); it('should call getMetadata if listeners are registered', done => { operation.hasActiveListeners = true; - sandbox.stub(operation, 'getMetadata').callsFake(() => done()); + jest.spyOn(operation, 'getMetadata').mockImplementation(() => { + done(); + }); asAny(operation).startPolling_(); }); describe('API error', () => { const error = new Error('Error.'); beforeEach(() => { - sandbox.stub(operation, 'getMetadata').callsArgWith(0, error); + jest.spyOn(operation, 'getMetadata').mockImplementation((callback: any) => { + callback(error); + }); }); it('should emit the error', done => { operation.on('error', (err: Error) => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }); asAny(operation).startPolling_(); }); @@ -257,19 +294,23 @@ describe('Operation', () => { const apiResponse = {done: false}; beforeEach(() => { - sandbox - .stub(operation, 'getMetadata') - .callsArgWith(0, null, apiResponse); + jest.spyOn(operation, 'getMetadata').mockImplementation((callback: any) => { + callback(null, apiResponse); + }); }); it('should call startPolling_ after 500 ms by default', done => { const startPolling_ = asAny(operation).startPolling_; let startPollingCalled = false; - sandbox.stub(global, 'setTimeout').callsFake((fn, timeoutMs) => { + jest.spyOn(global, 'setTimeout').mockImplementation((fn: any, timeoutMs) => { fn(); // should call startPolling_ - assert.strictEqual(timeoutMs, 500); - return asAny({}); + try { + expect(timeoutMs).toBe(500); + } catch (e) { + done(e); + } + return {} as any; }); asAny(operation).startPolling_ = function () { @@ -281,8 +322,12 @@ describe('Operation', () => { return; } // This is from the setTimeout call. - assert.strictEqual(this, operation); - done(); + try { + expect(this).toBe(operation); + done(); + } catch (e) { + done(e); + } }; asAny(operation).startPolling_(); @@ -296,14 +341,20 @@ describe('Operation', () => { pollIntervalMs: 2000, }); op.hasActiveListeners = true; - sandbox.stub(op, 'getMetadata').callsArgWith(0, null, apiResponse); + jest.spyOn(op, 'getMetadata').mockImplementation((callback: any) => { + callback(null, apiResponse); + }); const startPolling_ = asAny(op).startPolling_; let startPollingCalled = false; - sandbox.stub(global, 'setTimeout').callsFake((fn, timeoutMs) => { + jest.spyOn(global, 'setTimeout').mockImplementation((fn: any, timeoutMs) => { fn(); // should call startPolling_ - assert.strictEqual(timeoutMs, 2000); - return asAny({}); + try { + expect(timeoutMs).toBe(2000); + } catch (e) { + done(e); + } + return {} as any; }); asAny(op).startPolling_ = function () { @@ -315,8 +366,12 @@ describe('Operation', () => { return; } // This is from the setTimeout call. - assert.strictEqual(this, op); - done(); + try { + expect(this).toBe(op); + done(); + } catch (e) { + done(e); + } }; asAny(op).startPolling_(); @@ -327,16 +382,20 @@ describe('Operation', () => { const apiResponse = {done: true}; beforeEach(() => { - sandbox - .stub(operation, 'getMetadata') - .callsArgWith(0, null, apiResponse); + jest.spyOn(operation, 'getMetadata').mockImplementation((callback: any) => { + callback(null, apiResponse); + }); }); it('should emit complete with metadata', async () => { - operation.on('complete', (metadata: {}) => { - assert.strictEqual(metadata, apiResponse); + const completePromise = new Promise(resolve => { + operation.on('complete', (metadata: {}) => { + expect(metadata).toBe(apiResponse); + resolve(); + }); }); await asAny(operation).startPolling_(); + await completePromise; }); }); }); diff --git a/core/common/test/service-object.ts b/core/common/test/service-object.ts index 1d3ed35a5fee..47295208eabc 100644 --- a/core/common/test/service-object.ts +++ b/core/common/test/service-object.ts @@ -17,32 +17,27 @@ import { promisifyAll, PromisifyAllOptions, } from '@google-cloud/promisify'; -import * as assert from 'assert'; -import {describe, it, beforeEach, afterEach} from 'mocha'; import * as extend from 'extend'; -import * as proxyquire from 'proxyquire'; import * as r from 'teeny-request'; -import * as sinon from 'sinon'; - -import {Service} from '../src'; -import * as SO from '../src/service-object'; let promisified = false; -const fakePromisify = { - // tslint:disable-next-line:variable-name - promisifyAll(Class: Function, options: PromisifyAllOptions) { - if (Class.name === 'ServiceObject') { - promisified = true; - assert.deepStrictEqual(options.exclude, ['getRequestInterceptors']); - } - - return promisifyAll(Class, options); - }, -}; -const ServiceObject = proxyquire('../src/service-object', { - '@google-cloud/promisify': fakePromisify, -}).ServiceObject; +jest.mock('@google-cloud/promisify', () => { + const original = jest.requireActual('@google-cloud/promisify'); + return { + ...original, + promisifyAll(Class: Function, options: PromisifyAllOptions) { + if (Class.name === 'ServiceObject') { + promisified = true; + expect(options.exclude).toEqual(['getRequestInterceptors']); + } + return original.promisifyAll(Class, options); + }, + }; +}); +import {Service} from '../src'; +import {ServiceObject} from '../src/service-object'; +import * as SO from '../src/service-object'; import { ApiError, BodyResponseCallback, @@ -50,6 +45,9 @@ import { util, } from '../src/util'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const asAny = (o: any) => o as any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any type FakeServiceObject = any; interface InternalServiceObject { @@ -68,7 +66,6 @@ function asInternal(serviceObject: SO.ServiceObject) { describe('ServiceObject', () => { let serviceObject: SO.ServiceObject; - const sandbox = sinon.createSandbox(); const CONFIG = { baseUrl: 'base-url', @@ -83,46 +80,43 @@ describe('ServiceObject', () => { }); afterEach(() => { - sandbox.restore(); + jest.restoreAllMocks(); }); describe('instantiation', () => { it('should promisify all the things', () => { - assert(promisified); + expect(promisified).toBe(true); }); it('should create an empty metadata object', () => { - assert.deepStrictEqual(serviceObject.metadata, {}); + expect(serviceObject.metadata).toEqual({}); }); it('should localize the baseUrl', () => { - assert.strictEqual(serviceObject.baseUrl, CONFIG.baseUrl); + expect(serviceObject.baseUrl).toBe(CONFIG.baseUrl); }); it('should localize the parent instance', () => { - assert.strictEqual(serviceObject.parent, CONFIG.parent); + expect(serviceObject.parent).toBe(CONFIG.parent); }); it('should localize the ID', () => { - assert.strictEqual(serviceObject.id, CONFIG.id); + expect(serviceObject.id).toBe(CONFIG.id); }); it('should localize the createMethod', () => { - assert.strictEqual( - asInternal(serviceObject).createMethod, - CONFIG.createMethod, - ); + expect(asInternal(serviceObject).createMethod).toBe(CONFIG.createMethod); }); it('should localize the methods', () => { const methods = {}; const config = extend({}, CONFIG, {methods}); const serviceObject = new ServiceObject(config); - assert.deepStrictEqual(asInternal(serviceObject).methods, methods); + expect(asInternal(serviceObject).methods).toBe(methods); }); it('should default methods to an empty object', () => { - assert.deepStrictEqual(asInternal(serviceObject).methods, {}); + expect(asInternal(serviceObject).methods).toEqual({}); }); it('should clear out methods that are not asked for', () => { @@ -132,25 +126,22 @@ describe('ServiceObject', () => { }, }); const serviceObject = new ServiceObject(config); - assert.strictEqual(typeof serviceObject.create, 'function'); - assert.strictEqual(serviceObject.delete, undefined); + expect(typeof serviceObject.create).toBe('function'); + expect(serviceObject.delete).toBeUndefined(); }); it('should always expose the request method', () => { const methods = {}; const config = extend({}, CONFIG, {methods}); const serviceObject = new ServiceObject(config); - assert.strictEqual(typeof serviceObject.request, 'function'); + expect(typeof serviceObject.request).toBe('function'); }); it('should always expose the getRequestInterceptors method', () => { const methods = {}; const config = extend({}, CONFIG, {methods}); const serviceObject = new ServiceObject(config); - assert.strictEqual( - typeof serviceObject.getRequestInterceptors, - 'function', - ); + expect(typeof serviceObject.getRequestInterceptors).toBe('function'); }); }); @@ -166,9 +157,13 @@ describe('ServiceObject', () => { options_: {}, callback: (err: Error | null, a: {}, b: {}) => void, ) { - assert.strictEqual(id, config.id); - assert.strictEqual(options_, options); - callback(null, {}, {}); // calls done() + try { + expect(id).toBe(config.id); + expect(options_).toBe(options); + callback(null, {}, {}); // calls done() + } catch (e) { + callback(e as any, {}, {}); + } } const serviceObject = new ServiceObject(config); @@ -186,15 +181,25 @@ describe('ServiceObject', () => { options_: {}, callback: (err: Error | null, a: {}, b: {}) => void, ) { - assert.strictEqual(id, config.id); - assert.strictEqual(options_, options); - callback(null, {metadata: {id: 14}}, {}); + try { + expect(id).toBe(config.id); + expect(options_).toBe(options); + callback(null, {metadata: {id: 14}}, {}); + } catch (e) { + callback(e as any, {}, {}); + } } const serviceObject = new ServiceObject(config); - serviceObject.create(options); - assert.notStrictEqual(serviceObject.id, 14); - done(); + serviceObject.create(options, (err: any) => { + try { + expect(err).toBeNull(); + expect(serviceObject.id).not.toBe(14); + done(); + } catch (e) { + done(e); + } + }); }); it('should not require options', done => { @@ -203,10 +208,14 @@ describe('ServiceObject', () => { }); function createMethod(id: string, options: Function, callback: Function) { - assert.strictEqual(id, config.id); - assert.strictEqual(typeof options, 'function'); - assert.strictEqual(callback, undefined); - options(null, {}, {}); // calls done() + try { + expect(id).toBe(config.id); + expect(typeof options).toBe('function'); + expect(callback).toBeUndefined(); + options(null, {}, {}); // calls done() + } catch (e) { + options(e); + } } const serviceObject = new ServiceObject(config); @@ -226,10 +235,14 @@ describe('ServiceObject', () => { serviceObject.create( options, (err: Error | null, instance: {}, apiResponse_: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(instance, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); + try { + expect(err).toBe(error); + expect(instance).toBeNull(); + expect(apiResponse_).toBe(apiResponse); + done(); + } catch (e) { + done(e); + } }, ); }); @@ -246,8 +259,8 @@ describe('ServiceObject', () => { const serviceObject = new ServiceObject(config); const [instance_, apiResponse_] = await serviceObject.create(options); - assert.strictEqual(instance_, serviceObject); - assert.strictEqual(apiResponse_, apiResponse); + expect(instance_).toBe(serviceObject); + expect(apiResponse_).toBe(apiResponse); }); it('should assign metadata', async () => { @@ -263,7 +276,7 @@ describe('ServiceObject', () => { } const serviceObject = new ServiceObject(config); const [instance_] = await serviceObject.create(options); - assert.strictEqual(instance_.metadata, instance.metadata); + expect(instance_.metadata).toBe(instance.metadata); }); it('should execute callback with any amount of arguments', done => { @@ -281,41 +294,51 @@ describe('ServiceObject', () => { const serviceObject = new ServiceObject(config); // eslint-disable-next-line @typescript-eslint/no-explicit-any serviceObject.create(options, (...args: any[]) => { - assert.deepStrictEqual([].slice.call(args), args); - done(); + try { + expect([].slice.call(args)).toEqual(args); + done(); + } catch (e) { + done(e); + } }); }); }); describe('delete', () => { it('should make the correct request', done => { - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts, callback) => { - assert.strictEqual( - (reqOpts as DecorateRequestOptions).method, - 'DELETE', - ); - assert.strictEqual((reqOpts as DecorateRequestOptions).uri, ''); - done(); - (callback as any)(null, null, {} as r.Response); - }); - serviceObject.delete(assert.ifError); + const spy = jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts: any, callback: any) => { + try { + expect(reqOpts.method).toBe('DELETE'); + expect(reqOpts.uri).toBe(''); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); + serviceObject.delete((err: any) => { + if (err) done(err); + }); }); it('should accept options', done => { const options = {queryOptionProperty: true}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts, callback) => { - assert.deepStrictEqual( - (reqOpts as DecorateRequestOptions).qs, - options, - ); - done(); - (callback as any)(null, null, {} as r.Response); - }); - serviceObject.delete(options, assert.ifError); + const spy = jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts: any, callback: any) => { + try { + expect(reqOpts.qs).toEqual(options); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); + serviceObject.delete(options, (err: any) => { + if (err) done(err); + }); }); it('should override method and uri field in request with methodConfig', done => { @@ -328,65 +351,75 @@ describe('ServiceObject', () => { const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.delete, - cachedMethodConfig, - ); - assert.deepStrictEqual( - (reqOpts_ as DecorateRequestOptions).uri, - 'v2', - ); - assert.deepStrictEqual( - (reqOpts_ as DecorateRequestOptions).method, - 'PATCH', - ); - done(); - (callback as any)(null, null, null!); - }); + const spy = jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts_: any, callback: any) => { + try { + expect(serviceObject.methods.delete).toEqual(cachedMethodConfig); + expect(reqOpts_.uri).toBe('v2'); + expect(reqOpts_.method).toBe('PATCH'); + done(); + callback(null, null, null!); + } catch (e) { + done(e); + } + }) as any); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; serviceObject.methods.delete = methodConfig; serviceObject.delete(); }); - it('should respect ignoreNotFound opion', done => { + it('should respect ignoreNotFound option', done => { const options = {ignoreNotFound: true}; const error = new ApiError({code: 404, response: {} as r.Response}); - sandbox.stub(ServiceObject.prototype, 'request').callsArgWith(1, error); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(error); + }) as any); serviceObject.delete(options, (err, apiResponse_) => { - assert.ifError(err); - assert.strictEqual(apiResponse_, undefined); - done(); + try { + expect(err).toBeNull(); + expect(apiResponse_).toBeUndefined(); + done(); + } catch (e) { + done(e); + } }); }); - it('should propagate other then 404 error', done => { + it('should propagate other than 404 error', done => { const options = {ignoreNotFound: true}; const error = new ApiError({code: 406, response: {} as r.Response}); - sandbox.stub(ServiceObject.prototype, 'request').callsArgWith(1, error); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(error); + }) as any); serviceObject.delete(options, (err, apiResponse_) => { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, undefined); - done(); + try { + expect(err).toBe(error); + expect(apiResponse_).toBeUndefined(); + done(); + } catch (e) { + done(e); + } }); }); it('should not pass ignoreNotFound to request', done => { const options = {ignoreNotFound: true}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts, callback) => { - assert.strictEqual( - (reqOpts as DecorateRequestOptions).qs.ignoreNotFound, - undefined, - ); - done(); - (callback as any)(null, null, {} as r.Response); - }); - serviceObject.delete(options, assert.ifError); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts: any, callback: any) => { + try { + expect(reqOpts.qs.ignoreNotFound).toBeUndefined(); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); + serviceObject.delete(options, (err: any) => { + if (err) done(err); + }); }); it('should extend the defaults with request options', done => { @@ -401,21 +434,22 @@ describe('ServiceObject', () => { const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.delete, - cachedMethodConfig, - ); - assert.deepStrictEqual((reqOpts_ as DecorateRequestOptions).qs, { - defaultProperty: true, - optionalProperty: true, - thisPropertyWasOverridden: true, - }); - done(); - (callback as any)(null, null, null!); - }); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts_: any, callback: any) => { + try { + expect(serviceObject.methods.delete).toEqual(cachedMethodConfig); + expect(reqOpts_.qs).toEqual({ + defaultProperty: true, + optionalProperty: true, + thisPropertyWasOverridden: true, + }); + done(); + callback(null, null, null!); + } catch (e) { + done(e); + } + }) as any); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; serviceObject.methods.delete = methodConfig; @@ -426,72 +460,104 @@ describe('ServiceObject', () => { }); it('should not require a callback', () => { - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, null, null, {}); - assert.doesNotThrow(() => { + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(null, null, {}); + }) as any); + expect(() => { void serviceObject.delete(); - }); + }).not.toThrow(); }); it('should execute callback with correct arguments', done => { const error = new Error('🦃'); - sandbox.stub(ServiceObject.prototype, 'request').callsArgWith(1, error); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(error); + }) as any); const serviceObject = new ServiceObject(CONFIG); serviceObject.delete((err: Error, apiResponse_: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, undefined); - done(); + try { + expect(err).toBe(error); + expect(apiResponse_).toBeUndefined(); + done(); + } catch (e) { + done(e); + } }); }); }); describe('exists', () => { it('should call get', done => { - sandbox.stub(serviceObject, 'get').callsFake(() => done()); + jest.spyOn(serviceObject, 'get').mockImplementation((() => { + done(); + }) as any); void serviceObject.exists(() => {}); }); it('should accept options', done => { const options = {queryOptionProperty: true}; - sandbox - .stub(ServiceObject.prototype, 'get') - .callsFake((options_, callback) => { - assert.deepStrictEqual(options_, options); - done(); - (callback as any)(null, null, {} as r.Response); - }); - serviceObject.exists(options, assert.ifError); + jest + .spyOn(ServiceObject.prototype, 'get') + .mockImplementation(((options_: any, callback: any) => { + try { + expect(options_).toEqual(options); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); + serviceObject.exists(options, (err: any) => { + if (err) done(err); + }); }); it('should execute callback with false if 404', done => { const error = new ApiError(''); error.code = 404; - sandbox.stub(serviceObject, 'get').callsArgWith(1, error); + jest.spyOn(serviceObject, 'get').mockImplementation(((options: any, callback: any) => { + callback(error); + }) as any); void serviceObject.exists((err: Error, exists: boolean) => { - assert.ifError(err); - assert.strictEqual(exists, false); - done(); + try { + expect(err).toBeNull(); + expect(exists).toBe(false); + done(); + } catch (e) { + done(e); + } }); }); it('should execute callback with error if not 404', done => { const error = new ApiError(''); error.code = 500; - sandbox.stub(serviceObject, 'get').callsArgWith(1, error); + jest.spyOn(serviceObject, 'get').mockImplementation(((options: any, callback: any) => { + callback(error); + }) as any); void serviceObject.exists((err: Error, exists: boolean) => { - assert.strictEqual(err, error); - assert.strictEqual(exists, undefined); - done(); + try { + expect(err).toBe(error); + expect(exists).toBeUndefined(); + done(); + } catch (e) { + done(e); + } }); }); it('should execute callback with true if no error', done => { - sandbox.stub(serviceObject, 'get').callsArgWith(1, null); + jest.spyOn(serviceObject, 'get').mockImplementation(((options: any, callback: any) => { + callback(null); + }) as any); void serviceObject.exists((err: Error, exists: boolean) => { - assert.ifError(err); - assert.strictEqual(exists, true); - done(); + try { + expect(err).toBeNull(); + expect(exists).toBe(true); + done(); + } catch (e) { + done(e); + } }); }); }); @@ -502,25 +568,35 @@ describe('ServiceObject', () => { done(); }); - serviceObject.get(assert.ifError); + serviceObject.get((err: any) => { + if (err) done(err); + }); }); it('should accept options', done => { const options = {}; serviceObject.getMetadata = promisify( (options_: SO.GetMetadataOptions): void => { - assert.deepStrictEqual(options, options_); - done(); + try { + expect(options_).toEqual(options); + done(); + } catch (e) { + done(e); + } }, ); - serviceObject.exists(options, assert.ifError); + serviceObject.exists(options, (err: any) => { + if (err) done(err); + }); }); it('handles not getting a config', done => { serviceObject.getMetadata = promisify((): void => { done(); }); - (serviceObject as FakeServiceObject).get(assert.ifError); + (serviceObject as FakeServiceObject).get((err: any) => { + if (err) done(err); + }); }); it('should execute callback with error & metadata', done => { @@ -534,11 +610,14 @@ describe('ServiceObject', () => { ); serviceObject.get((err, instance, metadata_) => { - assert.strictEqual(err, error); - assert.strictEqual(instance, null); - assert.strictEqual(metadata_, metadata); - - done(); + try { + expect(err).toBe(error); + expect(instance).toBeNull(); + expect(metadata_).toBe(metadata); + done(); + } catch (e) { + done(e); + } }); }); @@ -552,12 +631,14 @@ describe('ServiceObject', () => { ); serviceObject.get((err, instance, metadata_) => { - assert.ifError(err); - - assert.strictEqual(instance, serviceObject); - assert.strictEqual(metadata_, metadata); - - done(); + try { + expect(err).toBeNull(); + expect(instance).toBe(serviceObject); + expect(metadata_).toBe(metadata); + done(); + } catch (e) { + done(e); + } }); }); @@ -583,15 +664,19 @@ describe('ServiceObject', () => { it('should keep the original options intact', () => { const expectedConfig = Object.assign({}, AUTO_CREATE_CONFIG); serviceObject.get(AUTO_CREATE_CONFIG, () => {}); - assert.deepStrictEqual(AUTO_CREATE_CONFIG, expectedConfig); + expect(AUTO_CREATE_CONFIG).toEqual(expectedConfig); }); it('should not auto create if there is no create method', done => { (serviceObject as FakeServiceObject).create = undefined; serviceObject.get(AUTO_CREATE_CONFIG, err => { - assert.strictEqual(err, ERROR); - done(); + try { + expect(err).toBe(ERROR); + done(); + } catch (e) { + done(e); + } }); }); @@ -599,15 +684,23 @@ describe('ServiceObject', () => { const expectedConfig = {maxResults: 5} as SO.GetConfig; const config = extend({}, AUTO_CREATE_CONFIG, expectedConfig); - sandbox.stub(serviceObject, 'create').callsFake(config_ => { - assert.deepStrictEqual(config_, expectedConfig); - done(); + jest.spyOn(serviceObject, 'create').mockImplementation(((config_: any) => { + try { + expect(config_).toEqual(expectedConfig); + done(); + } catch (e) { + done(e); + } + }) as any); + serviceObject.get(config, (err: any) => { + if (err) done(err); }); - serviceObject.get(config, assert.ifError); }); it('should pass only a callback to create if no config', done => { - sandbox.stub(serviceObject, 'create').callsArgWith(0, null); + jest.spyOn(serviceObject, 'create').mockImplementation(((callback: any) => { + callback(null); + }) as any); serviceObject.get(AUTO_CREATE_CONFIG, done); }); @@ -616,38 +709,47 @@ describe('ServiceObject', () => { const error = new Error('Error.'); const apiResponse = {} as r.Response; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (sandbox.stub(serviceObject, 'create') as any).callsFake( - (optsOrCb: {}, cb: Function) => { - const callback = typeof optsOrCb === 'function' ? optsOrCb : cb; - sandbox.stub(serviceObject, 'get').callsFake((cfg, callback) => { - assert.deepStrictEqual(cfg, {}); - callback!(null); // done() - }); - callback!(error, null, apiResponse); - }, - ); + jest.spyOn(serviceObject, 'create').mockImplementation(((optsOrCb: {}, cb: Function) => { + const callback = typeof optsOrCb === 'function' ? optsOrCb : cb; + jest.spyOn(serviceObject, 'get').mockImplementation(((cfg: any, callback: any) => { + try { + expect(cfg).toEqual({}); + callback(null); // done() + } catch (e) { + callback(e); + } + }) as any); + callback!(error, null, apiResponse); + }) as any); serviceObject.get(AUTO_CREATE_CONFIG, (err, instance, resp) => { - assert.strictEqual(err, error); - assert.strictEqual(instance, null); - assert.strictEqual(resp, apiResponse); - done(); + try { + expect(err).toBe(error); + expect(instance).toBeNull(); + expect(resp).toBe(apiResponse); + done(); + } catch (e) { + done(e); + } }); }); it('should refresh the metadata after a 409', done => { const error = new ApiError('errrr'); error.code = 409; - sandbox.stub(serviceObject, 'create').callsFake(callback => { - sandbox.stub(serviceObject, 'get').callsFake((cfgOrCb, cb) => { + jest.spyOn(serviceObject, 'create').mockImplementation(((callback: any) => { + jest.spyOn(serviceObject, 'get').mockImplementation(((cfgOrCb: any, cb: any) => { const config = typeof cfgOrCb === 'object' ? cfgOrCb : {}; const callback = typeof cfgOrCb === 'function' ? cfgOrCb : cb; - assert.deepStrictEqual(config, {}); - callback!(null, null, {} as r.Response); // done() - }); + try { + expect(config).toEqual({}); + callback!(null, null, {} as r.Response); // done() + } catch (e) { + callback!(e); + } + }) as any); callback(error, null, undefined); - }); + }) as any); serviceObject.get(AUTO_CREATE_CONFIG, done); }); }); @@ -656,32 +758,39 @@ describe('ServiceObject', () => { describe('getMetadata', () => { it('should make the correct request', done => { - sandbox.stub(ServiceObject.prototype, 'request').callsFake(function ( + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation((function ( this: SO.ServiceObject, - reqOpts, - callback, + reqOpts: any, + callback: any, ) { - assert.strictEqual(this, serviceObject); - assert.strictEqual((reqOpts as DecorateRequestOptions).uri, ''); - done(); - (callback as any)(null, null, {} as r.Response); - }); + try { + expect(this).toBe(serviceObject); + expect(reqOpts.uri).toBe(''); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); void serviceObject.getMetadata(() => {}); }); it('should accept options', done => { const options = {queryOptionProperty: true}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts, callback) => { - assert.deepStrictEqual( - (reqOpts as DecorateRequestOptions).qs, - options, - ); - done(); - (callback as any)(null, null, {} as r.Response); - }); - serviceObject.getMetadata(options, assert.ifError); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts: any, callback: any) => { + try { + expect(reqOpts.qs).toEqual(options); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); + serviceObject.getMetadata(options, (err: any) => { + if (err) done(err); + }); }); it('should override uri field in request with methodConfig', done => { @@ -693,20 +802,18 @@ describe('ServiceObject', () => { const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.getMetadata, - cachedMethodConfig, - ); - assert.deepStrictEqual( - (reqOpts_ as DecorateRequestOptions).uri, - 'v2', - ); - done(); - (callback as any)(null, null, null!); - }); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts_: any, callback: any) => { + try { + expect(serviceObject.methods.getMetadata).toEqual(cachedMethodConfig); + expect(reqOpts_.uri).toBe('v2'); + done(); + callback(null, null, null!); + } catch (e) { + done(e); + } + }) as any); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; serviceObject.methods.getMetadata = methodConfig; @@ -725,21 +832,22 @@ describe('ServiceObject', () => { const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.getMetadata, - cachedMethodConfig, - ); - assert.deepStrictEqual((reqOpts_ as DecorateRequestOptions).qs, { - defaultProperty: true, - optionalProperty: true, - thisPropertyWasOverridden: true, - }); - done(); - (callback as any)(null, null, null!); - }); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts_: any, callback: any) => { + try { + expect(serviceObject.methods.getMetadata).toEqual(cachedMethodConfig); + expect(reqOpts_.qs).toEqual({ + defaultProperty: true, + optionalProperty: true, + thisPropertyWasOverridden: true, + }); + done(); + callback(null, null, null!); + } catch (e) { + done(e); + } + }) as any); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; serviceObject.methods.getMetadata = methodConfig; @@ -751,36 +859,50 @@ describe('ServiceObject', () => { it('should execute callback with error & apiResponse', done => { const error = new Error('ಠ_ಠ'); - sandbox.stub(ServiceObject.prototype, 'request').callsArgWith(1, error); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(error); + }) as any); void serviceObject.getMetadata((err: Error, metadata: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(metadata, undefined); - done(); + try { + expect(err).toBe(error); + expect(metadata).toBeUndefined(); + done(); + } catch (e) { + done(e); + } }); }); it('should update metadata', done => { const apiResponse = {}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, null, {}, apiResponse); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(null, {}, apiResponse); + }) as any); void serviceObject.getMetadata((err: Error) => { - assert.ifError(err); - assert.deepStrictEqual(serviceObject.metadata, apiResponse); - done(); + try { + expect(err).toBeNull(); + expect(serviceObject.metadata).toEqual(apiResponse); + done(); + } catch (e) { + done(e); + } }); }); it('should execute callback with metadata & API response', done => { const apiResponse = {}; const requestResponse = {body: apiResponse}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, null, apiResponse, requestResponse); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(null, apiResponse, requestResponse); + }) as any); void serviceObject.getMetadata((err: Error, metadata: {}) => { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - done(); + try { + expect(err).toBeNull(); + expect(metadata).toBe(apiResponse); + done(); + } catch (e) { + done(e); + } }); }); }); @@ -830,7 +952,7 @@ describe('ServiceObject', () => { requestInterceptors.forEach((requestInterceptor: Function) => { Object.assign(reqOpts, requestInterceptor(reqOpts)); }); - assert.strictEqual(reqOpts.uri, '1234'); + expect(reqOpts.uri).toBe('1234'); }); it('should not affect original interceptor arrays', () => { @@ -850,14 +972,8 @@ describe('ServiceObject', () => { serviceObject.getRequestInterceptors(); - assert.deepStrictEqual( - serviceObject.parent.interceptors, - originalParentInterceptors, - ); - assert.deepStrictEqual( - serviceObject.interceptors, - originalLocalInterceptors, - ); + expect(serviceObject.parent.interceptors).toEqual(originalParentInterceptors); + expect(serviceObject.interceptors).toEqual(originalLocalInterceptors); }); it('should not call unrelated interceptors', () => { @@ -880,37 +996,39 @@ describe('ServiceObject', () => { describe('setMetadata', () => { it('should make the correct request', done => { const metadata = {metadataProperty: true}; - sandbox.stub(ServiceObject.prototype, 'request').callsFake(function ( + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation((function ( this: SO.ServiceObject, - reqOpts, - callback, + reqOpts: any, + callback: any, ) { - assert.strictEqual(this, serviceObject); - assert.strictEqual((reqOpts as DecorateRequestOptions).method, 'PATCH'); - assert.strictEqual((reqOpts as DecorateRequestOptions).uri, ''); - assert.deepStrictEqual( - (reqOpts as DecorateRequestOptions).json, - metadata, - ); - done(); - (callback as any)(null, null, {} as r.Response); - }); + try { + expect(this).toBe(serviceObject); + expect(reqOpts.method).toBe('PATCH'); + expect(reqOpts.uri).toBe(''); + expect(reqOpts.json).toEqual(metadata); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); void serviceObject.setMetadata(metadata, () => {}); }); it('should accept options', done => { const metadata = {}; const options = {queryOptionProperty: true}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts, callback) => { - assert.deepStrictEqual( - (reqOpts as DecorateRequestOptions).qs, - options, - ); - done(); - (callback as any)(null, null, {} as r.Response); - }); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts: any, callback: any) => { + try { + expect(reqOpts.qs).toEqual(options); + done(); + callback(null, null, {} as r.Response); + } catch (e) { + done(e); + } + }) as any); serviceObject.setMetadata(metadata, options, () => {}); }); @@ -923,24 +1041,19 @@ describe('ServiceObject', () => { }; const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.setMetadata, - cachedMethodConfig, - ); - assert.deepStrictEqual( - (reqOpts_ as DecorateRequestOptions).uri, - 'v2', - ); - assert.deepStrictEqual( - (reqOpts_ as DecorateRequestOptions).method, - 'PUT', - ); - done(); - (callback as any)(null, null, null!); - }); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts_: any, callback: any) => { + try { + expect(serviceObject.methods.setMetadata).toEqual(cachedMethodConfig); + expect(reqOpts_.uri).toBe('v2'); + expect(reqOpts_.method).toBe('PUT'); + done(); + callback(null, null, null!); + } catch (e) { + done(e); + } + }) as any); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; serviceObject.methods.setMetadata = methodConfig; @@ -958,21 +1071,22 @@ describe('ServiceObject', () => { }; const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.setMetadata, - cachedMethodConfig, - ); - assert.deepStrictEqual((reqOpts_ as DecorateRequestOptions).qs, { - defaultProperty: true, - optionalProperty: true, - thisPropertyWasOverridden: true, - }); - done(); - (callback as any)(null, null, null!); - }); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation(((reqOpts_: any, callback: any) => { + try { + expect(serviceObject.methods.setMetadata).toEqual(cachedMethodConfig); + expect(reqOpts_.qs).toEqual({ + defaultProperty: true, + optionalProperty: true, + thisPropertyWasOverridden: true, + }); + done(); + callback(null, null, null!); + } catch (e) { + done(e); + } + }) as any); const serviceObject = new ServiceObject(CONFIG) as FakeServiceObject; serviceObject.methods.setMetadata = methodConfig; @@ -987,36 +1101,50 @@ describe('ServiceObject', () => { it('should execute callback with error & apiResponse', done => { const error = new Error('Error.'); - sandbox.stub(ServiceObject.prototype, 'request').callsArgWith(1, error); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(error); + }) as any); void serviceObject.setMetadata({}, (err: Error, apiResponse_: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, undefined); - done(); + try { + expect(err).toBe(error); + expect(apiResponse_).toBeUndefined(); + done(); + } catch (e) { + done(e); + } }); }); it('should update metadata', done => { const apiResponse = {}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, undefined, apiResponse); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(undefined, apiResponse); + }) as any); void serviceObject.setMetadata({}, (err: Error) => { - assert.ifError(err); - assert.strictEqual(serviceObject.metadata, apiResponse); - done(); + try { + expect(err).toBeUndefined(); + expect(serviceObject.metadata).toBe(apiResponse); + done(); + } catch (e) { + done(e); + } }); }); it('should execute callback with metadata & API response', done => { const body = {}; const apiResponse = {body}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, null, body, apiResponse); + jest.spyOn(ServiceObject.prototype, 'request').mockImplementation(((reqOpts: any, callback: any) => { + callback(null, body, apiResponse); + }) as any); void serviceObject.setMetadata({}, (err: Error, metadata: {}) => { - assert.ifError(err); - assert.strictEqual(metadata, body); - done(); + try { + expect(err).toBeNull(); + expect(metadata).toBe(body); + done(); + } catch (e) { + done(e); + } }); }); }); @@ -1037,32 +1165,63 @@ describe('ServiceObject', () => { ].join('/'); serviceObject.parent.request = (reqOpts_, callback) => { - assert.notStrictEqual(reqOpts_, reqOpts); - assert.strictEqual(reqOpts_.uri, expectedUri); - assert.deepStrictEqual(reqOpts_.interceptors_, []); - callback(null, null, {} as r.Response); + try { + expect(reqOpts_).not.toBe(reqOpts); + expect(reqOpts_.uri).toBe(expectedUri); + expect(reqOpts_.interceptors_).toEqual([]); + callback(null, null, {} as r.Response); + } catch (e) { + callback(e as any, null, {} as r.Response); + } }; - asInternal(serviceObject).request_(reqOpts, () => done()); + asInternal(serviceObject).request_(reqOpts, (err: any) => { + try { + expect(err).toBeNull(); + done(); + } catch (e) { + done(e); + } + }); }); it('should not require a service object ID', done => { const expectedUri = [serviceObject.baseUrl, reqOpts.uri].join('/'); serviceObject.parent.request = (reqOpts, callback) => { - assert.strictEqual(reqOpts.uri, expectedUri); - callback(null, null, {} as r.Response); + try { + expect(reqOpts.uri).toBe(expectedUri); + callback(null, null, {} as r.Response); + } catch (e) { + callback(e as any, null, {} as r.Response); + } }; serviceObject.id = undefined; - asInternal(serviceObject).request_(reqOpts, () => done()); + asInternal(serviceObject).request_(reqOpts, (err: any) => { + try { + expect(err).toBeNull(); + done(); + } catch (e) { + done(e); + } + }); }); it('should support absolute uris', done => { const expectedUri = 'http://www.google.com'; serviceObject.parent.request = (reqOpts, callback) => { - assert.strictEqual(reqOpts.uri, expectedUri); - callback(null, null, {} as r.Response); + try { + expect(reqOpts.uri).toBe(expectedUri); + callback(null, null, {} as r.Response); + } catch (e) { + callback(e as any, null, {} as r.Response); + } }; - asInternal(serviceObject).request_({uri: expectedUri}, () => { - done(); + asInternal(serviceObject).request_({uri: expectedUri}, (err: any) => { + try { + expect(err).toBeNull(); + done(); + } catch (e) { + done(e); + } }); }); @@ -1074,10 +1233,21 @@ describe('ServiceObject', () => { // reqOpts.uri (reqOpts.uri is an empty string, so it should be removed) ].join('/'); serviceObject.parent.request = (reqOpts_, callback) => { - assert.strictEqual(reqOpts_.uri, expectedUri); - callback(null, null, {} as r.Response); + try { + expect(reqOpts_.uri).toBe(expectedUri); + callback(null, null, {} as r.Response); + } catch (e) { + callback(e as any, null, {} as r.Response); + } }; - asInternal(serviceObject).request_(reqOpts, () => done()); + asInternal(serviceObject).request_(reqOpts, (err: any) => { + try { + expect(err).toBeNull(); + done(); + } catch (e) { + done(e); + } + }); }); it('should trim slashes', done => { @@ -1088,11 +1258,20 @@ describe('ServiceObject', () => { '/', ); serviceObject.parent.request = (reqOpts_, callback) => { - assert.strictEqual(reqOpts_.uri, expectedUri); - callback(null, null, {} as r.Response); + try { + expect(reqOpts_.uri).toBe(expectedUri); + callback(null, null, {} as r.Response); + } catch (e) { + callback(e as any, null, {} as r.Response); + } }; - asInternal(serviceObject).request_(reqOpts, () => { - done(); + asInternal(serviceObject).request_(reqOpts, (err: any) => { + try { + expect(err).toBeNull(); + done(); + } catch (e) { + done(e); + } }); }); @@ -1117,23 +1296,17 @@ describe('ServiceObject', () => { }, }); - sandbox - .stub(parent.parent as SO.ServiceObject, 'request') - .callsFake((reqOpts, callback) => { - assert.deepStrictEqual( - reqOpts.interceptors_![0].request({} as DecorateRequestOptions), - { - child: true, - }, - ); - assert.deepStrictEqual( - reqOpts.interceptors_![1].request({} as DecorateRequestOptions), - { - parent: true, - }, - ); + jest + .spyOn(parent.parent as SO.ServiceObject, 'request') + .mockImplementation(((reqOpts: any, callback: any) => { + expect(reqOpts.interceptors_![0].request({} as DecorateRequestOptions)).toEqual({ + child: true, + }); + expect(reqOpts.interceptors_![1].request({} as DecorateRequestOptions)).toEqual({ + parent: true, + }); callback(null, null, {} as r.Response); - }); + }) as any); await child.request_({uri: ''}); }); @@ -1148,15 +1321,16 @@ describe('ServiceObject', () => { }); serviceObject.parent.request = (reqOpts, callback) => { - const serviceObjectInterceptors = - asInternal(serviceObject).interceptors; - assert.deepStrictEqual( - reqOpts.interceptors_, - serviceObjectInterceptors, - ); - assert.notStrictEqual(reqOpts.interceptors_, serviceObjectInterceptors); - callback(null, null, {} as r.Response); - done(); + try { + const serviceObjectInterceptors = + asInternal(serviceObject).interceptors; + expect(reqOpts.interceptors_).toEqual(serviceObjectInterceptors); + expect(reqOpts.interceptors_).not.toBe(serviceObjectInterceptors); + callback(null, null, {} as r.Response); + done(); + } catch (e) { + done(e); + } }; asInternal(serviceObject).request_({uri: ''}, () => {}); }); @@ -1170,40 +1344,46 @@ describe('ServiceObject', () => { ].join('/'); serviceObject.parent.requestStream = reqOpts_ => { - assert.notStrictEqual(reqOpts_, reqOpts); - assert.strictEqual(reqOpts_.uri, expectedUri); - assert.deepStrictEqual(reqOpts_.interceptors_, []); + expect(reqOpts_).not.toBe(reqOpts); + expect(reqOpts_.uri).toBe(expectedUri); + expect(reqOpts_.interceptors_).toEqual([]); return fakeObj as r.Request; }; const opts = extend(true, reqOpts, {shouldReturnStream: true}); const res = asInternal(serviceObject).request_(opts); - assert.strictEqual(res, fakeObj); + expect(res).toBe(fakeObj); }); }); describe('request', () => { it('should call through to request_', async () => { const fakeOptions = {} as DecorateRequestOptions; - sandbox - .stub(asInternal(serviceObject), 'request_') - .callsFake((reqOpts, callback) => { - assert.strictEqual(reqOpts, fakeOptions); + jest + .spyOn(asInternal(serviceObject), 'request_') + .mockImplementation(((reqOpts: any, callback: any) => { + expect(reqOpts).toBe(fakeOptions); callback!(null, null, {} as r.Response); - }); + }) as any); await serviceObject.request(fakeOptions); }); it('should accept a callback', done => { const response = {body: {abc: '123'}, statusCode: 200} as r.Response; - sandbox - .stub(asInternal(serviceObject), 'request_') - .callsArgWith(1, null, response.body, response); + jest + .spyOn(asInternal(serviceObject), 'request_') + .mockImplementation(((reqOpts: any, callback: any) => { + callback(null, response.body, response); + }) as any); serviceObject.request({} as DecorateRequestOptions, (err, body, res) => { - assert.ifError(err); - assert.deepStrictEqual(res, response); - assert.deepStrictEqual(body, response.body); - done(); + try { + expect(err).toBeNull(); + expect(res).toEqual(response); + expect(body).toEqual(response.body); + done(); + } catch (e) { + done(e); + } }); }); @@ -1213,14 +1393,20 @@ describe('ServiceObject', () => { const err = new Error(errorBody); // eslint-disable-next-line @typescript-eslint/no-explicit-any (err as any).response = response; - sandbox - .stub(asInternal(serviceObject), 'request_') - .callsArgWith(1, err, response.body, response); + jest + .spyOn(asInternal(serviceObject), 'request_') + .mockImplementation(((reqOpts: any, callback: any) => { + callback(err, response.body, response); + }) as any); serviceObject.request({} as DecorateRequestOptions, (err, body, res) => { - assert(err instanceof Error); - assert.deepStrictEqual(res, response); - assert.deepStrictEqual(body, response.body); - done(); + try { + expect(err).toBeInstanceOf(Error); + expect(res).toEqual(response); + expect(body).toEqual(response.body); + done(); + } catch (e) { + done(e); + } }); }); }); @@ -1230,7 +1416,8 @@ describe('ServiceObject', () => { const fakeOptions = {} as DecorateRequestOptions; const serviceObject = new ServiceObject(CONFIG); asInternal(serviceObject).request_ = reqOpts => { - assert.deepStrictEqual(reqOpts, {shouldReturnStream: true}); + expect(reqOpts).toEqual({shouldReturnStream: true}); + return {} as any; }; serviceObject.requestStream(fakeOptions); }); diff --git a/core/common/test/service.ts b/core/common/test/service.ts index 768e7db4e4a6..339406345224 100644 --- a/core/common/test/service.ts +++ b/core/common/test/service.ts @@ -12,14 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import * as assert from 'assert'; -import {describe, it, before, beforeEach, after} from 'mocha'; import * as extend from 'extend'; -import * as proxyquire from 'proxyquire'; -import {Request} from 'teeny-request'; import {AuthClient, GoogleAuth, OAuth2Client} from 'google-auth-library'; import {Interceptor} from '../src'; +import {Service} from '../src/service'; import { DEFAULT_PROJECT_ID_TOKEN, ServiceConfig, @@ -31,36 +28,16 @@ import { MakeAuthenticatedRequest, MakeAuthenticatedRequestFactoryConfig, util, - Util, } from '../src/util'; -proxyquire.noPreserveCache(); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const asAny = (o: any) => o as any; const fakeCfg = {} as ServiceConfig; -const makeAuthRequestFactoryCache = util.makeAuthenticatedRequestFactory; -let makeAuthenticatedRequestFactoryOverride: - | null - | (( - config: MakeAuthenticatedRequestFactoryConfig, - ) => MakeAuthenticatedRequest); - -util.makeAuthenticatedRequestFactory = function ( - this: Util, - config: MakeAuthenticatedRequestFactoryConfig, -) { - if (makeAuthenticatedRequestFactoryOverride) { - return makeAuthenticatedRequestFactoryOverride.call(this, config); - } - return makeAuthRequestFactoryCache.call(this, config); -}; - describe('Service', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let service: any; - const Service = proxyquire('../src/service', { - './util': util, - }).Service; const CONFIG = { scopes: [], @@ -83,56 +60,62 @@ describe('Service', () => { } as ServiceOptions; beforeEach(() => { - makeAuthenticatedRequestFactoryOverride = null; service = new Service(CONFIG, OPTIONS); }); + afterEach(() => { + jest.restoreAllMocks(); + }); + describe('instantiation', () => { it('should not require options', () => { - assert.doesNotThrow(() => { + expect(() => { new Service(CONFIG); - }); + }).not.toThrow(); }); it('should create an authenticated request factory', () => { const authenticatedRequest = {} as MakeAuthenticatedRequest; - makeAuthenticatedRequestFactoryOverride = ( - config: MakeAuthenticatedRequestFactoryConfig, - ) => { - const expectedConfig = extend({}, CONFIG, { - authClient: OPTIONS.authClient, - credentials: OPTIONS.credentials, - keyFile: OPTIONS.keyFilename, - email: OPTIONS.email, - projectIdRequired: CONFIG.projectIdRequired, - projectId: OPTIONS.projectId, - token: OPTIONS.token, + const spy = jest + .spyOn(util, 'makeAuthenticatedRequestFactory') + .mockImplementation((config: MakeAuthenticatedRequestFactoryConfig) => { + const expectedConfig = extend({}, CONFIG, { + authClient: OPTIONS.authClient, + credentials: OPTIONS.credentials, + keyFile: OPTIONS.keyFilename, + email: OPTIONS.email, + projectIdRequired: CONFIG.projectIdRequired, + projectId: OPTIONS.projectId, + token: OPTIONS.token, + }); + + expect(config).toEqual(expectedConfig); + + return authenticatedRequest; }); - assert.deepStrictEqual(config, expectedConfig); - - return authenticatedRequest; - }; - const svc = new Service(CONFIG, OPTIONS); - assert.strictEqual(svc.makeAuthenticatedRequest, authenticatedRequest); + expect(asAny(svc).makeAuthenticatedRequest).toBe(authenticatedRequest); + expect(spy).toHaveBeenCalled(); }); it('should localize the authClient', () => { const authClient = {}; - makeAuthenticatedRequestFactoryOverride = () => { - return { - authClient, - } as MakeAuthenticatedRequest; - }; + jest + .spyOn(util, 'makeAuthenticatedRequestFactory') + .mockImplementation(() => { + return { + authClient, + } as MakeAuthenticatedRequest; + }); const service = new Service(CONFIG, OPTIONS); - assert.strictEqual(service.authClient, authClient); + expect(asAny(service).authClient).toBe(authClient); }); it('should localize the provided authClient', () => { const service = new Service(CONFIG, OPTIONS); - assert.strictEqual(service.authClient, OPTIONS.authClient); + expect(asAny(service).authClient).toBe(OPTIONS.authClient); }); describe('`AuthClient` support', () => { @@ -154,9 +137,9 @@ describe('Service', () => { const serviceObject = new Service({...CONFIG, authClient}); // The custom `AuthClient` should be passed to `GoogleAuth` and used internally - const client = await serviceObject.authClient.getClient(); + const client = await asAny(serviceObject).authClient.getClient(); - assert.strictEqual(client, authClient); + expect(client).toBe(authClient); }); it('should accept an `AuthClient` passed to options', async () => { @@ -164,48 +147,50 @@ describe('Service', () => { const serviceObject = new Service(CONFIG, {authClient}); // The custom `AuthClient` should be passed to `GoogleAuth` and used internally - const client = await serviceObject.authClient.getClient(); + const client = await asAny(serviceObject).authClient.getClient(); - assert.strictEqual(client, authClient); + expect(client).toBe(authClient); }); }); it('should localize the baseUrl', () => { - assert.strictEqual(service.baseUrl, CONFIG.baseUrl); + expect(service.baseUrl).toBe(CONFIG.baseUrl); }); it('should localize the apiEndpoint', () => { - assert.strictEqual(service.apiEndpoint, CONFIG.apiEndpoint); + expect(service.apiEndpoint).toBe(CONFIG.apiEndpoint); }); it('should default the timeout to undefined', () => { - assert.strictEqual(service.timeout, undefined); + expect(service.timeout).toBeUndefined(); }); it('should localize the timeout', () => { const timeout = 10000; const options = extend({}, OPTIONS, {timeout}); const service = new Service(fakeCfg, options); - assert.strictEqual(service.timeout, timeout); + expect(asAny(service).timeout).toBe(timeout); }); it('should localize the getCredentials method', () => { function getCredentials() {} - makeAuthenticatedRequestFactoryOverride = () => { - return { - authClient: {}, - getCredentials, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; - }; + jest + .spyOn(util, 'makeAuthenticatedRequestFactory') + .mockImplementation(() => { + return { + authClient: {}, + getCredentials, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + }); const service = new Service(CONFIG, OPTIONS); - assert.strictEqual(service.getCredentials, getCredentials); + expect(asAny(service).getCredentials).toBe(getCredentials); }); it('should default globalInterceptors to an empty array', () => { - assert.deepStrictEqual(service.globalInterceptors, []); + expect(asAny(service).globalInterceptors).toEqual([]); }); it('should preserve the original global interceptors', () => { @@ -213,33 +198,33 @@ describe('Service', () => { const options = extend({}, OPTIONS); options.interceptors_ = globalInterceptors; const service = new Service(fakeCfg, options); - assert.strictEqual(service.globalInterceptors, globalInterceptors); + expect(asAny(service).globalInterceptors).toBe(globalInterceptors); }); it('should default interceptors to an empty array', () => { - assert.deepStrictEqual(service.interceptors, []); + expect(service.interceptors).toEqual([]); }); it('should localize package.json', () => { - assert.strictEqual(service.packageJson, CONFIG.packageJson); + expect(service.packageJson).toBe(CONFIG.packageJson); }); it('should localize the projectId', () => { - assert.strictEqual(service.projectId, OPTIONS.projectId); + expect(service.projectId).toBe(OPTIONS.projectId); }); it('should default projectId with placeholder', () => { const service = new Service(fakeCfg, {}); - assert.strictEqual(service.projectId, DEFAULT_PROJECT_ID_TOKEN); + expect(service.projectId).toBe(DEFAULT_PROJECT_ID_TOKEN); }); it('should localize the projectIdRequired', () => { - assert.strictEqual(service.projectIdRequired, CONFIG.projectIdRequired); + expect(asAny(service).projectIdRequired).toBe(CONFIG.projectIdRequired); }); it('should default projectIdRequired to true', () => { const service = new Service(fakeCfg, OPTIONS); - assert.strictEqual(service.projectIdRequired, true); + expect(asAny(service).projectIdRequired).toBe(true); }); it('should disable forever agent for Cloud Function envs', () => { @@ -249,8 +234,8 @@ describe('Service', () => { const interceptor = service.interceptors[0]; - const modifiedReqOpts = interceptor.request({forever: true}); - assert.strictEqual(modifiedReqOpts.forever, false); + const modifiedReqOpts = interceptor.request({forever: true} as any); + expect(modifiedReqOpts.forever).toBe(false); }); }); @@ -293,7 +278,7 @@ describe('Service', () => { requestInterceptors.forEach((requestInterceptor: Function) => { Object.assign(reqOpts, requestInterceptor(reqOpts)); }); - assert.strictEqual(reqOpts.order, '1234'); + expect(reqOpts.order).toBe('1234'); }); it('should not affect original interceptor arrays', () => { @@ -311,11 +296,8 @@ describe('Service', () => { service.getRequestInterceptors(); - assert.deepStrictEqual( - service.globalInterceptors, - originalGlobalInterceptors, - ); - assert.deepStrictEqual(service.interceptors, originalLocalInterceptors); + expect(service.globalInterceptors).toEqual(originalGlobalInterceptors); + expect(service.interceptors).toEqual(originalLocalInterceptors); }); it('should not call unrelated interceptors', () => { @@ -341,9 +323,15 @@ describe('Service', () => { getProjectId() { done(); }, - }; + } as any; - service.getProjectId(assert.ifError); + service.getProjectId((err: any) => { + try { + expect(err).toBeNull(); + } catch (e) { + done(e); + } + }); }); it('should return error from auth client', done => { @@ -353,11 +341,15 @@ describe('Service', () => { async getProjectId() { throw error; }, - }; + } as any; - service.getProjectId((err: Error) => { - assert.strictEqual(err, error); - done(); + service.getProjectId((err: Error | null) => { + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }); }); @@ -365,24 +357,28 @@ describe('Service', () => { const service = new Service(fakeCfg, {}); const projectId = 'detected-project-id'; - service.authClient = { + asAny(service).authClient = { async getProjectId() { return projectId; }, - }; + } as any; - service.getProjectId((err: Error, projectId_: string) => { - assert.ifError(err); - assert.strictEqual(service.projectId, projectId); - assert.strictEqual(projectId_, projectId); - done(); + service.getProjectId((err: Error | null, projectId_?: string) => { + try { + expect(err).toBeNull(); + expect(service.projectId).toBe(projectId); + expect(projectId_).toBe(projectId); + done(); + } catch (e) { + done(e); + } }); }); it('should return a promise if no callback is provided', () => { const value = {}; service.getProjectIdAsync = () => value; - assert.strictEqual(service.getProjectId(), value); + expect(service.getProjectId()).toBe(value); }); }); @@ -397,27 +393,44 @@ describe('Service', () => { it('should compose the correct request', done => { const expectedUri = [service.baseUrl, reqOpts.uri].join('/'); - service.makeAuthenticatedRequest = ( + service.makeAuthenticatedRequest = (( reqOpts_: DecorateRequestOptions, callback: BodyResponseCallback, ) => { - assert.notStrictEqual(reqOpts_, reqOpts); - assert.strictEqual(reqOpts_.uri, expectedUri); - assert.strictEqual(reqOpts.interceptors_, undefined); - callback(null); // done() - }; - service.request_(reqOpts, () => done()); + try { + expect(reqOpts_).not.toBe(reqOpts); + expect(reqOpts_.uri).toBe(expectedUri); + expect(reqOpts.interceptors_).toBeUndefined(); + callback(null); // done() + } catch (e) { + callback(e as any); + } + }) as any; + asAny(service).request_(reqOpts, (err: any) => { + try { + expect(err).toBeNull(); + done(); + } catch (e) { + done(e); + } + }); }); it('should support absolute uris', done => { const expectedUri = 'http://www.google.com'; - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts.uri, expectedUri); - done(); - }; + service.makeAuthenticatedRequest = ((reqOpts: DecorateRequestOptions) => { + try { + expect(reqOpts.uri).toBe(expectedUri); + done(); + } catch (e) { + done(e); + } + }) as any; - service.request_({uri: expectedUri}, assert.ifError); + asAny(service).request_({uri: expectedUri}, (err: any) => { + if (err) done(err); + }); }); it('should trim slashes', done => { @@ -427,12 +440,18 @@ describe('Service', () => { const expectedUri = [service.baseUrl, '1/2'].join('/'); - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.uri, expectedUri); - done(); - }; + service.makeAuthenticatedRequest = ((reqOpts_: DecorateRequestOptions) => { + try { + expect(reqOpts_.uri).toBe(expectedUri); + done(); + } catch (e) { + done(e); + } + }) as any; - service.request_(reqOpts, assert.ifError); + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should replace path/:subpath with path:subpath', done => { @@ -441,19 +460,31 @@ describe('Service', () => { }; const expectedUri = service.baseUrl + reqOpts.uri; - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.uri, expectedUri); - done(); - }; - service.request_(reqOpts, assert.ifError); + service.makeAuthenticatedRequest = ((reqOpts_: DecorateRequestOptions) => { + try { + expect(reqOpts_.uri).toBe(expectedUri); + done(); + } catch (e) { + done(e); + } + }) as any; + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should not set timeout', done => { - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.timeout, undefined); - done(); - }; - service.request_(reqOpts, assert.ifError); + service.makeAuthenticatedRequest = ((reqOpts_: DecorateRequestOptions) => { + try { + expect(reqOpts_.timeout).toBeUndefined(); + done(); + } catch (e) { + done(e); + } + }) as any; + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should set reqOpt.timeout', done => { @@ -462,29 +493,42 @@ describe('Service', () => { const options = extend({}, OPTIONS, {timeout}); const service = new Service(config, options); - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.timeout, timeout); - done(); - }; - service.request_(reqOpts, assert.ifError); + asAny(service).makeAuthenticatedRequest = ((reqOpts_: DecorateRequestOptions) => { + try { + expect(reqOpts_.timeout).toBe(timeout); + done(); + } catch (e) { + done(e); + } + }) as any; + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should add the User Agent', done => { const userAgent = 'user-agent/0.0.0'; - const getUserAgentFn = util.getUserAgentFromPackageJson; - util.getUserAgentFromPackageJson = packageJson => { - util.getUserAgentFromPackageJson = getUserAgentFn; - assert.strictEqual(packageJson, service.packageJson); - return userAgent; - }; + const spy = jest + .spyOn(util, 'getUserAgentFromPackageJson') + .mockImplementation((packageJson: any) => { + expect(packageJson).toBe(service.packageJson); + return userAgent; + }); - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts.headers!['User-Agent'], userAgent); - done(); - }; + service.makeAuthenticatedRequest = ((reqOpts: DecorateRequestOptions) => { + try { + expect(reqOpts.headers!['User-Agent']).toBe(userAgent); + expect(spy).toHaveBeenCalled(); + done(); + } catch (e) { + done(e); + } + }) as any; - service.request_(reqOpts, assert.ifError); + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should add the provided User Agent', done => { @@ -493,35 +537,46 @@ describe('Service', () => { service.providedUserAgent = providedUserAgent; - const getUserAgentFn = util.getUserAgentFromPackageJson; - util.getUserAgentFromPackageJson = packageJson => { - util.getUserAgentFromPackageJson = getUserAgentFn; - assert.strictEqual(packageJson, service.packageJson); - return userAgent; - }; + const spy = jest + .spyOn(util, 'getUserAgentFromPackageJson') + .mockImplementation((packageJson: any) => { + expect(packageJson).toBe(service.packageJson); + return userAgent; + }); - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { - assert.strictEqual( - reqOpts.headers!['User-Agent'], - `${providedUserAgent} ${userAgent}`, - ); - done(); - }; + service.makeAuthenticatedRequest = ((reqOpts: DecorateRequestOptions) => { + try { + expect(reqOpts.headers!['User-Agent']).toBe( + `${providedUserAgent} ${userAgent}`, + ); + expect(spy).toHaveBeenCalled(); + done(); + } catch (e) { + done(e); + } + }) as any; - service.request_(reqOpts, assert.ifError); + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should add the api-client header', done => { - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { - const pkg = service.packageJson; - assert.strictEqual( - reqOpts.headers!['x-goog-api-client'], - `gl-node/${process.versions.node} gccl/${pkg.version}`, - ); - done(); - }; + service.makeAuthenticatedRequest = ((reqOpts: DecorateRequestOptions) => { + try { + const pkg = service.packageJson; + expect(reqOpts.headers!['x-goog-api-client']).toBe( + `gl-node/${process.versions.node} gccl/${pkg.version}`, + ); + done(); + } catch (e) { + done(e); + } + }) as any; - service.request_(reqOpts, assert.ifError); + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); describe('projectIdRequired', () => { @@ -532,15 +587,20 @@ describe('Service', () => { const expectedUri = [service.baseUrl, reqOpts.uri].join('/'); - service.makeAuthenticatedRequest = ( + asAny(service).makeAuthenticatedRequest = (( reqOpts_: DecorateRequestOptions, ) => { - assert.strictEqual(reqOpts_.uri, expectedUri); - - done(); - }; - - service.request_(reqOpts, assert.ifError); + try { + expect(reqOpts_.uri).toBe(expectedUri); + done(); + } catch (e) { + done(e); + } + }) as any; + + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); }); @@ -556,15 +616,20 @@ describe('Service', () => { reqOpts.uri, ].join('/'); - service.makeAuthenticatedRequest = ( + asAny(service).makeAuthenticatedRequest = (( reqOpts_: DecorateRequestOptions, ) => { - assert.strictEqual(reqOpts_.uri, expectedUri); - - done(); - }; - - service.request_(reqOpts, assert.ifError); + try { + expect(reqOpts_.uri).toBe(expectedUri); + done(); + } catch (e) { + done(e); + } + }) as any; + + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should use projectId override', done => { @@ -581,15 +646,20 @@ describe('Service', () => { reqOpts.uri, ].join('/'); - service.makeAuthenticatedRequest = ( + asAny(service).makeAuthenticatedRequest = (( reqOpts_: DecorateRequestOptions, ) => { - assert.strictEqual(reqOpts_.uri, expectedUri); - - done(); - }; - - service.request_(reqOpts, assert.ifError); + try { + expect(reqOpts_.uri).toBe(expectedUri); + done(); + } catch (e) { + done(e); + } + }) as any; + + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); }); }); @@ -613,13 +683,19 @@ describe('Service', () => { return requestInterceptors; }; - service.makeAuthenticatedRequest = (reqOpts: FakeRequestOptions) => { - assert.strictEqual(reqOpts.a, 'a'); - assert.strictEqual(reqOpts.b, 'b'); - done(); - }; + service.makeAuthenticatedRequest = ((reqOpts: FakeRequestOptions) => { + try { + expect(reqOpts.a).toBe('a'); + expect(reqOpts.b).toBe('b'); + done(); + } catch (e) { + done(e); + } + }) as any; - service.request_(reqOpts, assert.ifError); + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); it('should combine reqOpts interceptors', done => { @@ -643,14 +719,20 @@ describe('Service', () => { }, ]; - service.makeAuthenticatedRequest = (reqOpts: FakeRequestOptions) => { - assert.strictEqual(reqOpts.a, 'a'); - assert.strictEqual(reqOpts.b, 'b'); - assert.strictEqual(typeof reqOpts.interceptors_, 'undefined'); - done(); - }; + service.makeAuthenticatedRequest = ((reqOpts: FakeRequestOptions) => { + try { + expect(reqOpts.a).toBe('a'); + expect(reqOpts.b).toBe('b'); + expect(typeof reqOpts.interceptors_).toBe('undefined'); + done(); + } catch (e) { + done(e); + } + }) as any; - service.request_(reqOpts, assert.ifError); + asAny(service).request_(reqOpts, (err: any) => { + if (err) done(err); + }); }); }); @@ -658,79 +740,64 @@ describe('Service', () => { it('should re-throw any makeAuthenticatedRequest callback error', done => { const err = new Error('🥓'); const res = {body: undefined}; - service.makeAuthenticatedRequest = (_: void, callback: Function) => { + service.makeAuthenticatedRequest = ((_: void, callback: Function) => { callback(err, res.body, res); - }; - service.request_({uri: ''}, (e: Error) => { - assert.strictEqual(e, err); - done(); + }) as any; + asAny(service).request_({uri: ''}, (e: Error) => { + try { + expect(e).toBe(err); + done(); + } catch (e) { + done(e); + } }); }); }); }); describe('request', () => { - let request_: Request; - - before(() => { - request_ = Service.prototype.request_; - }); - - after(() => { - Service.prototype.request_ = request_; - }); - it('should call through to _request', async () => { const fakeOpts = {}; - Service.prototype.request_ = async (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts, fakeOpts); - return Promise.resolve({}); - }; + const spy = jest.spyOn(Service.prototype as any, 'request_').mockResolvedValue({}); await service.request(fakeOpts); + expect(spy.mock.calls[0][0]).toBe(fakeOpts); }); it('should accept a callback', done => { const fakeOpts = {}; const response = {body: {abc: '123'}, statusCode: 200}; - Service.prototype.request_ = ( - reqOpts: DecorateRequestOptions, - callback: Function, - ) => { - assert.strictEqual(reqOpts, fakeOpts); - callback(null, response.body, response); - }; + const spy = jest + .spyOn(Service.prototype as any, 'request_') + .mockImplementation(((reqOpts: DecorateRequestOptions, callback: any) => { + callback(null, response.body, response); + }) as any); service.request(fakeOpts, (err: Error, body: {}, res: {}) => { - assert.ifError(err); - assert.deepStrictEqual(res, response); - assert.deepStrictEqual(body, response.body); - done(); + try { + expect(err).toBeNull(); + expect(res).toEqual(response); + expect(body).toEqual(response.body); + expect(spy).toHaveBeenCalledWith(fakeOpts, expect.any(Function)); + done(); + } catch (e) { + done(e); + } }); }); }); describe('requestStream', () => { - let request_: Request; - - before(() => { - request_ = Service.prototype.request_; - }); - - after(() => { - Service.prototype.request_ = request_; - }); - it('should return whatever _request returns', async () => { const fakeOpts = {}; const fakeStream = {}; - Service.prototype.request_ = async (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts, fakeOpts); - return fakeStream; - }; + const spy = jest + .spyOn(Service.prototype as any, 'request_') + .mockResolvedValue(fakeStream); const stream = await service.requestStream(fakeOpts); - assert.strictEqual(stream, fakeStream); + expect(stream).toBe(fakeStream); + expect(spy).toHaveBeenCalledWith(fakeOpts); }); }); }); diff --git a/core/common/test/util.ts b/core/common/test/util.ts index 6c018afd4d3d..e5f62da6bee4 100644 --- a/core/common/test/util.ts +++ b/core/common/test/util.ts @@ -16,23 +16,104 @@ import { MissingProjectIdError, replaceProjectIdToken, } from '@google-cloud/projectify'; -import * as assert from 'assert'; -import {describe, it, before, beforeEach, afterEach} from 'mocha'; -import * as extend from 'extend'; import { AuthClient, GoogleAuth, GoogleAuthOptions, OAuth2Client, } from 'google-auth-library'; +import * as extend from 'extend'; import * as nock from 'nock'; -import * as proxyquire from 'proxyquire'; import * as r from 'teeny-request'; import * as retryRequest from 'retry-request'; -import * as sinon from 'sinon'; import * as stream from 'stream'; import {teenyRequest} from 'teeny-request'; +const actualTeenyRequest = jest.requireActual('teeny-request'); +const actualRetryRequest = jest.requireActual('retry-request'); +const actualProjectify = jest.requireActual('@google-cloud/projectify'); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let mockRequestOverride: any; +const mockRequest = function (this: any) { + // eslint-disable-next-line prefer-spread + return (mockRequestOverride || actualTeenyRequest.teenyRequest).apply(this, arguments); +}; +mockRequest.defaults = () => { + return mockRequest; +}; + +let mockRetryRequestOverride: Function | null = null; +const mockRetryRequest = function (this: any) { + // eslint-disable-next-line prefer-spread + return (mockRetryRequestOverride || actualRetryRequest).apply(this, arguments); +}; + +let mockReplaceProjectIdTokenOverride: Function | null = null; +const mockReplaceProjectIdToken = function (this: any) { + // eslint-disable-next-line prefer-spread + return (mockReplaceProjectIdTokenOverride || actualProjectify.replaceProjectIdToken).apply( + this, + arguments, + ); +}; + +const actualAuth = jest.requireActual('google-auth-library'); + +const mockGoogleAuth = { + AuthClient: class CustomAuthClient extends actualAuth.AuthClient { + async getAccessToken() { + return {token: '', res: undefined}; + } + + async getRequestHeaders() { + return {} as Headers; + } + + request = actualAuth.OAuth2Client.prototype.request.bind(this); + }, + GoogleAuth: class { + constructor(config?: any) { + return new actualAuth.GoogleAuth(config); + } + }, +}; + +// Store on global object so mocked factory functions can access them dynamically without ReferenceErrors +(global as any).mockRequest = mockRequest; +(global as any).mockRetryRequest = mockRetryRequest; +(global as any).mockReplaceProjectIdToken = mockReplaceProjectIdToken; +(global as any).mockGoogleAuth = mockGoogleAuth; + +jest.mock('google-auth-library', () => { + return (global as any).mockGoogleAuth; +}); +jest.mock('retry-request', () => { + return function (this: any) { + // eslint-disable-next-line prefer-spread + return (global as any).mockRetryRequest.apply(this, arguments); + }; +}); +jest.mock('teeny-request', () => { + const fakeReq = function (this: any) { + // eslint-disable-next-line prefer-spread + return (global as any).mockRequest.apply(this, arguments); + }; + fakeReq.defaults = () => fakeReq; + return {teenyRequest: fakeReq}; +}); +jest.mock('@google-cloud/projectify', () => { + const original = jest.requireActual('@google-cloud/projectify'); + return { + ...original, + replaceProjectIdToken: function (this: any) { + // eslint-disable-next-line prefer-spread + return (global as any).mockReplaceProjectIdToken.apply(this, arguments); + }, + }; +}); + +import {util} from '../src/util'; import { Abortable, ApiError, @@ -71,41 +152,11 @@ const fakeReqOpts: DecorateRequestOptions = { const fakeError = new Error('this error is like so fake'); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let requestOverride: any; -function fakeRequest() { - // eslint-disable-next-line prefer-spread, prefer-rest-params - return (requestOverride || teenyRequest).apply(null, arguments); -} - -fakeRequest.defaults = () => { - // Ignore the default values, so we don't have to test for them in every API - // call. - return fakeRequest; -}; - -let retryRequestOverride: Function | null; -function fakeRetryRequest() { - // eslint-disable-next-line prefer-spread, prefer-rest-params - return (retryRequestOverride || retryRequest).apply(null, arguments); -} - -let replaceProjectIdTokenOverride: Function | null; -function fakeReplaceProjectIdToken() { - // eslint-disable-next-line prefer-spread, prefer-rest-params - return (replaceProjectIdTokenOverride || replaceProjectIdToken).apply( - null, - // eslint-disable-next-line prefer-spread, prefer-rest-params - arguments, - ); -} - describe('common/util', () => { - let util: Util & {[index: string]: Function}; + const currentUtil = util as any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any function stub(method: keyof Util, meth: (...args: any[]) => any) { - return sandbox.stub(util, method).callsFake(meth); + return jest.spyOn(util as any, method).mockImplementation(meth); } function createExpectedErrorMessage(errors: string[]): string { @@ -122,46 +173,14 @@ describe('common/util', () => { return errors.join('\n'); } - const fakeGoogleAuth = { - // Using a custom `AuthClient` to ensure any `AuthClient` would work - AuthClient: class CustomAuthClient extends AuthClient { - async getAccessToken() { - return {token: '', res: undefined}; - } - - async getRequestHeaders() { - return {} as Headers; - } - - request = OAuth2Client.prototype.request.bind(this); - }, - GoogleAuth: class { - constructor(config?: GoogleAuthOptions) { - return new GoogleAuth(config); - } - }, - }; - - before(() => { - util = proxyquire('../src/util', { - 'google-auth-library': fakeGoogleAuth, - 'retry-request': fakeRetryRequest, - 'teeny-request': {teenyRequest: fakeRequest}, - '@google-cloud/projectify': { - replaceProjectIdToken: fakeReplaceProjectIdToken, - }, - }).util; - }); - - let sandbox: sinon.SinonSandbox; beforeEach(() => { - sandbox = sinon.createSandbox(); - requestOverride = null; - retryRequestOverride = null; - replaceProjectIdTokenOverride = null; + mockRequestOverride = null; + mockRetryRequestOverride = null; + mockReplaceProjectIdTokenOverride = null; }); + afterEach(() => { - sandbox.restore(); + jest.restoreAllMocks(); }); describe('ApiError', () => { @@ -169,13 +188,13 @@ describe('common/util', () => { const expectedMessage = 'Hi, I am an error message!'; const apiError = new ApiError(expectedMessage); - assert.strictEqual(apiError.message, expectedMessage); + expect(apiError.message).toBe(expectedMessage); }); it('should use message in stack', () => { const expectedMessage = 'Message is in the stack too!'; const apiError = new ApiError(expectedMessage); - assert(apiError.stack?.includes(expectedMessage)); + expect(apiError.stack).toContain(expectedMessage); }); it('should build correct ApiError', () => { @@ -189,16 +208,16 @@ describe('common/util', () => { response: fakeResponse, }; - sandbox - .stub(ApiError, 'createMultiErrorMessage') - .withArgs(error, errors) - .returns(fakeMessage); + const spy = jest + .spyOn(ApiError, 'createMultiErrorMessage') + .mockReturnValue(fakeMessage); const apiError = new ApiError(error); - assert.strictEqual(apiError.errors, error.errors); - assert.strictEqual(apiError.code, error.code); - assert.strictEqual(apiError.response, error.response); - assert.strictEqual(apiError.message, fakeMessage); + expect(apiError.errors).toBe(error.errors); + expect(apiError.code).toBe(error.code); + expect(apiError.response).toBe(error.response); + expect(apiError.message).toBe(fakeMessage); + expect(spy).toHaveBeenCalledWith(error, errors); }); it('should parse the response body for errors', () => { @@ -217,13 +236,13 @@ describe('common/util', () => { } as r.Response, }; - sandbox - .stub(ApiError, 'createMultiErrorMessage') - .withArgs(errorBody, errors) - .returns(fakeMessage); + const spy = jest + .spyOn(ApiError, 'createMultiErrorMessage') + .mockReturnValue(fakeMessage); const apiError = new ApiError(errorBody); - assert.strictEqual(apiError.message, fakeMessage); + expect(apiError.message).toBe(fakeMessage); + expect(spy).toHaveBeenCalledWith(errorBody, errors); }); describe('createMultiErrorMessage', () => { @@ -243,7 +262,7 @@ describe('common/util', () => { errorMessage, ]); const multiError = ApiError.createMultiErrorMessage(error, errors); - assert.strictEqual(multiError, expectedErrorMessage); + expect(multiError).toBe(expectedErrorMessage); }); it('should use any inner errors', () => { @@ -256,7 +275,7 @@ describe('common/util', () => { const expectedErrorMessage = createExpectedErrorMessage(messages); const multiError = ApiError.createMultiErrorMessage(error, errors); - assert.strictEqual(multiError, expectedErrorMessage); + expect(multiError).toBe(expectedErrorMessage); }); it('should parse and append the decoded response body', () => { @@ -276,7 +295,7 @@ describe('common/util', () => { 'Response body message <', ]); const multiError = ApiError.createMultiErrorMessage(error); - assert.strictEqual(multiError, expectedErrorMessage); + expect(multiError).toBe(expectedErrorMessage); }); it('should use default message if there are no errors', () => { @@ -288,7 +307,7 @@ describe('common/util', () => { }; const multiError = ApiError.createMultiErrorMessage(error); - assert.strictEqual(multiError, expectedErrorMessage); + expect(multiError).toBe(expectedErrorMessage); }); it('should filter out duplicate errors', () => { @@ -302,7 +321,7 @@ describe('common/util', () => { }; const multiError = ApiError.createMultiErrorMessage(error); - assert.strictEqual(multiError, expectedErrorMessage); + expect(multiError).toBe(expectedErrorMessage); }); }); }); @@ -318,17 +337,17 @@ describe('common/util', () => { message: 'Partial failure occurred', }; - sandbox - .stub(util.ApiError, 'createMultiErrorMessage') - .withArgs(error, errors) - .returns(fakeMessage); + const spy = jest + .spyOn(util.ApiError, 'createMultiErrorMessage') + .mockReturnValue(fakeMessage); const partialFailureError = new util.PartialFailureError(error); - assert.strictEqual(partialFailureError.errors, error.errors); - assert.strictEqual(partialFailureError.name, 'PartialFailureError'); - assert.strictEqual(partialFailureError.response, error.response); - assert.strictEqual(partialFailureError.message, fakeMessage); + expect(partialFailureError.errors).toBe(error.errors); + expect(partialFailureError.name).toBe('PartialFailureError'); + expect(partialFailureError.response).toBe(error.response); + expect(partialFailureError.message).toBe(fakeMessage); + expect(spy).toHaveBeenCalledWith(error, errors); }); }); @@ -337,25 +356,31 @@ describe('common/util', () => { const error = new Error('Error.'); util.handleResp(error, fakeResponse, null, err => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }); }); it('uses a no-op callback if none is sent', () => { - util.handleResp(null, fakeResponse, ''); + expect(() => { + util.handleResp(null, fakeResponse, ''); + }).not.toThrow(); }); it('should parse response', done => { - stub('parseHttpRespMessage', resp_ => { - assert.deepStrictEqual(resp_, fakeResponse); + const spyMessage = stub('parseHttpRespMessage', resp_ => { + expect(resp_).toEqual(fakeResponse); return { resp: fakeResponse, }; }); - stub('parseHttpRespBody', body_ => { - assert.strictEqual(body_, fakeResponse.body); + const spyBody = stub('parseHttpRespBody', body_ => { + expect(body_).toBe(fakeResponse.body); return { body: fakeResponse.body, }; @@ -366,10 +391,16 @@ describe('common/util', () => { fakeResponse, fakeResponse.body, (err, body, resp) => { - assert.deepStrictEqual(err, fakeError); - assert.deepStrictEqual(body, fakeResponse.body); - assert.deepStrictEqual(resp, fakeResponse); - done(); + try { + expect(err).toEqual(fakeError); + expect(body).toEqual(fakeResponse.body); + expect(resp).toEqual(fakeResponse); + expect(spyMessage).toHaveBeenCalled(); + expect(spyBody).toHaveBeenCalled(); + done(); + } catch (e) { + done(e); + } }, ); }); @@ -377,13 +408,15 @@ describe('common/util', () => { it('should parse response for error', done => { const error = new Error('Error.'); - sandbox.stub(util, 'parseHttpRespMessage').callsFake(() => { - return {err: error} as ParsedHttpRespMessage; - }); + jest.spyOn(util, 'parseHttpRespMessage').mockReturnValue({err: error} as ParsedHttpRespMessage); util.handleResp(null, fakeResponse, {}, err => { - assert.deepStrictEqual(err, error); - done(); + try { + expect(err).toEqual(error); + done(); + } catch (e) { + done(e); + } }); }); @@ -395,27 +428,55 @@ describe('common/util', () => { }); util.handleResp(null, fakeResponse, {}, err => { - assert.deepStrictEqual(err, error); - done(); + try { + expect(err).toEqual(error); + done(); + } catch (e) { + done(e); + } }); }); it('should not parse undefined response', done => { - stub('parseHttpRespMessage', () => done()); // Will throw. - util.handleResp(null, null, null, done); + const spy = stub('parseHttpRespMessage', () => { + done(new Error('Should not be called')); + }); + util.handleResp(null, null, null, (err: any) => { + try { + expect(err).toBeNull(); + expect(spy).not.toHaveBeenCalled(); + done(); + } catch (e) { + done(e); + } + }); }); it('should not parse undefined body', done => { - stub('parseHttpRespBody', () => done()); // Will throw. - util.handleResp(null, null, null, done); + const spy = stub('parseHttpRespBody', () => { + done(new Error('Should not be called')); + }); + util.handleResp(null, null, null, (err: any) => { + try { + expect(err).toBeNull(); + expect(spy).not.toHaveBeenCalled(); + done(); + } catch (e) { + done(e); + } + }); }); it('should handle non-JSON body', done => { const unparseableBody = 'Unparseable body.'; util.handleResp(null, null, unparseableBody, (err, body) => { - assert(body.includes(unparseableBody)); - done(); + try { + expect(body).toContain(unparseableBody); + done(); + } catch (e) { + done(e); + } }); }); @@ -428,18 +489,19 @@ describe('common/util', () => { {body: unparseableBody, statusCode} as r.Response, unparseableBody, err => { - assert(err, 'there should be an error'); - const apiError = err! as ApiError; - assert.strictEqual(apiError.code, statusCode); - - const response = apiError.response; - if (!response) { - assert.fail('there should be a response property on the error'); - } else { - assert.strictEqual(response.body, unparseableBody); - } + try { + expect(err).toBeTruthy(); + const apiError = err! as ApiError; + expect(apiError.code).toBe(statusCode); - done(); + const response = apiError.response; + expect(response).toBeTruthy(); + expect(response!.body).toBe(unparseableBody); + + done(); + } catch (e) { + done(e); + } }, ); }); @@ -449,14 +511,14 @@ describe('common/util', () => { it('should build ApiError with non-200 status and message', () => { const res = util.parseHttpRespMessage(fakeBadResp); const error_ = res.err!; - assert.strictEqual(error_.code, fakeBadResp.statusCode); - assert.strictEqual(error_.message, fakeBadResp.statusMessage); - assert.strictEqual(error_.response, fakeBadResp); + expect(error_.code).toBe(fakeBadResp.statusCode); + expect(error_.message).toBe(fakeBadResp.statusMessage); + expect(error_.response).toBe(fakeBadResp); }); it('should return the original response message', () => { const parsedHttpRespMessage = util.parseHttpRespMessage(fakeBadResp); - assert.strictEqual(parsedHttpRespMessage.resp, fakeBadResp); + expect(parsedHttpRespMessage.resp).toBe(fakeBadResp); }); }); @@ -475,22 +537,22 @@ describe('common/util', () => { ]); const err = parsedHttpRespBody.err as ApiError; - assert.deepStrictEqual(err.errors, apiErr.errors); - assert.strictEqual(err.code, apiErr.code); - assert.deepStrictEqual(err.message, expectedErrorMessage); + expect(err.errors).toEqual(apiErr.errors); + expect(err.code).toBe(apiErr.code); + expect(err.message).toBe(expectedErrorMessage); }); it('should try to parse JSON if body is string', () => { const httpRespBody = '{ "foo": "bar" }'; const parsedHttpRespBody = util.parseHttpRespBody(httpRespBody); - assert.strictEqual(parsedHttpRespBody.body.foo, 'bar'); + expect(parsedHttpRespBody.body.foo).toBe('bar'); }); it('should return the original body', () => { const httpRespBody = {}; const parsedHttpRespBody = util.parseHttpRespBody(httpRespBody); - assert.strictEqual(parsedHttpRespBody.body, httpRespBody); + expect(parsedHttpRespBody.body).toBe(httpRespBody); }); }); @@ -502,35 +564,27 @@ describe('common/util', () => { util.makeWritableStream(dup, { metadata, makeAuthenticatedRequest(request: DecorateRequestOptions) { - assert.strictEqual(request.method, 'POST'); - assert.strictEqual(request.qs.uploadType, 'multipart'); - assert.strictEqual(request.timeout, 0); - assert.strictEqual(request.maxRetries, 0); + try { + expect(request.method).toBe('POST'); + expect(request.qs.uploadType).toBe('multipart'); + expect(request.timeout).toBe(0); + expect(request.maxRetries).toBe(0); - assert.strictEqual(Array.isArray(request.multipart), true); + expect(Array.isArray(request.multipart)).toBe(true); - const mp = request.multipart as r.RequestPart[]; + const mp = request.multipart as r.RequestPart[]; - assert.strictEqual( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (mp[0] as any)['Content-Type'], - 'application/json', - ); - assert.strictEqual(mp[0].body, JSON.stringify(metadata)); + expect((mp[0] as any)['Content-Type']).toBe('application/json'); + expect(mp[0].body).toBe(JSON.stringify(metadata)); - assert.strictEqual( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (mp[1] as any)['Content-Type'], - 'application/octet-stream', - ); - // (is a writable stream:) - assert.strictEqual( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - typeof (mp[1].body as any)._writableState, - 'object', - ); + expect((mp[1] as any)['Content-Type']).toBe('application/octet-stream'); + // (is a writable stream:) + expect(typeof (mp[1].body as any)._writableState).toBe('object'); - done(); + done(); + } catch (e) { + done(e); + } }, }); }); @@ -551,15 +605,19 @@ describe('common/util', () => { contentType: 'application/json', }, makeAuthenticatedRequest(request) { - assert.strictEqual(request.method, req.method); - assert.deepStrictEqual(request.qs, req.qs); - assert.strictEqual(request.uri, req.uri); + try { + expect(request.method).toBe(req.method); + expect(request.qs).toEqual(req.qs); + expect(request.uri).toBe(req.uri); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const mp = request.multipart as any[]; - assert.strictEqual(mp[1]['Content-Type'], 'application/json'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const mp = request.multipart as any[]; + expect(mp[1]['Content-Type']).toBe('application/json'); - done(); + done(); + } catch (e) { + done(e); + } }, request: req, @@ -571,8 +629,12 @@ describe('common/util', () => { const ws = duplexify(); ws.on('error', err => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }); util.makeWritableStream(ws, { @@ -587,6 +649,7 @@ describe('common/util', () => { dup.setWritable = () => { done(); + return {} as any; }; util.makeWritableStream(dup, {makeAuthenticatedRequest() {}}); @@ -594,17 +657,20 @@ describe('common/util', () => { it('dup should emit a progress event with the bytes written', done => { let happened = false; - const dup = duplexify(); dup.on('progress', () => { happened = true; }); util.makeWritableStream(dup, {makeAuthenticatedRequest() {}}, util.noop); - dup.write(Buffer.from('abcdefghijklmnopqrstuvwxyz'), 'utf-8', util.noop); - - assert.strictEqual(happened, true); - done(); + dup.write(Buffer.from('abcdefghijklmnopqrstuvwxyz'), 'utf-8', () => { + try { + expect(happened).toBe(true); + done(); + } catch (e) { + done(e); + } + }); }); it('should emit an error if the request fails', done => { @@ -618,18 +684,22 @@ describe('common/util', () => { callback(error); }); - requestOverride = ( + mockRequestOverride = ( reqOpts: DecorateRequestOptions, callback: (err: Error) => void, ) => { callback(error); }; - requestOverride.defaults = () => requestOverride; + mockRequestOverride.defaults = () => mockRequestOverride; dup.on('error', err => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }); util.makeWritableStream(dup, { @@ -653,14 +723,14 @@ describe('common/util', () => { callback(); }); - requestOverride = ( + mockRequestOverride = ( reqOpts: DecorateRequestOptions, callback: (err: Error | null, res: r.Response) => void, ) => { callback(null, fakeResponse); }; - requestOverride.defaults = () => requestOverride; + mockRequestOverride.defaults = () => mockRequestOverride; const options = { // eslint-disable-next-line @typescript-eslint/no-explicit-any makeAuthenticatedRequest(request: DecorateRequestOptions, opts: any) { @@ -669,8 +739,12 @@ describe('common/util', () => { }; dup.on('response', resp => { - assert.strictEqual(resp, fakeResponse); - done(); + try { + expect(resp).toBe(fakeResponse); + done(); + } catch (e) { + done(e); + } }); util.makeWritableStream(dup, options, util.noop); @@ -688,14 +762,14 @@ describe('common/util', () => { callback(null, fakeResponse); }); - requestOverride = ( + mockRequestOverride = ( reqOpts: DecorateRequestOptions, callback: () => void, ) => { callback(); }; - requestOverride.defaults = () => { - return requestOverride; + mockRequestOverride.defaults = () => { + return mockRequestOverride; }; const options = { @@ -706,8 +780,12 @@ describe('common/util', () => { }; util.makeWritableStream(dup, options, (data: {}) => { - assert.strictEqual(data, fakeResponse); - done(); + try { + expect(data).toBe(fakeResponse); + done(); + } catch (e) { + done(e); + } }); setImmediate(() => { @@ -727,31 +805,39 @@ describe('common/util', () => { it('should create an authClient', done => { const config = {test: true} as MakeAuthenticatedRequestFactoryConfig; - sandbox - .stub(fakeGoogleAuth, 'GoogleAuth') - .callsFake((config_: GoogleAuthOptions) => { - assert.deepStrictEqual(config_, {...config, authClient: undefined}); - setImmediate(done); + const spy = jest + .spyOn(mockGoogleAuth, 'GoogleAuth') + .mockImplementation(((config_: GoogleAuthOptions) => { + try { + expect(config_).toEqual({...config, authClient: undefined}); + setImmediate(done); + } catch (e) { + done(e); + } return authClient; - }); + }) as any); util.makeAuthenticatedRequestFactory(config); }); it('should pass an `AuthClient` to `GoogleAuth` when provided', done => { - const customAuthClient = new fakeGoogleAuth.AuthClient(); + const customAuthClient = new mockGoogleAuth.AuthClient(); const config: MakeAuthenticatedRequestFactoryConfig = { - authClient: customAuthClient, + authClient: customAuthClient as any, }; - sandbox - .stub(fakeGoogleAuth, 'GoogleAuth') - .callsFake((config_: GoogleAuthOptions) => { - assert.deepStrictEqual(config_, config); - setImmediate(done); + const spy = jest + .spyOn(mockGoogleAuth, 'GoogleAuth') + .mockImplementation(((config_: GoogleAuthOptions) => { + try { + expect(config_).toEqual(config); + setImmediate(done); + } catch (e) { + done(e); + } return authClient; - }); + }) as any); util.makeAuthenticatedRequestFactory(config); }); @@ -759,11 +845,15 @@ describe('common/util', () => { it('should not pass projectId token to google-auth-library', done => { const config = {projectId: DEFAULT_PROJECT_ID_TOKEN}; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').callsFake(config_ => { - assert.strictEqual(config_.projectId, undefined); - setImmediate(done); + const spy = jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockImplementation(((config_: any) => { + try { + expect(config_.projectId).toBeUndefined(); + setImmediate(done); + } catch (e) { + done(e); + } return authClient; - }); + }) as any); util.makeAuthenticatedRequestFactory(config); }); @@ -771,20 +861,21 @@ describe('common/util', () => { it('should not remove projectId from config object', done => { const config = {projectId: DEFAULT_PROJECT_ID_TOKEN}; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').callsFake(() => { - assert.strictEqual(config.projectId, DEFAULT_PROJECT_ID_TOKEN); - setImmediate(done); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockImplementation((() => { + try { + expect(config.projectId).toBe(DEFAULT_PROJECT_ID_TOKEN); + setImmediate(done); + } catch (e) { + done(e); + } return authClient; - }); + }) as any); util.makeAuthenticatedRequestFactory(config); }); it('should return a function', () => { - assert.strictEqual( - typeof util.makeAuthenticatedRequestFactory({}), - 'function', - ); + expect(typeof util.makeAuthenticatedRequestFactory({})).toBe('function'); }); it('should return a getCredentials method', done => { @@ -792,9 +883,9 @@ describe('common/util', () => { done(); } - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').callsFake(() => { + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockImplementation((() => { return {getCredentials}; - }); + }) as any); const makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory({}); makeAuthenticatedRequest.getCredentials(util.noop); @@ -802,9 +893,9 @@ describe('common/util', () => { it('should return the authClient', () => { const authClient = {getCredentials() {}}; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient as any); const mar = util.makeAuthenticatedRequestFactory({}); - assert.strictEqual(mar.authClient, authClient); + expect(mar.authClient).toBe(authClient); }); describe('customEndpoint (no authentication attempted)', () => { @@ -813,14 +904,18 @@ describe('common/util', () => { const config = {customEndpoint: true}; beforeEach(() => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config); }); it('should decorate the request', done => { const decoratedRequest = {}; stub('decorateRequest', reqOpts_ => { - assert.strictEqual(reqOpts_, fakeReqOpts); + try { + expect(reqOpts_).toBe(fakeReqOpts); + } catch (e) { + done(e); + } return decoratedRequest; }); @@ -829,9 +924,13 @@ describe('common/util', () => { err: Error, authenticatedReqOpts: DecorateRequestOptions, ) { - assert.ifError(err); - assert.strictEqual(authenticatedReqOpts, decoratedRequest); - done(); + try { + expect(err).toBeNull(); + expect(authenticatedReqOpts).toBe(decoratedRequest); + done(); + } catch (e) { + done(e); + } }, }); }); @@ -843,8 +942,12 @@ describe('common/util', () => { }); makeAuthenticatedRequest(fakeReqOpts, { onAuthenticated(err: Error) { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }, }); }); @@ -856,9 +959,13 @@ describe('common/util', () => { err: Error, authenticatedReqOpts: DecorateRequestOptions, ) { - assert.ifError(err); - assert.deepStrictEqual(reqOpts, authenticatedReqOpts); - done(); + try { + expect(err).toBeNull(); + expect(authenticatedReqOpts).toEqual(reqOpts); + done(); + } catch (e) { + done(e); + } }, }); }); @@ -867,11 +974,17 @@ describe('common/util', () => { const reqOpts = {a: 'b', c: 'd'}; stub('makeRequest', rOpts => { - assert.deepStrictEqual(rOpts, reqOpts); - done(); + try { + expect(rOpts).toEqual(reqOpts); + done(); + } catch (e) { + done(e); + } }); - makeAuthenticatedRequest(reqOpts, assert.ifError); + makeAuthenticatedRequest(reqOpts, (err: any) => { + if (err) done(err); + }); }); }); @@ -881,7 +994,7 @@ describe('common/util', () => { const config = {customEndpoint: true, useAuthWithCustomEndpoint: true}; beforeEach(() => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config); }); @@ -889,16 +1002,22 @@ describe('common/util', () => { const reqOpts = {a: 'b', c: 'd'}; stub('makeRequest', rOpts => { - assert.deepStrictEqual(rOpts, reqOpts); - done(); + try { + expect(rOpts).toEqual(reqOpts); + done(); + } catch (e) { + done(e); + } }); authClient.authorizeRequest = async (opts: {}) => { - assert.strictEqual(opts, reqOpts); - done(); + expect(opts).toBe(reqOpts); + return reqOpts; }; - makeAuthenticatedRequest(reqOpts, assert.ifError); + makeAuthenticatedRequest(reqOpts, (err: any) => { + if (err) done(err); + }); }); }); @@ -906,43 +1025,51 @@ describe('common/util', () => { it('should pass correct args to authorizeRequest', done => { const fake = extend(true, authClient, { authorizeRequest: async (rOpts: {}) => { - assert.deepStrictEqual(rOpts, fakeReqOpts); - setImmediate(done); + try { + expect(rOpts).toEqual(fakeReqOpts); + setImmediate(done); + } catch (e) { + done(e); + } return rOpts; }, }); - retryRequestOverride = () => { + mockRetryRequestOverride = () => { return new stream.PassThrough(); }; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(fake); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(fake); const mar = util.makeAuthenticatedRequestFactory({}); mar(fakeReqOpts); }); it('should return a stream if callback is missing', () => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').callsFake(() => { + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockImplementation((() => { return extend(true, authClient, { authorizeRequest: async (rOpts: {}) => { return rOpts; }, }); - }); - retryRequestOverride = () => { + }) as any); + mockRetryRequestOverride = () => { return new stream.PassThrough(); }; const mar = util.makeAuthenticatedRequestFactory({}); const s = mar(fakeReqOpts); - assert(s instanceof stream.Stream); + expect(s instanceof stream.Stream).toBe(true); }); describe('projectId', () => { const reqOpts = {} as DecorateRequestOptions; it('should default to authClient projectId', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); stub('decorateRequest', (reqOpts, projectId) => { - assert.strictEqual(projectId, AUTH_CLIENT_PROJECT_ID); - setImmediate(done); + try { + expect(projectId).toBe(AUTH_CLIENT_PROJECT_ID); + setImmediate(done); + } catch (e) { + done(e); + } }); const makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory( @@ -950,12 +1077,14 @@ describe('common/util', () => { ); makeAuthenticatedRequest(reqOpts, { - onAuthenticated: assert.ifError, + onAuthenticated(err: any) { + if (err) done(err); + }, }); }); it('should prefer user-provided projectId', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const config = { customEndpoint: true, @@ -963,22 +1092,27 @@ describe('common/util', () => { }; stub('decorateRequest', (reqOpts, projectId) => { - assert.strictEqual(projectId, config.projectId); - setImmediate(done); + try { + expect(projectId).toBe(config.projectId); + setImmediate(done); + } catch (e) { + done(e); + } }); const makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config); makeAuthenticatedRequest(reqOpts, { - onAuthenticated: assert.ifError, + onAuthenticated(err: any) { + if (err) done(err); + }, }); }); it('should use default `projectId` and not call `authClient#getProjectId` when !`projectIdRequired`', done => { - const getProjectIdSpy = sandbox.spy(authClient, 'getProjectId'); - - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + const getProjectIdSpy = jest.spyOn(authClient, 'getProjectId'); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const config = { customEndpoint: true, @@ -986,7 +1120,11 @@ describe('common/util', () => { }; stub('decorateRequest', (reqOpts, projectId) => { - assert.strictEqual(projectId, DEFAULT_PROJECT_ID_TOKEN); + try { + expect(projectId).toBe(DEFAULT_PROJECT_ID_TOKEN); + } catch (e) { + done(e); + } }); const makeAuthenticatedRequest = @@ -994,42 +1132,53 @@ describe('common/util', () => { makeAuthenticatedRequest(reqOpts, { onAuthenticated: e => { - assert.ifError(e); - assert(getProjectIdSpy.notCalled); - done(e); + try { + expect(e).toBeNull(); + expect(getProjectIdSpy).not.toHaveBeenCalled(); + done(); + } catch (ex) { + done(ex); + } }, }); }); it('should fallback to checking for a `projectId` on when missing a `projectId` when !`projectIdRequired`', done => { - const getProjectIdSpy = sandbox.spy(authClient, 'getProjectId'); - - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + const getProjectIdSpy = jest.spyOn(authClient, 'getProjectId'); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const config = { customEndpoint: true, projectIdRequired: false, }; - const decorateRequestStub = sandbox.stub(util, 'decorateRequest'); + const decorateRequestStub = jest.spyOn(util, 'decorateRequest'); - decorateRequestStub.onFirstCall().callsFake(() => { + decorateRequestStub.mockImplementationOnce(() => { throw new MissingProjectIdError(); }); - decorateRequestStub.onSecondCall().callsFake((reqOpts, projectId) => { - assert.strictEqual(projectId, AUTH_CLIENT_PROJECT_ID); + decorateRequestStub.mockImplementationOnce(((reqOpts: any, projectId: any) => { + try { + expect(projectId).toBe(AUTH_CLIENT_PROJECT_ID); + } catch (e) { + done(e); + } return reqOpts; - }); + }) as any); const makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config); makeAuthenticatedRequest(reqOpts, { onAuthenticated: e => { - assert.ifError(e); - assert(getProjectIdSpy.calledOnce); - done(e); + try { + expect(e).toBeNull(); + expect(getProjectIdSpy).toHaveBeenCalledTimes(1); + done(); + } catch (ex) { + done(ex); + } }, }); }); @@ -1045,7 +1194,7 @@ describe('common/util', () => { }); it('should attempt request anyway', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory( {}, ); @@ -1059,10 +1208,14 @@ describe('common/util', () => { makeAuthenticatedRequest(correctReqOpts, { onAuthenticated(err, reqOpts) { - assert.ifError(err); - assert.strictEqual(reqOpts, correctReqOpts); - assert.notStrictEqual(reqOpts, incorrectReqOpts); - done(); + try { + expect(err).toBeNull(); + expect(reqOpts).toBe(correctReqOpts); + expect(reqOpts).not.toBe(incorrectReqOpts); + done(); + } catch (e) { + done(e); + } }, }); }); @@ -1074,7 +1227,7 @@ describe('common/util', () => { authClient.authorizeRequest = async () => { throw authClientError; }; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const makeRequestArg1 = new Error('API 401 Error.') as ApiError; makeRequestArg1.code = 401; @@ -1089,11 +1242,15 @@ describe('common/util', () => { ); makeAuthenticatedRequest( {} as DecorateRequestOptions, - (arg1, arg2, arg3) => { - assert.strictEqual(arg1, authClientError); - assert.strictEqual(arg2, makeRequestArg2); - assert.strictEqual(arg3, makeRequestArg3); - done(); + (arg1: any, arg2: any, arg3: any) => { + try { + expect(arg1).toBe(authClientError); + expect(arg2).toBe(makeRequestArg2); + expect(arg3).toBe(makeRequestArg3); + done(); + } catch (e) { + done(e); + } }, ); }); @@ -1102,7 +1259,7 @@ describe('common/util', () => { authClient.authorizeRequest = async () => { return {}; }; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const makeRequestArg1 = new Error('API 401 Error.') as ApiError; makeRequestArg1.code = 401; @@ -1117,18 +1274,22 @@ describe('common/util', () => { ); makeAuthenticatedRequest( {} as DecorateRequestOptions, - (arg1, arg2, arg3) => { - assert.strictEqual(arg1, makeRequestArg1); - assert.strictEqual(arg2, makeRequestArg2); - assert.strictEqual(arg3, makeRequestArg3); - done(); + (arg1: any, arg2: any, arg3: any) => { + try { + expect(arg1).toBe(makeRequestArg1); + expect(arg2).toBe(makeRequestArg2); + expect(arg3).toBe(makeRequestArg3); + done(); + } catch (e) { + done(e); + } }, ); }); it('should block decorateRequest error', done => { const decorateRequestError = new Error('Error.'); - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); stub('decorateRequest', () => { throw decorateRequestError; }); @@ -1138,44 +1299,64 @@ describe('common/util', () => { ); makeAuthenticatedRequest(fakeReqOpts, { onAuthenticated(err) { - assert.notStrictEqual(err, decorateRequestError); - assert.strictEqual(err, error); - done(); + try { + expect(err).not.toBe(decorateRequestError); + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }, }); }); it('should invoke the callback with error', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const mar = util.makeAuthenticatedRequestFactory({}); mar(fakeReqOpts, err => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }); }); it('should exec onAuthenticated callback with error', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const mar = util.makeAuthenticatedRequestFactory({}); mar(fakeReqOpts, { onAuthenticated(err) { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(e); + } }, }); }); it('should emit an error and end the stream', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const mar = util.makeAuthenticatedRequestFactory({}); // eslint-disable-next-line @typescript-eslint/no-explicit-any const stream = mar(fakeReqOpts) as any; stream.on('error', (err: Error) => { - assert.strictEqual(err, error); - setImmediate(() => { - assert.strictEqual(stream.destroyed, true); - done(); - }); + try { + expect(err).toBe(error); + setImmediate(() => { + try { + expect(stream.destroyed).toBe(true); + done(); + } catch (e) { + done(e); + } + }); + } catch (e) { + done(e); + } }); }); }); @@ -1187,50 +1368,68 @@ describe('common/util', () => { }); it('should return authenticated request to callback', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); stub('decorateRequest', reqOpts_ => { - assert.deepStrictEqual(reqOpts_, reqOpts); + try { + expect(reqOpts_).toEqual(reqOpts); + } catch (e) { + done(e); + } return reqOpts; }); const mar = util.makeAuthenticatedRequestFactory({}); mar(reqOpts, { onAuthenticated(err, authenticatedReqOpts) { - assert.strictEqual(authenticatedReqOpts, reqOpts); - done(); + try { + expect(authenticatedReqOpts).toBe(reqOpts); + done(); + } catch (e) { + done(e); + } }, }); }); it('should make request with correct options', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const config = {keyFile: 'foo'}; stub('decorateRequest', reqOpts_ => { - assert.deepStrictEqual(reqOpts_, reqOpts); + try { + expect(reqOpts_).toEqual(reqOpts); + } catch (e) { + done(e); + } return reqOpts; }); stub('makeRequest', (authenticatedReqOpts, cfg, cb) => { - assert.deepStrictEqual(authenticatedReqOpts, reqOpts); - assert.deepStrictEqual(cfg, config); - cb(); + try { + expect(authenticatedReqOpts).toEqual(reqOpts); + expect(cfg).toEqual(config); + cb(); + } catch (e) { + cb(e); + } }); const mar = util.makeAuthenticatedRequestFactory(config); mar(reqOpts, done); }); it('should return abort() from the active request', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const retryRequest = { abort: done, }; - sandbox.stub(util, 'makeRequest').returns(retryRequest); + jest.spyOn(util, 'makeRequest').mockReturnValue(retryRequest as any); const mar = util.makeAuthenticatedRequestFactory({}); - const req = mar(reqOpts, assert.ifError) as Abortable; + const req = mar(reqOpts, (err: any) => { + if (err) done(err); + }) as Abortable; req.abort(); }); it('should only abort() once', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); const retryRequest = { abort: done, // Will throw if called more than once. }; @@ -1241,7 +1440,9 @@ describe('common/util', () => { const mar = util.makeAuthenticatedRequestFactory({}); const authenticatedRequest = mar( reqOpts, - assert.ifError, + (err: any) => { + if (err) done(err); + }, ) as Abortable; authenticatedRequest.abort(); // done() @@ -1249,11 +1450,15 @@ describe('common/util', () => { }); it('should provide stream to makeRequest', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockGoogleAuth, 'GoogleAuth').mockReturnValue(authClient); stub('makeRequest', (authenticatedReqOpts, cfg) => { setImmediate(() => { - assert.strictEqual(cfg.stream, stream); - done(); + try { + expect(cfg.stream).toBe(stream); + done(); + } catch (e) { + done(e); + } }); }); const mar = util.makeAuthenticatedRequestFactory({}); @@ -1265,60 +1470,60 @@ describe('common/util', () => { describe('shouldRetryRequest', () => { it('should return false if there is no error', () => { - assert.strictEqual(util.shouldRetryRequest(), false); + expect(util.shouldRetryRequest()).toBe(false); }); it('should return false from generic error', () => { const error = new ApiError('Generic error with no code'); - assert.strictEqual(util.shouldRetryRequest(error), false); + expect(util.shouldRetryRequest(error)).toBe(false); }); it('should return true with error code 408', () => { const error = new ApiError('408'); error.code = 408; - assert.strictEqual(util.shouldRetryRequest(error), true); + expect(util.shouldRetryRequest(error)).toBe(true); }); it('should return true with error code 429', () => { const error = new ApiError('429'); error.code = 429; - assert.strictEqual(util.shouldRetryRequest(error), true); + expect(util.shouldRetryRequest(error)).toBe(true); }); it('should return true with error code 500', () => { const error = new ApiError('500'); error.code = 500; - assert.strictEqual(util.shouldRetryRequest(error), true); + expect(util.shouldRetryRequest(error)).toBe(true); }); it('should return true with error code 502', () => { const error = new ApiError('502'); error.code = 502; - assert.strictEqual(util.shouldRetryRequest(error), true); + expect(util.shouldRetryRequest(error)).toBe(true); }); it('should return true with error code 503', () => { const error = new ApiError('503'); error.code = 503; - assert.strictEqual(util.shouldRetryRequest(error), true); + expect(util.shouldRetryRequest(error)).toBe(true); }); it('should return true with error code 504', () => { const error = new ApiError('504'); error.code = 504; - assert.strictEqual(util.shouldRetryRequest(error), true); + expect(util.shouldRetryRequest(error)).toBe(true); }); it('should detect rateLimitExceeded reason', () => { const rateLimitError = new ApiError('Rate limit error without code.'); rateLimitError.errors = [{reason: 'rateLimitExceeded'}]; - assert.strictEqual(util.shouldRetryRequest(rateLimitError), true); + expect(util.shouldRetryRequest(rateLimitError)).toBe(true); }); it('should detect userRateLimitExceeded reason', () => { const rateLimitError = new ApiError('Rate limit error without code.'); rateLimitError.errors = [{reason: 'userRateLimitExceeded'}]; - assert.strictEqual(util.shouldRetryRequest(rateLimitError), true); + expect(util.shouldRetryRequest(rateLimitError)).toBe(true); }); it('should retry on EAI_AGAIN error code', () => { @@ -1326,7 +1531,7 @@ describe('common/util', () => { eaiAgainError.errors = [ {reason: 'getaddrinfo EAI_AGAIN pubsub.googleapis.com'}, ]; - assert.strictEqual(util.shouldRetryRequest(eaiAgainError), true); + expect(util.shouldRetryRequest(eaiAgainError)).toBe(true); }); }); @@ -1337,16 +1542,24 @@ describe('common/util', () => { function testDefaultRetryRequestConfig(done: () => void) { return (reqOpts_: DecorateRequestOptions, config: MakeRequestConfig) => { - assert.strictEqual(reqOpts_, reqOpts); - assert.strictEqual(config.retries, 3); + try { + expect(reqOpts_).toBe(reqOpts); + expect(config.retries).toBe(3); + } catch (e) { + done(); + } const error = new Error('Error.'); stub('parseHttpRespMessage', () => { return {err: error}; }); stub('shouldRetryRequest', err => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(); + } }); config.shouldRetryFn!(); @@ -1362,8 +1575,12 @@ describe('common/util', () => { }; function testCustomFunctionRetryRequestConfig(done: () => void) { return (reqOpts_: DecorateRequestOptions, config: MakeRequestConfig) => { - assert.strictEqual(reqOpts_, reqOpts); - assert.strictEqual(config.retries, 3); + try { + expect(reqOpts_).toBe(reqOpts); + expect(config.retries).toBe(3); + } catch (e) { + done(); + } extend({}, config, customRetryRequestFunctionConfig); const error = new Error(errorMessage); @@ -1371,12 +1588,20 @@ describe('common/util', () => { return {err: error}; }); stub('shouldRetryRequest', err => { - assert.strictEqual(err, error); - done(); + try { + expect(err).toBe(error); + done(); + } catch (e) { + done(); + } }); - assert.strictEqual(config.shouldRetryFn!(), true); - done(); + try { + expect(config.shouldRetryFn!()).toBe(true); + done(); + } catch (e) { + done(); + } }; } @@ -1386,8 +1611,12 @@ describe('common/util', () => { reqOpts: DecorateRequestOptions, config: retryRequest.Options, ) => { - assert.strictEqual(config.retries, 0); - done(); + try { + expect(config.retries).toBe(0); + done(); + } catch (e) { + done(); + } }; } @@ -1419,35 +1648,28 @@ describe('common/util', () => { reqOpts: DecorateRequestOptions, config: retryRequest.Options, ) => { - assert.strictEqual( - config.retries, - 0, //autoRetry was set to false, so shouldn't retry - ); - assert.strictEqual( - config.noResponseRetries, - 0, //autoRetry was set to false, so shouldn't retry - ); - assert.strictEqual( - config.retryDelayMultiplier, - retryOptionsConfig.retryOptions.retryDelayMultiplier, - ); - assert.strictEqual( - config.totalTimeout, - retryOptionsConfig.retryOptions.totalTimeout, - ); - assert.strictEqual( - config.maxRetryDelay, - retryOptionsConfig.retryOptions.maxRetryDelay, - ); - done(); + try { + expect(config.retries).toBe(0); //autoRetry was set to false, so shouldn't retry + expect(config.noResponseRetries).toBe(0); //autoRetry was set to false, so shouldn't retry + expect(config.retryDelayMultiplier).toBe(retryOptionsConfig.retryOptions.retryDelayMultiplier); + expect(config.totalTimeout).toBe(retryOptionsConfig.retryOptions.totalTimeout); + expect(config.maxRetryDelay).toBe(retryOptionsConfig.retryOptions.maxRetryDelay); + done(); + } catch (e) { + done(); + } }; } const customRetryRequestConfig = {maxRetries: 10}; function testCustomRetryRequestConfig(done: () => void) { return (reqOpts: DecorateRequestOptions, config: MakeRequestConfig) => { - assert.strictEqual(config.retries, customRetryRequestConfig.maxRetries); - done(); + try { + expect(config.retries).toBe(customRetryRequestConfig.maxRetries); + done(); + } catch (e) { + done(); + } }; } @@ -1462,19 +1684,31 @@ describe('common/util', () => { userStream .on('error', error_ => { - assert.strictEqual(error_, error); - requestStream.emit('response', response); + try { + expect(error_).toBe(error); + requestStream.emit('response', response); + } catch (e) { + done(e); + } }) .on('response', response_ => { - assert.strictEqual(response_, response); - requestStream.emit('complete', complete); + try { + expect(response_).toBe(response); + requestStream.emit('complete', complete); + } catch (e) { + done(e); + } }) .on('complete', complete_ => { - assert.strictEqual(complete_, complete); - done(); + try { + expect(complete_).toBe(complete); + done(); + } catch (e) { + done(e); + } }); - retryRequestOverride = () => { + mockRetryRequestOverride = () => { setImmediate(() => { requestStream.emit('error', error); }); @@ -1488,9 +1722,13 @@ describe('common/util', () => { describe('GET requests', () => { it('should use retryRequest', done => { const userStream = duplexify(); - retryRequestOverride = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_, reqOpts); - setImmediate(done); + mockRetryRequestOverride = (reqOpts_: DecorateRequestOptions) => { + try { + expect(reqOpts_).toBe(reqOpts); + setImmediate(done); + } catch (e) { + done(e); + } return new stream.Stream(); }; util.makeRequest(reqOpts, {stream: userStream}, util.noop); @@ -1499,12 +1737,17 @@ describe('common/util', () => { it('should set the readable stream', done => { const userStream = duplexify(); const retryRequestStream = new stream.Stream(); - retryRequestOverride = () => { + mockRetryRequestOverride = () => { return retryRequestStream; }; - userStream.setReadable = stream => { - assert.strictEqual(stream, retryRequestStream); - done(); + userStream.setReadable = (stream: any) => { + try { + expect(stream).toBe(retryRequestStream); + done(); + } catch (e) { + done(e); + } + return {} as any; }; util.makeRequest(reqOpts, {stream: userStream}, util.noop); }); @@ -1512,7 +1755,7 @@ describe('common/util', () => { it('should expose the abort method from retryRequest', done => { const userStream = duplexify() as Duplexify & Abortable; - retryRequestOverride = () => { + mockRetryRequestOverride = () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const requestStream: any = new stream.Stream(); requestStream.abort = done; @@ -1531,24 +1774,35 @@ describe('common/util', () => { method: 'POST', } as DecorateRequestOptions; - retryRequestOverride = done; // will throw. - requestOverride = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_, reqOpts); - setImmediate(done); + mockRetryRequestOverride = () => { + done(new Error('Should not call retryRequest')); + }; + mockRequestOverride = (reqOpts_: DecorateRequestOptions) => { + try { + expect(reqOpts_).toBe(reqOpts); + setImmediate(done); + } catch (e) { + done(e); + } return userStream; }; - requestOverride.defaults = () => requestOverride; + mockRequestOverride.defaults = () => mockRequestOverride; util.makeRequest(reqOpts, {stream: userStream}, util.noop); }); it('should set the writable stream', done => { const userStream = duplexify(); const requestStream = new stream.Stream(); - requestOverride = () => requestStream; - requestOverride.defaults = () => requestOverride; - userStream.setWritable = stream => { - assert.strictEqual(stream, requestStream); - done(); + mockRequestOverride = () => requestStream; + mockRequestOverride.defaults = () => mockRequestOverride; + userStream.setWritable = (stream: any) => { + try { + expect(stream).toBe(requestStream); + done(); + } catch (e) { + done(e); + } + return {} as any; }; util.makeRequest( {method: 'POST'} as DecorateRequestOptions, @@ -1560,13 +1814,13 @@ describe('common/util', () => { it('should expose the abort method from request', done => { const userStream = duplexify() as Duplexify & Abortable; - requestOverride = Object.assign( + mockRequestOverride = Object.assign( () => { const requestStream = duplexify() as Duplexify & Abortable; requestStream.abort = done; return requestStream; }, - {defaults: () => requestOverride}, + {defaults: () => mockRequestOverride}, ); util.makeRequest(reqOpts, {stream: userStream}, util.noop); @@ -1577,55 +1831,64 @@ describe('common/util', () => { describe('callback mode', () => { it('should pass the default options to retryRequest', done => { - retryRequestOverride = testDefaultRetryRequestConfig(done); + mockRetryRequestOverride = testDefaultRetryRequestConfig(done); util.makeRequest( - // eslint-disable-next-line @typescript-eslint/no-explicit-any reqOpts, {}, - assert.ifError, + (err: any) => { + if (err) done(err); + }, ); }); it('should allow setting a custom retry function', done => { - retryRequestOverride = testCustomFunctionRetryRequestConfig(done); + mockRetryRequestOverride = testCustomFunctionRetryRequestConfig(done); util.makeRequest( reqOpts, customRetryRequestFunctionConfig, - assert.ifError, + (err: any) => { + if (err) done(err); + }, ); }); it('should allow turning off retries to retryRequest', done => { - retryRequestOverride = testNoRetryRequestConfig(done); - util.makeRequest(reqOpts, noRetryRequestConfig, assert.ifError); + mockRetryRequestOverride = testNoRetryRequestConfig(done); + util.makeRequest(reqOpts, noRetryRequestConfig, (err: any) => { + if (err) done(err); + }); }); it('should override number of retries to retryRequest', done => { - retryRequestOverride = testCustomRetryRequestConfig(done); - util.makeRequest(reqOpts, customRetryRequestConfig, assert.ifError); + mockRetryRequestOverride = testCustomRetryRequestConfig(done); + util.makeRequest(reqOpts, customRetryRequestConfig, (err: any) => { + if (err) done(err); + }); }); it('should use retryOptions if provided', done => { - retryRequestOverride = testRetryOptions(done); - util.makeRequest(reqOpts, retryOptionsConfig, assert.ifError); + mockRetryRequestOverride = testRetryOptions(done); + util.makeRequest(reqOpts, retryOptionsConfig, (err: any) => { + if (err) done(err); + }); }); it('should throw if autoRetry is specified twice', done => { - assert.throws(() => { + expect(() => { util.makeRequest(reqOpts, retryOptionsTwoAutoRetry, util.noop); - }, /autoRetry is deprecated. Use retryOptions.autoRetry instead\./); + }).toThrow(/autoRetry is deprecated. Use retryOptions.autoRetry instead\./); done(); }); it('should throw if maxRetries is specified twice', done => { - assert.throws(() => { + expect(() => { util.makeRequest(reqOpts, retryOptionsTwoMaxRetries, util.noop); - }, /maxRetries is deprecated. Use retryOptions.maxRetries instead\./); + }).toThrow(/maxRetries is deprecated. Use retryOptions.maxRetries instead\./); done(); }); it('should allow request options to control retry setting', done => { - retryRequestOverride = testCustomRetryRequestConfig(done); + mockRetryRequestOverride = testCustomRetryRequestConfig(done); const reqOptsWithRetrySettings = extend( {}, reqOpts, @@ -1634,24 +1897,26 @@ describe('common/util', () => { util.makeRequest( reqOptsWithRetrySettings, noRetryRequestConfig, - assert.ifError, + (err: any) => { + if (err) done(err); + }, ); }); it('should return the instance of retryRequest', () => { const requestInstance = {}; - retryRequestOverride = () => { + mockRetryRequestOverride = () => { return requestInstance; }; - const res = util.makeRequest(reqOpts, {}, assert.ifError); - assert.strictEqual(res, requestInstance); + const res = util.makeRequest(reqOpts, {}, (err: any) => {}); + expect(res).toBe(requestInstance); }); it('should let handleResp handle the response', done => { const error = new Error('Error.'); const body = fakeResponse.body; - retryRequestOverride = ( + mockRetryRequestOverride = ( rOpts: DecorateRequestOptions, opts: MakeRequestConfig, callback: r.RequestCallback, @@ -1660,13 +1925,19 @@ describe('common/util', () => { }; stub('handleResp', (err, resp, body_) => { - assert.strictEqual(err, error); - assert.strictEqual(resp, fakeResponse); - assert.strictEqual(body_, body); - done(); + try { + expect(err).toBe(error); + expect(resp).toBe(fakeResponse); + expect(body_).toBe(body); + done(); + } catch (e) { + done(e); + } }); - util.makeRequest(fakeReqOpts, {}, assert.ifError); + util.makeRequest(fakeReqOpts, {}, (err: any) => { + if (err) done(err); + }); }); }); }); @@ -1681,7 +1952,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.autoPaginate, undefined); + expect(decoratedReqOpts.autoPaginate).toBeUndefined(); }); it('should delete qs.autoPaginateVal', () => { @@ -1692,7 +1963,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.autoPaginateVal, undefined); + expect(decoratedReqOpts.autoPaginateVal).toBeUndefined(); }); it('should delete objectMode', () => { @@ -1703,7 +1974,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.objectMode, undefined); + expect(decoratedReqOpts.objectMode).toBeUndefined(); }); it('should delete qs.autoPaginate', () => { @@ -1716,7 +1987,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.qs.autoPaginate, undefined); + expect(decoratedReqOpts.qs.autoPaginate).toBeUndefined(); }); it('should delete qs.autoPaginateVal', () => { @@ -1729,7 +2000,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.qs.autoPaginateVal, undefined); + expect(decoratedReqOpts.qs.autoPaginateVal).toBeUndefined(); }); it('should delete json.autoPaginate', () => { @@ -1742,7 +2013,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.json.autoPaginate, undefined); + expect(decoratedReqOpts.json.autoPaginate).toBeUndefined(); }); it('should delete json.autoPaginateVal', () => { @@ -1755,7 +2026,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.json.autoPaginateVal, undefined); + expect(decoratedReqOpts.json.autoPaginateVal).toBeUndefined(); }); it('should replace project ID tokens for qs object', () => { @@ -1766,17 +2037,17 @@ describe('common/util', () => { }; const decoratedQs = {}; - replaceProjectIdTokenOverride = (qs: {}, projectId_: string) => { + mockReplaceProjectIdTokenOverride = (qs: {}, projectId_: string) => { if (qs === reqOpts.uri) { return; } - assert.deepStrictEqual(qs, reqOpts.qs); - assert.strictEqual(projectId_, projectId); + expect(qs).toEqual(reqOpts.qs); + expect(projectId_).toBe(projectId); return decoratedQs; }; const decoratedRequest = util.decorateRequest(reqOpts, projectId); - assert.deepStrictEqual(decoratedRequest.qs, decoratedQs); + expect(decoratedRequest.qs).toEqual(decoratedQs); }); it('should replace project ID tokens for multipart array', () => { @@ -1792,17 +2063,17 @@ describe('common/util', () => { }; const decoratedPart = {}; - replaceProjectIdTokenOverride = (part: {}, projectId_: string) => { + mockReplaceProjectIdTokenOverride = (part: {}, projectId_: string) => { if (part === reqOpts.uri) { return; } - assert.deepStrictEqual(part, reqOpts.multipart[0]); - assert.strictEqual(projectId_, projectId); + expect(part).toEqual(reqOpts.multipart[0]); + expect(projectId_).toBe(projectId); return decoratedPart; }; const decoratedRequest = util.decorateRequest(reqOpts, projectId); - assert.deepStrictEqual(decoratedRequest.multipart, [decoratedPart]); + expect(decoratedRequest.multipart).toEqual([decoratedPart]); }); it('should replace project ID tokens for json object', () => { @@ -1813,17 +2084,17 @@ describe('common/util', () => { }; const decoratedJson = {}; - replaceProjectIdTokenOverride = (json: {}, projectId_: string) => { + mockReplaceProjectIdTokenOverride = (json: {}, projectId_: string) => { if (json === reqOpts.uri) { return; } - assert.strictEqual(reqOpts.json, json); - assert.strictEqual(projectId_, projectId); + expect(reqOpts.json).toBe(json); + expect(projectId_).toBe(projectId); return decoratedJson; }; const decoratedRequest = util.decorateRequest(reqOpts, projectId); - assert.deepStrictEqual(decoratedRequest.json, decoratedJson); + expect(decoratedRequest.json).toEqual(decoratedJson); }); it('should decorate the request', () => { @@ -1833,13 +2104,13 @@ describe('common/util', () => { }; const decoratedUri = 'http://decorated'; - replaceProjectIdTokenOverride = (uri: string, projectId_: string) => { - assert.strictEqual(uri, reqOpts.uri); - assert.strictEqual(projectId_, projectId); + mockReplaceProjectIdTokenOverride = (uri: string, projectId_: string) => { + expect(uri).toBe(reqOpts.uri); + expect(projectId_).toBe(projectId); return decoratedUri; }; - assert.deepStrictEqual(util.decorateRequest(reqOpts, projectId), { + expect(util.decorateRequest(reqOpts, projectId)).toEqual({ uri: decoratedUri, }); }); @@ -1861,33 +2132,33 @@ describe('common/util', () => { describe('Service objects', () => { it('should match by constructor name', () => { - assert(util.isCustomType(pubsub, 'pubsub')); + expect(util.isCustomType(pubsub, 'pubsub')).toBe(true); }); it('should support any casing', () => { - assert(util.isCustomType(pubsub, 'PubSub')); + expect(util.isCustomType(pubsub, 'PubSub')).toBe(true); }); it('should not match if the wrong Service', () => { - assert(!util.isCustomType(subscription, 'BigQuery')); + expect(util.isCustomType(subscription, 'BigQuery')).toBe(false); }); }); describe('ServiceObject objects', () => { it('should match by constructor names', () => { - assert(util.isCustomType(subscription, 'pubsub')); - assert(util.isCustomType(subscription, 'pubsub/subscription')); + expect(util.isCustomType(subscription, 'pubsub')).toBe(true); + expect(util.isCustomType(subscription, 'pubsub/subscription')).toBe(true); - assert(util.isCustomType(subscription, 'middlelayer')); - assert(util.isCustomType(subscription, 'middlelayer/subscription')); + expect(util.isCustomType(subscription, 'middlelayer')).toBe(true); + expect(util.isCustomType(subscription, 'middlelayer/subscription')).toBe(true); }); it('should support any casing', () => { - assert(util.isCustomType(subscription, 'PubSub/Subscription')); + expect(util.isCustomType(subscription, 'PubSub/Subscription')).toBe(true); }); it('should not match if the wrong ServiceObject', () => { - assert(!util.isCustomType(subscription, 'pubsub/topic')); + expect(util.isCustomType(subscription, 'pubsub/topic')).toBe(false); }); }); }); @@ -1899,7 +2170,7 @@ describe('common/util', () => { version: '0.1.0', }); - assert.strictEqual(userAgent, 'gcloud-node-storage/0.1.0'); + expect(userAgent).toBe('gcloud-node-storage/0.1.0'); }); }); @@ -1907,8 +2178,8 @@ describe('common/util', () => { it('should allow passing just a callback', () => { const optionsOrCallback = () => {}; const [opts, cb] = util.maybeOptionsOrCallback(optionsOrCallback); - assert.strictEqual(optionsOrCallback, cb); - assert.deepStrictEqual(opts, {}); + expect(cb).toBe(optionsOrCallback); + expect(opts).toEqual({}); }); it('should allow passing both opts and callback', () => { @@ -1918,8 +2189,8 @@ describe('common/util', () => { optionsOrCallback, callback, ); - assert.strictEqual(opts, optionsOrCallback); - assert.strictEqual(cb, callback); + expect(opts).toBe(optionsOrCallback); + expect(cb).toBe(callback); }); }); });