From 1b91fa1b7986f87d4eb46e49177c44159e9b902a Mon Sep 17 00:00:00 2001 From: Gabe Pearhill Date: Wed, 24 Jun 2026 11:38:09 -0700 Subject: [PATCH 1/3] chore: migrate core/common to jest --- core/common/.gitignore | 1 + core/common/.nycrc | 24 - core/common/{.mocharc.js => jest.config.js} | 31 +- core/common/package.json | 15 +- core/common/system-test/common.ts | 244 ++++++- core/common/system-test/install.ts | 7 +- core/common/test/index.ts | 10 +- core/common/test/operation.ts | 119 ++-- core/common/test/service-object.ts | 487 ++++++-------- core/common/test/service.ts | 235 ++++--- core/common/test/util.ts | 674 ++++++++++---------- 11 files changed, 945 insertions(+), 902 deletions(-) delete mode 100644 core/common/.nycrc rename core/common/{.mocharc.js => jest.config.js} (52%) diff --git a/core/common/.gitignore b/core/common/.gitignore index d11ca259021d..50bf4bc22627 100644 --- a/core/common/.gitignore +++ b/core/common/.gitignore @@ -12,3 +12,4 @@ build/ .vscode package-lock.json __pycache__ +coverage/ 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..29b1244206d2 100644 --- a/core/common/.mocharc.js +++ b/core/common/jest.config.js @@ -4,26 +4,23 @@ // 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 = { + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.json', + }, + ], + }, + clearMocks: true, + testMatch: ['/test/**/*.ts', '/system-test/*.ts'], +}; diff --git a/core/common/package.json b/core/common/package.json index 0a4adb7f6b65..a12ae2bbcd82 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 test --runInBand --forceExit", "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 system-test --runInBand --forceExit", "samples-test": "cd samples/ && npm link ../ && npm test && cd ../", "prelint": "cd samples; npm link ../; npm install", "clean": "gts clean", @@ -47,27 +47,22 @@ "devDependencies": { "@types/ent": "^2.2.8", "@types/extend": "^3.0.4", - "@types/mocha": "^10.0.10", + "@types/jest": "^29.5.12", "@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": "^29.7.0", "jsdoc": "^4.0.4", "jsdoc-fresh": "^3.0.0", "jsdoc-region-tag": "^3.0.0", - "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.1.2", "typescript": "^5.8.2" }, "homepage": "https://github.com/googleapis/google-cloud-node/tree/main/core/common" diff --git a/core/common/system-test/common.ts b/core/common/system-test/common.ts index 9f12f9280a2c..62b222db4e8d 100644 --- a/core/common/system-test/common.ts +++ b/core/common/system-test/common.ts @@ -12,10 +12,181 @@ // 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'; +jest.mock('gaxios', () => { + const gaxiosRequest = async (opts: any) => { + const url = opts.url || opts.uri; + const method = opts.method || 'GET'; + const headers = opts.headers || {}; + let body = opts.data || opts.body; + + if (body && typeof body === 'object' && !(body instanceof ArrayBuffer || body instanceof Blob)) { + if (body.toString() === '[object URLSearchParams]') { + body = body.toString(); + } else { + body = JSON.stringify(body); + } + } + + const res = await globalThis.fetch(url, { + method, + headers, + body: method !== 'GET' && method !== 'HEAD' ? body : undefined, + }); + + const contentType = res.headers.get('content-type') || ''; + let data; + if (contentType.includes('application/json')) { + try { + data = await res.json(); + } catch { + data = await res.text(); + } + } else { + data = await res.text(); + } + + const resHeaders: Record = {}; + res.headers.forEach((val, key) => { + resHeaders[key] = val; + }); + + return { + data, + status: res.status, + statusText: res.statusText, + headers: resHeaders, + config: opts, + }; + }; + + class HttpInterceptorManager { + handlers: any[] = []; + add(interceptor: any) { + this.handlers.push(interceptor); + return this.handlers.length - 1; + } + eject(id: number) { + this.handlers[id] = null; + } + } + + class Gaxios { + interceptors = { + request: new HttpInterceptorManager(), + response: new HttpInterceptorManager(), + }; + static mergeHeaders(first: any, second: any) { + const merged: Record = {}; + const addHeader = (key: string, val: any) => { + merged[key.toLowerCase()] = val; + }; + const process = (hdrs: any) => { + if (!hdrs) return; + if (hdrs instanceof globalThis.Headers) { + hdrs.forEach((val, key) => addHeader(key, val)); + } else if (typeof hdrs === 'object') { + for (const [key, val] of Object.entries(hdrs)) { + addHeader(key, val); + } + } + }; + process(first); + process(second); + return merged; + } + async request(opts: any) { + let currentOpts = { ...opts }; + for (const handler of this.interceptors.request.handlers) { + if (handler && handler.fulfilled) { + currentOpts = await handler.fulfilled(currentOpts); + } + } + let res = await gaxiosRequest(currentOpts); + for (const handler of this.interceptors.response.handlers) { + if (handler && handler.fulfilled) { + res = await handler.fulfilled(res); + } + } + return res; + } + } + + return { + Gaxios, + request: gaxiosRequest, + gaxios: new Gaxios(), + }; +}); + +jest.mock('teeny-request', () => { + const teenyRequest = (opts: any, callback?: any) => { + let url = opts.uri || opts.url; + if (opts.qs) { + const searchParams = new URLSearchParams(opts.qs); + url += '?' + searchParams.toString(); + } + + const method = opts.method || 'GET'; + const headers = opts.headers || {}; + let body = opts.body || opts.json; + + if (opts.json && typeof opts.json === 'object') { + headers['content-type'] = headers['content-type'] || 'application/json'; + body = JSON.stringify(opts.json); + } + + globalThis.fetch(url, { + method, + headers, + body: method !== 'GET' && method !== 'HEAD' ? body : undefined, + }) + .then(async res => { + const contentType = res.headers.get('content-type') || ''; + let body; + if (contentType.includes('application/json')) { + try { + body = await res.json(); + } catch { + body = await res.text(); + } + } else { + body = await res.text(); + } + + const resHeaders: Record = {}; + res.headers.forEach((val, key) => { + resHeaders[key] = val; + }); + + const response = { + statusCode: res.status, + headers: resHeaders, + body, + request: { uri: url }, + }; + + if (res.status >= 200 && res.status < 300) { + callback(null, response, body); + } else { + const err = new Error(res.statusText) as any; + err.code = res.status; + callback(err, response, body); + } + }) + .catch(err => { + callback(err); + }); + }; + + teenyRequest.defaults = () => teenyRequest; + + return { + teenyRequest, + }; +}); + import * as common from '../src'; describe('Common', () => { @@ -23,40 +194,47 @@ describe('Common', () => { const MOCK_HOST = `http://localhost:${MOCK_HOST_PORT}`; describe('Service', () => { - let service: common.Service; - - before(() => { - service = new common.Service({ - baseUrl: MOCK_HOST, - apiEndpoint: MOCK_HOST, + function createService(port: number) { + const host = `http://localhost:${port}`; + return new common.Service({ + baseUrl: host, + apiEndpoint: host, scopes: [], packageJson: {name: 'tests', version: '1.0.0'}, + }, { + projectId: 'fake-project', }); - }); + } it('should send a request and receive a response', done => { + const port = 8118; + const service = createService(port); const mockResponse = 'response'; const mockServer = new http.Server((req, res) => { res.end(mockResponse); }); - mockServer.listen(MOCK_HOST_PORT); + mockServer.listen(port); 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); + mockServer.close(done); + } catch (e) { + mockServer.close(() => done(e)); + } }, ); }); it('should retry a request', function (done) { - this.timeout(60 * 1000); - + const port = 8119; + const service = createService(port); let numRequestAttempts = 0; const mockServer = new http.Server((req, res) => { @@ -65,23 +243,27 @@ describe('Common', () => { res.end(); }); - mockServer.listen(MOCK_HOST_PORT); + mockServer.listen(port); service.request( { uri: '/mock-endpoint-retry', }, err => { - assert.strictEqual((err! as common.ApiError).code, 408); - assert.strictEqual(numRequestAttempts, 4); - mockServer.close(done); + try { + expect((err! as common.ApiError).code).toBe(408); + expect(numRequestAttempts).toBe(4); + mockServer.close(done); + } catch (e) { + mockServer.close(() => done(e)); + } }, ); - }); + }, 60000); it('should retry non-responsive hosts', function (done) { - this.timeout(60 * 1000); - + const port = 8120; + const service = createService(port); function getMinimumRetryDelay(retryNumber: number) { return Math.pow(2, retryNumber) * 1000; } @@ -100,12 +282,20 @@ describe('Common', () => { uri: '/mock-endpoint-no-response', }, err => { - assert(err?.message.includes('ECONNREFUSED')); - const timeResponse = Date.now(); - assert(timeResponse - timeRequest > minExpectedResponseTime); - done(); + try { + const errCode = (err as any)?.code || (err as any)?.errno || (err as any)?.cause?.code; + expect( + errCode === 'ECONNREFUSED' || + err?.message.includes('ECONNREFUSED') + ).toBeTruthy(); + const timeResponse = Date.now(); + expect(timeResponse - timeRequest > minExpectedResponseTime).toBeTruthy(); + done(); + } catch (e) { + done(e); + } }, ); - }); + }, 60000); }); }); diff --git a/core/common/system-test/install.ts b/core/common/system-test/install.ts index 487c01cffeae..4b0991bc6b64 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'; @@ -62,12 +61,12 @@ describe('install tests', () => { await mvp(tarball, `${stagingPath}/${pkgName}.tgz`); await ncpp('system-test/fixtures/kitchen', `${stagingPath}/`); await spawnp(npm, ['install'], {cwd: `${stagingPath}/`}); - }).timeout(120000); + }, 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..0bda9fcc5e41 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).toBeDefined(); + expect(Service).toBeDefined(); + expect(ServiceObject).toBeDefined(); + expect(util).toBeDefined(); }); }); diff --git a/core/common/test/operation.ts b/core/common/test/operation.ts index e8f598b74b83..0581e0041d37 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,13 @@ 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 +41,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 +64,19 @@ 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 +95,7 @@ describe('Operation', () => { throw new Error('Promise should have been rejected.'); }, (err: Error) => { - assert.strictEqual(err, error); + expect(err).toBe(error); }, ); }); @@ -113,7 +108,7 @@ describe('Operation', () => { }); return operation.promise().then(data => { - assert.deepStrictEqual(data, [metadata]); + expect(data).toEqual([metadata]); }); }); }); @@ -129,11 +124,11 @@ describe('Operation', () => { }); 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,32 +136,34 @@ 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 => { + it('should call getMetadata', done => { asAny(operation).getMetadata = () => done(); - asAny(operation).poll_(assert.ifError); + asAny(operation).poll_((err: Error) => { + expect(err).toBeNull(); + }); }); 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); + expect(err).toBe(error); done(); }); }); @@ -175,11 +172,9 @@ 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); + expect(err).toBe(apiResponse.error); done(); }); }); @@ -189,14 +184,12 @@ 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); + expect(resp).toBeUndefined(); done(); }); }); @@ -205,14 +198,12 @@ 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); + expect(resp).toBe(apiResponse); done(); }); }); @@ -221,32 +212,32 @@ 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); // if called, test will fail. asAny(operation).startPolling_(); done(); }); 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); + expect(err).toBe(error); done(); }); asAny(operation).startPolling_(); @@ -257,22 +248,20 @@ 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: any) => { fn(); // should call startPolling_ - assert.strictEqual(timeoutMs, 500); - return asAny({}); + expect(timeoutMs).toBe(500); + return {} as any; }); - asAny(operation).startPolling_ = function () { + asAny(operation).startPolling_ = function (this: any) { if (!startPollingCalled) { // Call #1. startPollingCalled = true; @@ -281,7 +270,7 @@ describe('Operation', () => { return; } // This is from the setTimeout call. - assert.strictEqual(this, operation); + expect(this).toBe(operation); done(); }; @@ -296,17 +285,17 @@ 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: any) => { fn(); // should call startPolling_ - assert.strictEqual(timeoutMs, 2000); - return asAny({}); + expect(timeoutMs).toBe(2000); + return {} as any; }); - asAny(op).startPolling_ = function () { + asAny(op).startPolling_ = function (this: any) { if (!startPollingCalled) { // Call #1. startPollingCalled = true; @@ -315,7 +304,7 @@ describe('Operation', () => { return; } // This is from the setTimeout call. - assert.strictEqual(this, op); + expect(this).toBe(op); done(); }; @@ -327,14 +316,12 @@ 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); + expect(metadata).toBe(apiResponse); }); await asAny(operation).startPolling_(); }); diff --git a/core/common/test/service-object.ts b/core/common/test/service-object.ts index 1d3ed35a5fee..dfb63e95ea2f 100644 --- a/core/common/test/service-object.ts +++ b/core/common/test/service-object.ts @@ -1,3 +1,4 @@ +import * as assert from 'assert'; // Copyright 2015 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,31 +18,28 @@ 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; +let mockPromisified = false; +jest.mock('@google-cloud/promisify', () => { + const actual = jest.requireActual('@google-cloud/promisify'); + return { + ...actual, + promisifyAll(Class: Function, options: PromisifyAllOptions) { + if (Class.name === 'ServiceObject') { + mockPromisified = true; + expect(options.exclude).toEqual(['getRequestInterceptors']); + } + return actual.promisifyAll(Class, options); + }, + }; +}); + +import {ServiceObject} from '../src/service-object'; import { ApiError, @@ -68,8 +66,7 @@ function asInternal(serviceObject: SO.ServiceObject) { describe('ServiceObject', () => { let serviceObject: SO.ServiceObject; - const sandbox = sinon.createSandbox(); - + const CONFIG = { baseUrl: 'base-url', parent: {} as Service, @@ -83,46 +80,43 @@ describe('ServiceObject', () => { }); afterEach(() => { - sandbox.restore(); + jest.restoreAllMocks(); }); describe('instantiation', () => { it('should promisify all the things', () => { - assert(promisified); + expect(mockPromisified).toBeTruthy(); }); 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).toEqual(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).toBe(undefined); }); 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,8 +157,8 @@ describe('ServiceObject', () => { options_: {}, callback: (err: Error | null, a: {}, b: {}) => void, ) { - assert.strictEqual(id, config.id); - assert.strictEqual(options_, options); + expect(id).toBe(config.id); + expect(options_).toBe(options); callback(null, {}, {}); // calls done() } @@ -186,8 +177,8 @@ describe('ServiceObject', () => { options_: {}, callback: (err: Error | null, a: {}, b: {}) => void, ) { - assert.strictEqual(id, config.id); - assert.strictEqual(options_, options); + expect(id).toBe(config.id); + expect(options_).toBe(options); callback(null, {metadata: {id: 14}}, {}); } @@ -203,9 +194,9 @@ describe('ServiceObject', () => { }); function createMethod(id: string, options: Function, callback: Function) { - assert.strictEqual(id, config.id); - assert.strictEqual(typeof options, 'function'); - assert.strictEqual(callback, undefined); + expect(id).toBe(config.id); + expect(typeof options).toBe('function'); + expect(callback).toBe(undefined); options(null, {}, {}); // calls done() } @@ -226,9 +217,9 @@ describe('ServiceObject', () => { serviceObject.create( options, (err: Error | null, instance: {}, apiResponse_: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(instance, null); - assert.strictEqual(apiResponse_, apiResponse); + expect(err).toBe(error); + expect(instance).toBe(null); + expect(apiResponse_).toBe(apiResponse); done(); }, ); @@ -246,8 +237,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 +254,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,7 +272,7 @@ 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); + expect([].slice.call(args)).toEqual(args); done(); }); }); @@ -289,14 +280,11 @@ describe('ServiceObject', () => { 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, ''); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts: any, callback: any) => { + expect((reqOpts as DecorateRequestOptions).method).toBe('DELETE',); + expect((reqOpts as DecorateRequestOptions).uri).toBe(''); done(); (callback as any)(null, null, {} as r.Response); }); @@ -305,13 +293,10 @@ describe('ServiceObject', () => { it('should accept options', done => { const options = {queryOptionProperty: true}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts, callback) => { - assert.deepStrictEqual( - (reqOpts as DecorateRequestOptions).qs, - options, - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts: any, callback: any) => { + expect((reqOpts as DecorateRequestOptions).qs).toEqual(options,); done(); (callback as any)(null, null, {} as r.Response); }); @@ -328,21 +313,12 @@ 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', - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts_: any, callback: any) => { + expect(serviceObject.methods.delete).toEqual(cachedMethodConfig,); + expect((reqOpts_ as DecorateRequestOptions).uri).toEqual('v2',); + expect((reqOpts_ as DecorateRequestOptions).method).toEqual('PATCH',); done(); (callback as any)(null, null, null!); }); @@ -355,10 +331,10 @@ describe('ServiceObject', () => { it('should respect ignoreNotFound opion', 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((_0: any, cb: any) => cb(error)); serviceObject.delete(options, (err, apiResponse_) => { - assert.ifError(err); - assert.strictEqual(apiResponse_, undefined); + expect(err).toBeFalsy(); + expect(apiResponse_).toBe(undefined); done(); }); }); @@ -366,23 +342,20 @@ describe('ServiceObject', () => { it('should propagate other then 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((_0: any, cb: any) => cb(error)); serviceObject.delete(options, (err, apiResponse_) => { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, undefined); + expect(err).toBe(error); + expect(apiResponse_).toBe(undefined); done(); }); }); 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, - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts: any, callback: any) => { + expect((reqOpts as DecorateRequestOptions).qs.ignoreNotFound).toBe(undefined,); done(); (callback as any)(null, null, {} as r.Response); }); @@ -401,13 +374,10 @@ describe('ServiceObject', () => { const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.delete, - cachedMethodConfig, - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts_: any, callback: any) => { + expect(serviceObject.methods.delete).toEqual(cachedMethodConfig,); assert.deepStrictEqual((reqOpts_ as DecorateRequestOptions).qs, { defaultProperty: true, optionalProperty: true, @@ -426,9 +396,9 @@ describe('ServiceObject', () => { }); it('should not require a callback', () => { - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, null, null, {}); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((_0: any, cb: any) => cb(null, null, {})); assert.doesNotThrow(() => { void serviceObject.delete(); }); @@ -436,11 +406,11 @@ describe('ServiceObject', () => { 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((_0: any, cb: any) => cb(error)); const serviceObject = new ServiceObject(CONFIG); serviceObject.delete((err: Error, apiResponse_: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, undefined); + expect(err).toBe(error); + expect(apiResponse_).toBe(undefined); done(); }); }); @@ -448,16 +418,16 @@ describe('ServiceObject', () => { describe('exists', () => { it('should call get', done => { - sandbox.stub(serviceObject, 'get').callsFake(() => done()); + jest.spyOn(serviceObject, 'get').mockImplementation(() => done()); void serviceObject.exists(() => {}); }); it('should accept options', done => { const options = {queryOptionProperty: true}; - sandbox - .stub(ServiceObject.prototype, 'get') - .callsFake((options_, callback) => { - assert.deepStrictEqual(options_, options); + jest + .spyOn(ServiceObject.prototype, 'get') + .mockImplementation((options_: any, callback: any) => { + expect(options_).toEqual(options); done(); (callback as any)(null, null, {} as r.Response); }); @@ -467,10 +437,10 @@ describe('ServiceObject', () => { 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((_0: any, cb: any) => cb(error)); void serviceObject.exists((err: Error, exists: boolean) => { - assert.ifError(err); - assert.strictEqual(exists, false); + expect(err).toBeFalsy(); + expect(exists).toBe(false); done(); }); }); @@ -478,19 +448,19 @@ describe('ServiceObject', () => { 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((_0: any, cb: any) => cb(error)); void serviceObject.exists((err: Error, exists: boolean) => { - assert.strictEqual(err, error); - assert.strictEqual(exists, undefined); + expect(err).toBe(error); + expect(exists).toBe(undefined); done(); }); }); it('should execute callback with true if no error', done => { - sandbox.stub(serviceObject, 'get').callsArgWith(1, null); + jest.spyOn(serviceObject, 'get').mockImplementation((_0: any, cb: any) => cb(null)); void serviceObject.exists((err: Error, exists: boolean) => { - assert.ifError(err); - assert.strictEqual(exists, true); + expect(err).toBeFalsy(); + expect(exists).toBe(true); done(); }); }); @@ -509,7 +479,7 @@ describe('ServiceObject', () => { const options = {}; serviceObject.getMetadata = promisify( (options_: SO.GetMetadataOptions): void => { - assert.deepStrictEqual(options, options_); + expect(options).toEqual(options_); done(); }, ); @@ -534,9 +504,9 @@ describe('ServiceObject', () => { ); serviceObject.get((err, instance, metadata_) => { - assert.strictEqual(err, error); - assert.strictEqual(instance, null); - assert.strictEqual(metadata_, metadata); + expect(err).toBe(error); + expect(instance).toBe(null); + expect(metadata_).toBe(metadata); done(); }); @@ -552,10 +522,10 @@ describe('ServiceObject', () => { ); serviceObject.get((err, instance, metadata_) => { - assert.ifError(err); + expect(err).toBeFalsy(); - assert.strictEqual(instance, serviceObject); - assert.strictEqual(metadata_, metadata); + expect(instance).toBe(serviceObject); + expect(metadata_).toBe(metadata); done(); }); @@ -583,14 +553,14 @@ 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); + expect(err).toBe(ERROR); done(); }); }); @@ -599,15 +569,15 @@ 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); + jest.spyOn(serviceObject, 'create').mockImplementation(config_ => { + expect(config_).toEqual(expectedConfig); done(); }); 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((cb: any) => cb(null)); serviceObject.get(AUTO_CREATE_CONFIG, done); }); @@ -617,11 +587,11 @@ describe('ServiceObject', () => { const apiResponse = {} as r.Response; // eslint-disable-next-line @typescript-eslint/no-explicit-any - (sandbox.stub(serviceObject, 'create') as any).callsFake( + (jest.spyOn(serviceObject, 'create') as any).mockImplementation( (optsOrCb: {}, cb: Function) => { const callback = typeof optsOrCb === 'function' ? optsOrCb : cb; - sandbox.stub(serviceObject, 'get').callsFake((cfg, callback) => { - assert.deepStrictEqual(cfg, {}); + jest.spyOn(serviceObject, 'get').mockImplementation((cfg: any, callback: any) => { + expect(cfg).toEqual({}); callback!(null); // done() }); callback!(error, null, apiResponse); @@ -629,9 +599,9 @@ describe('ServiceObject', () => { ); serviceObject.get(AUTO_CREATE_CONFIG, (err, instance, resp) => { - assert.strictEqual(err, error); - assert.strictEqual(instance, null); - assert.strictEqual(resp, apiResponse); + expect(err).toBe(error); + expect(instance).toBe(null); + expect(resp).toBe(apiResponse); done(); }); }); @@ -639,11 +609,11 @@ describe('ServiceObject', () => { 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 => { + 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, {}); + expect(config).toEqual({}); callback!(null, null, {} as r.Response); // done() }); callback(error, null, undefined); @@ -656,13 +626,13 @@ 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, ) { - assert.strictEqual(this, serviceObject); - assert.strictEqual((reqOpts as DecorateRequestOptions).uri, ''); + expect(this).toBe(serviceObject); + expect((reqOpts as DecorateRequestOptions).uri).toBe(''); done(); (callback as any)(null, null, {} as r.Response); }); @@ -671,13 +641,10 @@ describe('ServiceObject', () => { it('should accept options', done => { const options = {queryOptionProperty: true}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts, callback) => { - assert.deepStrictEqual( - (reqOpts as DecorateRequestOptions).qs, - options, - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts: any, callback: any) => { + expect((reqOpts as DecorateRequestOptions).qs).toEqual(options,); done(); (callback as any)(null, null, {} as r.Response); }); @@ -693,17 +660,11 @@ 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', - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts_: any, callback: any) => { + expect(serviceObject.methods.getMetadata).toEqual(cachedMethodConfig,); + expect((reqOpts_ as DecorateRequestOptions).uri).toEqual('v2',); done(); (callback as any)(null, null, null!); }); @@ -725,13 +686,10 @@ describe('ServiceObject', () => { const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.getMetadata, - cachedMethodConfig, - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts_: any, callback: any) => { + expect(serviceObject.methods.getMetadata).toEqual(cachedMethodConfig,); assert.deepStrictEqual((reqOpts_ as DecorateRequestOptions).qs, { defaultProperty: true, optionalProperty: true, @@ -751,22 +709,22 @@ 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((_0: any, cb: any) => cb(error)); void serviceObject.getMetadata((err: Error, metadata: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(metadata, undefined); + expect(err).toBe(error); + expect(metadata).toBe(undefined); done(); }); }); it('should update metadata', done => { const apiResponse = {}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, null, {}, apiResponse); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((_0: any, cb: any) => cb(null, {}, apiResponse)); void serviceObject.getMetadata((err: Error) => { - assert.ifError(err); - assert.deepStrictEqual(serviceObject.metadata, apiResponse); + expect(err).toBeFalsy(); + expect(serviceObject.metadata).toEqual(apiResponse); done(); }); }); @@ -774,12 +732,12 @@ describe('ServiceObject', () => { 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((_0: any, cb: any) => cb(null, apiResponse, requestResponse)); void serviceObject.getMetadata((err: Error, metadata: {}) => { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); + expect(err).toBeFalsy(); + expect(metadata).toBe(apiResponse); done(); }); }); @@ -830,7 +788,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 +808,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,18 +832,15 @@ 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, ) { - assert.strictEqual(this, serviceObject); - assert.strictEqual((reqOpts as DecorateRequestOptions).method, 'PATCH'); - assert.strictEqual((reqOpts as DecorateRequestOptions).uri, ''); - assert.deepStrictEqual( - (reqOpts as DecorateRequestOptions).json, - metadata, - ); + expect(this).toBe(serviceObject); + expect((reqOpts as DecorateRequestOptions).method).toBe('PATCH'); + expect((reqOpts as DecorateRequestOptions).uri).toBe(''); + expect((reqOpts as DecorateRequestOptions).json).toEqual(metadata,); done(); (callback as any)(null, null, {} as r.Response); }); @@ -901,13 +850,10 @@ describe('ServiceObject', () => { 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, - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts: any, callback: any) => { + expect((reqOpts as DecorateRequestOptions).qs).toEqual(options,); done(); (callback as any)(null, null, {} as r.Response); }); @@ -923,21 +869,12 @@ 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', - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts_: any, callback: any) => { + expect(serviceObject.methods.setMetadata).toEqual(cachedMethodConfig,); + expect((reqOpts_ as DecorateRequestOptions).uri).toEqual('v2',); + expect((reqOpts_ as DecorateRequestOptions).method).toEqual('PUT',); done(); (callback as any)(null, null, null!); }); @@ -958,13 +895,10 @@ describe('ServiceObject', () => { }; const cachedMethodConfig = extend(true, {}, methodConfig); - sandbox - .stub(ServiceObject.prototype, 'request') - .callsFake((reqOpts_, callback) => { - assert.deepStrictEqual( - serviceObject.methods.setMetadata, - cachedMethodConfig, - ); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((reqOpts_: any, callback: any) => { + expect(serviceObject.methods.setMetadata).toEqual(cachedMethodConfig,); assert.deepStrictEqual((reqOpts_ as DecorateRequestOptions).qs, { defaultProperty: true, optionalProperty: true, @@ -987,22 +921,22 @@ 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((_0: any, cb: any) => cb(error)); void serviceObject.setMetadata({}, (err: Error, apiResponse_: {}) => { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, undefined); + expect(err).toBe(error); + expect(apiResponse_).toBe(undefined); done(); }); }); it('should update metadata', done => { const apiResponse = {}; - sandbox - .stub(ServiceObject.prototype, 'request') - .callsArgWith(1, undefined, apiResponse); + jest + .spyOn(ServiceObject.prototype, 'request') + .mockImplementation((_0: any, cb: any) => cb(undefined, apiResponse)); void serviceObject.setMetadata({}, (err: Error) => { - assert.ifError(err); - assert.strictEqual(serviceObject.metadata, apiResponse); + expect(err).toBeFalsy(); + expect(serviceObject.metadata).toBe(apiResponse); done(); }); }); @@ -1010,12 +944,12 @@ describe('ServiceObject', () => { 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((_0: any, cb: any) => cb(null, body, apiResponse)); void serviceObject.setMetadata({}, (err: Error, metadata: {}) => { - assert.ifError(err); - assert.strictEqual(metadata, body); + expect(err).toBeFalsy(); + expect(metadata).toBe(body); done(); }); }); @@ -1038,8 +972,8 @@ describe('ServiceObject', () => { serviceObject.parent.request = (reqOpts_, callback) => { assert.notStrictEqual(reqOpts_, reqOpts); - assert.strictEqual(reqOpts_.uri, expectedUri); - assert.deepStrictEqual(reqOpts_.interceptors_, []); + expect(reqOpts_.uri).toBe(expectedUri); + expect(reqOpts_.interceptors_).toEqual([]); callback(null, null, {} as r.Response); }; asInternal(serviceObject).request_(reqOpts, () => done()); @@ -1048,7 +982,7 @@ describe('ServiceObject', () => { 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); + expect(reqOpts.uri).toBe(expectedUri); callback(null, null, {} as r.Response); }; serviceObject.id = undefined; @@ -1058,7 +992,7 @@ describe('ServiceObject', () => { it('should support absolute uris', done => { const expectedUri = 'http://www.google.com'; serviceObject.parent.request = (reqOpts, callback) => { - assert.strictEqual(reqOpts.uri, expectedUri); + expect(reqOpts.uri).toBe(expectedUri); callback(null, null, {} as r.Response); }; asInternal(serviceObject).request_({uri: expectedUri}, () => { @@ -1074,7 +1008,7 @@ 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); + expect(reqOpts_.uri).toBe(expectedUri); callback(null, null, {} as r.Response); }; asInternal(serviceObject).request_(reqOpts, () => done()); @@ -1088,7 +1022,7 @@ describe('ServiceObject', () => { '/', ); serviceObject.parent.request = (reqOpts_, callback) => { - assert.strictEqual(reqOpts_.uri, expectedUri); + expect(reqOpts_.uri).toBe(expectedUri); callback(null, null, {} as r.Response); }; asInternal(serviceObject).request_(reqOpts, () => { @@ -1117,9 +1051,9 @@ describe('ServiceObject', () => { }, }); - sandbox - .stub(parent.parent as SO.ServiceObject, 'request') - .callsFake((reqOpts, callback) => { + jest + .spyOn(parent.parent as SO.ServiceObject, 'request') + .mockImplementation((reqOpts: any, callback: any) => { assert.deepStrictEqual( reqOpts.interceptors_![0].request({} as DecorateRequestOptions), { @@ -1150,10 +1084,7 @@ describe('ServiceObject', () => { serviceObject.parent.request = (reqOpts, callback) => { const serviceObjectInterceptors = asInternal(serviceObject).interceptors; - assert.deepStrictEqual( - reqOpts.interceptors_, - serviceObjectInterceptors, - ); + expect(reqOpts.interceptors_).toEqual(serviceObjectInterceptors,); assert.notStrictEqual(reqOpts.interceptors_, serviceObjectInterceptors); callback(null, null, {} as r.Response); done(); @@ -1171,24 +1102,24 @@ describe('ServiceObject', () => { serviceObject.parent.requestStream = reqOpts_ => { assert.notStrictEqual(reqOpts_, reqOpts); - assert.strictEqual(reqOpts_.uri, expectedUri); - assert.deepStrictEqual(reqOpts_.interceptors_, []); + 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); }); await serviceObject.request(fakeOptions); @@ -1196,13 +1127,13 @@ describe('ServiceObject', () => { 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((_0: any, cb: any) => cb(null, response.body, response)); serviceObject.request({} as DecorateRequestOptions, (err, body, res) => { - assert.ifError(err); - assert.deepStrictEqual(res, response); - assert.deepStrictEqual(body, response.body); + expect(err).toBeFalsy(); + expect(res).toEqual(response); + expect(body).toEqual(response.body); done(); }); }); @@ -1213,13 +1144,13 @@ 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((_0: any, cb: any) => cb(err, response.body, response)); serviceObject.request({} as DecorateRequestOptions, (err, body, res) => { - assert(err instanceof Error); - assert.deepStrictEqual(res, response); - assert.deepStrictEqual(body, response.body); + expect(err instanceof Error).toBeTruthy(); + expect(res).toEqual(response); + expect(body).toEqual(response.body); done(); }); }); @@ -1230,7 +1161,7 @@ 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}); }; serviceObject.requestStream(fakeOptions); }); diff --git a/core/common/test/service.ts b/core/common/test/service.ts index 768e7db4e4a6..839c5b9c4d4f 100644 --- a/core/common/test/service.ts +++ b/core/common/test/service.ts @@ -1,3 +1,4 @@ +import * as assert from 'assert'; // Copyright 2015 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,10 +13,7 @@ // 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'; @@ -24,6 +22,7 @@ import { DEFAULT_PROJECT_ID_TOKEN, ServiceConfig, ServiceOptions, + Service, } from '../src/service'; import { BodyResponseCallback, @@ -34,8 +33,6 @@ import { Util, } from '../src/util'; -proxyquire.noPreserveCache(); - const fakeCfg = {} as ServiceConfig; const makeAuthRequestFactoryCache = util.makeAuthenticatedRequestFactory; @@ -58,9 +55,6 @@ util.makeAuthenticatedRequestFactory = function ( 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: [], @@ -110,13 +104,13 @@ describe('Service', () => { token: OPTIONS.token, }); - assert.deepStrictEqual(config, expectedConfig); + expect(config).toEqual(expectedConfig); return authenticatedRequest; }; const svc = new Service(CONFIG, OPTIONS); - assert.strictEqual(svc.makeAuthenticatedRequest, authenticatedRequest); + expect(svc.makeAuthenticatedRequest).toBe(authenticatedRequest); }); it('should localize the authClient', () => { @@ -127,12 +121,12 @@ describe('Service', () => { } as MakeAuthenticatedRequest; }; const service = new Service(CONFIG, OPTIONS); - assert.strictEqual(service.authClient, authClient); + expect(service.authClient).toBe(authClient); }); it('should localize the provided authClient', () => { const service = new Service(CONFIG, OPTIONS); - assert.strictEqual(service.authClient, OPTIONS.authClient); + expect(service.authClient).toBe(OPTIONS.authClient); }); describe('`AuthClient` support', () => { @@ -156,7 +150,7 @@ describe('Service', () => { // The custom `AuthClient` should be passed to `GoogleAuth` and used internally const client = await serviceObject.authClient.getClient(); - assert.strictEqual(client, authClient); + expect(client).toBe(authClient); }); it('should accept an `AuthClient` passed to options', async () => { @@ -166,27 +160,27 @@ describe('Service', () => { // The custom `AuthClient` should be passed to `GoogleAuth` and used internally const client = await 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).toBe(undefined); }); 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(service.timeout).toBe(timeout); }); it('should localize the getCredentials method', () => { @@ -201,11 +195,11 @@ describe('Service', () => { }; const service = new Service(CONFIG, OPTIONS); - assert.strictEqual(service.getCredentials, getCredentials); + expect((service as any).getCredentials).toBe(getCredentials); }); it('should default globalInterceptors to an empty array', () => { - assert.deepStrictEqual(service.globalInterceptors, []); + expect((service as any).globalInterceptors).toEqual([]); }); it('should preserve the original global interceptors', () => { @@ -213,33 +207,33 @@ describe('Service', () => { const options = extend({}, OPTIONS); options.interceptors_ = globalInterceptors; const service = new Service(fakeCfg, options); - assert.strictEqual(service.globalInterceptors, globalInterceptors); + expect((service as any).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((service as any).projectIdRequired).toBe(CONFIG.projectIdRequired); }); it('should default projectIdRequired to true', () => { const service = new Service(fakeCfg, OPTIONS); - assert.strictEqual(service.projectIdRequired, true); + expect((service as any).projectIdRequired).toBe(true); }); it('should disable forever agent for Cloud Function envs', () => { @@ -249,15 +243,15 @@ 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); }); }); describe('getRequestInterceptors', () => { it('should call the request interceptors in order', () => { // Called first. - service.globalInterceptors.push({ + (service as any).globalInterceptors.push({ request(reqOpts: {order: string}) { reqOpts.order = '1'; return reqOpts; @@ -273,7 +267,7 @@ describe('Service', () => { }); // Called second. - service.globalInterceptors.push({ + (service as any).globalInterceptors.push({ request(reqOpts: {order: string}) { reqOpts.order += '2'; return reqOpts; @@ -293,7 +287,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', () => { @@ -301,21 +295,18 @@ describe('Service', () => { return reqOpts; } - service.globalInterceptors = [{request}]; + (service as any).globalInterceptors = [{request}]; service.interceptors = [{request}]; const originalGlobalInterceptors = [].slice.call( - service.globalInterceptors, + (service as any).globalInterceptors, ); const originalLocalInterceptors = [].slice.call(service.interceptors); service.getRequestInterceptors(); - assert.deepStrictEqual( - service.globalInterceptors, - originalGlobalInterceptors, - ); - assert.deepStrictEqual(service.interceptors, originalLocalInterceptors); + expect((service as any).globalInterceptors).toEqual(originalGlobalInterceptors,); + expect(service.interceptors).toEqual(originalLocalInterceptors); }); it('should not call unrelated interceptors', () => { @@ -337,7 +328,7 @@ describe('Service', () => { describe('getProjectId', () => { it('should get the project ID from the auth client', done => { - service.authClient = { + (service as any).authClient = { getProjectId() { done(); }, @@ -349,14 +340,14 @@ describe('Service', () => { it('should return error from auth client', done => { const error = new Error('Error.'); - service.authClient = { + (service as any).authClient = { async getProjectId() { throw error; }, }; service.getProjectId((err: Error) => { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }); }); @@ -365,16 +356,16 @@ describe('Service', () => { const service = new Service(fakeCfg, {}); const projectId = 'detected-project-id'; - service.authClient = { + (service as any).authClient = { async getProjectId() { return projectId; }, }; - service.getProjectId((err: Error, projectId_: string) => { - assert.ifError(err); - assert.strictEqual(service.projectId, projectId); - assert.strictEqual(projectId_, projectId); + service.getProjectId((err: Error | null, projectId_?: string) => { + expect(err).toBeFalsy(); + expect(service.projectId).toBe(projectId); + expect(projectId_).toBe(projectId); done(); }); }); @@ -382,7 +373,7 @@ describe('Service', () => { 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 +388,27 @@ describe('Service', () => { it('should compose the correct request', done => { const expectedUri = [service.baseUrl, reqOpts.uri].join('/'); - service.makeAuthenticatedRequest = ( + (service as any).makeAuthenticatedRequest = ( reqOpts_: DecorateRequestOptions, callback: BodyResponseCallback, ) => { assert.notStrictEqual(reqOpts_, reqOpts); - assert.strictEqual(reqOpts_.uri, expectedUri); - assert.strictEqual(reqOpts.interceptors_, undefined); + expect(reqOpts_.uri).toBe(expectedUri); + expect(reqOpts.interceptors_).toBe(undefined); callback(null); // done() }; - service.request_(reqOpts, () => done()); + (service as any).request_(reqOpts, () => done()); }); it('should support absolute uris', done => { const expectedUri = 'http://www.google.com'; - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts.uri, expectedUri); + (service as any).makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { + expect(reqOpts.uri).toBe(expectedUri); done(); }; - service.request_({uri: expectedUri}, assert.ifError); + (service as any).request_({uri: expectedUri}, assert.ifError); }); it('should trim slashes', done => { @@ -427,12 +418,12 @@ describe('Service', () => { const expectedUri = [service.baseUrl, '1/2'].join('/'); - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.uri, expectedUri); + (service as any).makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { + expect(reqOpts_.uri).toBe(expectedUri); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should replace path/:subpath with path:subpath', done => { @@ -441,19 +432,19 @@ describe('Service', () => { }; const expectedUri = service.baseUrl + reqOpts.uri; - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.uri, expectedUri); + (service as any).makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { + expect(reqOpts_.uri).toBe(expectedUri); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should not set timeout', done => { - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.timeout, undefined); + (service as any).makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { + expect(reqOpts_.timeout).toBe(undefined); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should set reqOpt.timeout', done => { @@ -462,11 +453,11 @@ describe('Service', () => { const options = extend({}, OPTIONS, {timeout}); const service = new Service(config, options); - service.makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_.timeout, timeout); + (service as any).makeAuthenticatedRequest = (reqOpts_: DecorateRequestOptions) => { + expect(reqOpts_.timeout).toBe(timeout); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should add the User Agent', done => { @@ -475,16 +466,16 @@ describe('Service', () => { const getUserAgentFn = util.getUserAgentFromPackageJson; util.getUserAgentFromPackageJson = packageJson => { util.getUserAgentFromPackageJson = getUserAgentFn; - assert.strictEqual(packageJson, service.packageJson); + expect(packageJson).toBe(service.packageJson); return userAgent; }; - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts.headers!['User-Agent'], userAgent); + (service as any).makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { + expect(reqOpts.headers!['User-Agent']).toBe(userAgent); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should add the provided User Agent', done => { @@ -496,32 +487,26 @@ describe('Service', () => { const getUserAgentFn = util.getUserAgentFromPackageJson; util.getUserAgentFromPackageJson = packageJson => { util.getUserAgentFromPackageJson = getUserAgentFn; - assert.strictEqual(packageJson, service.packageJson); + expect(packageJson).toBe(service.packageJson); return userAgent; }; - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { - assert.strictEqual( - reqOpts.headers!['User-Agent'], - `${providedUserAgent} ${userAgent}`, - ); + (service as any).makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { + expect(reqOpts.headers!['User-Agent']).toBe(`${providedUserAgent} ${userAgent}`,); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should add the api-client header', done => { - service.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { + (service as any).makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => { const pkg = service.packageJson; - assert.strictEqual( - reqOpts.headers!['x-goog-api-client'], - `gl-node/${process.versions.node} gccl/${pkg.version}`, - ); + expect(reqOpts.headers!['x-goog-api-client']).toBe(`gl-node/${process.versions.node} gccl/${pkg.version}`,); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); describe('projectIdRequired', () => { @@ -532,15 +517,15 @@ describe('Service', () => { const expectedUri = [service.baseUrl, reqOpts.uri].join('/'); - service.makeAuthenticatedRequest = ( + (service as any).makeAuthenticatedRequest = ( reqOpts_: DecorateRequestOptions, ) => { - assert.strictEqual(reqOpts_.uri, expectedUri); + expect(reqOpts_.uri).toBe(expectedUri); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); }); @@ -556,15 +541,15 @@ describe('Service', () => { reqOpts.uri, ].join('/'); - service.makeAuthenticatedRequest = ( + (service as any).makeAuthenticatedRequest = ( reqOpts_: DecorateRequestOptions, ) => { - assert.strictEqual(reqOpts_.uri, expectedUri); + expect(reqOpts_.uri).toBe(expectedUri); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should use projectId override', done => { @@ -581,15 +566,15 @@ describe('Service', () => { reqOpts.uri, ].join('/'); - service.makeAuthenticatedRequest = ( + (service as any).makeAuthenticatedRequest = ( reqOpts_: DecorateRequestOptions, ) => { - assert.strictEqual(reqOpts_.uri, expectedUri); + expect(reqOpts_.uri).toBe(expectedUri); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); }); }); @@ -613,13 +598,13 @@ describe('Service', () => { return requestInterceptors; }; - service.makeAuthenticatedRequest = (reqOpts: FakeRequestOptions) => { - assert.strictEqual(reqOpts.a, 'a'); - assert.strictEqual(reqOpts.b, 'b'); + (service as any).makeAuthenticatedRequest = (reqOpts: FakeRequestOptions) => { + expect(reqOpts.a).toBe('a'); + expect(reqOpts.b).toBe('b'); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); it('should combine reqOpts interceptors', done => { @@ -643,14 +628,14 @@ describe('Service', () => { }, ]; - service.makeAuthenticatedRequest = (reqOpts: FakeRequestOptions) => { - assert.strictEqual(reqOpts.a, 'a'); - assert.strictEqual(reqOpts.b, 'b'); - assert.strictEqual(typeof reqOpts.interceptors_, 'undefined'); + (service as any).makeAuthenticatedRequest = (reqOpts: FakeRequestOptions) => { + expect(reqOpts.a).toBe('a'); + expect(reqOpts.b).toBe('b'); + expect(typeof reqOpts.interceptors_).toBe('undefined'); done(); }; - service.request_(reqOpts, assert.ifError); + (service as any).request_(reqOpts, assert.ifError); }); }); @@ -658,11 +643,11 @@ 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 as any).makeAuthenticatedRequest = (_: void, callback: Function) => { callback(err, res.body, res); }; - service.request_({uri: ''}, (e: Error) => { - assert.strictEqual(e, err); + (service as any).request_({uri: ''}, (e: Error) => { + expect(e).toBe(err); done(); }); }); @@ -670,20 +655,20 @@ describe('Service', () => { }); describe('request', () => { - let request_: Request; + let request_: any; - before(() => { - request_ = Service.prototype.request_; + beforeAll(() => { + request_ = (Service.prototype as any).request_; }); - after(() => { - Service.prototype.request_ = request_; + afterAll(() => { + (Service.prototype as any).request_ = request_; }); it('should call through to _request', async () => { const fakeOpts = {}; - Service.prototype.request_ = async (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts, fakeOpts); + (Service.prototype as any).request_ = async (reqOpts: DecorateRequestOptions) => { + expect(reqOpts).toBe(fakeOpts); return Promise.resolve({}); }; await service.request(fakeOpts); @@ -692,45 +677,45 @@ describe('Service', () => { it('should accept a callback', done => { const fakeOpts = {}; const response = {body: {abc: '123'}, statusCode: 200}; - Service.prototype.request_ = ( + (Service.prototype as any).request_ = ( reqOpts: DecorateRequestOptions, callback: Function, ) => { - assert.strictEqual(reqOpts, fakeOpts); + expect(reqOpts).toBe(fakeOpts); callback(null, response.body, response); }; service.request(fakeOpts, (err: Error, body: {}, res: {}) => { - assert.ifError(err); - assert.deepStrictEqual(res, response); - assert.deepStrictEqual(body, response.body); + expect(err).toBeFalsy(); + expect(res).toEqual(response); + expect(body).toEqual(response.body); done(); }); }); }); describe('requestStream', () => { - let request_: Request; + let request_: any; - before(() => { - request_ = Service.prototype.request_; + beforeAll(() => { + request_ = (Service.prototype as any).request_; }); - after(() => { - Service.prototype.request_ = request_; + afterAll(() => { + (Service.prototype as any).request_ = request_; }); it('should return whatever _request returns', async () => { const fakeOpts = {}; const fakeStream = {}; - Service.prototype.request_ = async (reqOpts: DecorateRequestOptions) => { - assert.strictEqual(reqOpts, fakeOpts); + (Service.prototype as any).request_ = async (reqOpts: DecorateRequestOptions) => { + expect(reqOpts).toBe(fakeOpts); return fakeStream; }; const stream = await service.requestStream(fakeOpts); - assert.strictEqual(stream, fakeStream); + expect(stream).toBe(fakeStream); }); }); }); diff --git a/core/common/test/util.ts b/core/common/test/util.ts index 6c018afd4d3d..cca2dfb9c769 100644 --- a/core/common/test/util.ts +++ b/core/common/test/util.ts @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +import * as assert from 'assert'; 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, @@ -26,13 +25,76 @@ import { OAuth2Client, } from 'google-auth-library'; 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'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let mockRequestOverride: any; +function mockFakeRequest(...args: any[]) { + const actualTeenyRequest = jest.requireActual('teeny-request'); + return (mockRequestOverride || actualTeenyRequest.teenyRequest).apply(null, args); +} + +mockFakeRequest.defaults = () => { + return mockFakeRequest; +}; + +let mockRetryRequestOverride: Function | null; +function mockFakeRetryRequest(...args: any[]) { + const actualRetryRequest = jest.requireActual('retry-request'); + return (mockRetryRequestOverride || actualRetryRequest).apply(null, args); +} + +let mockReplaceProjectIdTokenOverride: Function | null; +function mockFakeReplaceProjectIdToken(...args: any[]) { + const actualProjectify = jest.requireActual('@google-cloud/projectify'); + return (mockReplaceProjectIdTokenOverride || actualProjectify.replaceProjectIdToken).apply( + null, + args, + ); +} + +const mockFakeGoogleAuth = { + AuthClient: class CustomAuthClient extends (jest.requireActual('google-auth-library').AuthClient) { + async getAccessToken() { + return {token: '', res: undefined}; + } + + async getRequestHeaders() { + return {} as Headers; + } + + request = jest.requireActual('google-auth-library').OAuth2Client.prototype.request.bind(this); + }, + GoogleAuth: class { + constructor(config?: GoogleAuthOptions) { + const actualAuth = jest.requireActual('google-auth-library'); + return new actualAuth.GoogleAuth(config); + } + }, +}; + +jest.mock('google-auth-library', () => mockFakeGoogleAuth); +jest.mock('retry-request', () => { + return (a: any, b: any, c: any, d: any) => mockFakeRetryRequest(a, b, c, d); +}); +jest.mock('teeny-request', () => { + const fakeReq = (a: any, b: any, c: any, d: any) => mockFakeRequest(a, b, c, d); + fakeReq.defaults = () => fakeReq; + return { + teenyRequest: fakeReq, + }; +}); +jest.mock('@google-cloud/projectify', () => { + const actualProjectify = jest.requireActual('@google-cloud/projectify'); + return { + ...actualProjectify, + replaceProjectIdToken: (a: any, b: any, c: any) => mockFakeReplaceProjectIdToken(a, b, c), + }; +}); + import { Abortable, ApiError, @@ -46,6 +108,7 @@ import { ParsedHttpRespMessage, ParsedHttpResponseBody, Util, + util as actualUtil, } from '../src/util'; import {DEFAULT_PROJECT_ID_TOKEN} from '../src/service'; @@ -71,41 +134,12 @@ 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}; // 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); + function stub(method: any, meth: (...args: any[]) => any) { + return jest.spyOn(util as any, method).mockImplementation(meth); } function createExpectedErrorMessage(errors: string[]): string { @@ -122,46 +156,17 @@ 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; + beforeAll(() => { + util = actualUtil as any; }); - 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 +174,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?.includes(expectedMessage)).toBeTruthy(); }); it('should build correct ApiError', () => { @@ -189,16 +194,15 @@ describe('common/util', () => { response: fakeResponse, }; - sandbox - .stub(ApiError, 'createMultiErrorMessage') - .withArgs(error, errors) - .returns(fakeMessage); + 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); }); it('should parse the response body for errors', () => { @@ -217,13 +221,12 @@ describe('common/util', () => { } as r.Response, }; - sandbox - .stub(ApiError, 'createMultiErrorMessage') - .withArgs(errorBody, errors) - .returns(fakeMessage); + jest + .spyOn(ApiError, 'createMultiErrorMessage') + .mockReturnValue(fakeMessage); const apiError = new ApiError(errorBody); - assert.strictEqual(apiError.message, fakeMessage); + expect(apiError.message).toBe(fakeMessage); }); describe('createMultiErrorMessage', () => { @@ -243,7 +246,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 +259,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 +279,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 +291,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 +305,7 @@ describe('common/util', () => { }; const multiError = ApiError.createMultiErrorMessage(error); - assert.strictEqual(multiError, expectedErrorMessage); + expect(multiError).toBe(expectedErrorMessage); }); }); }); @@ -318,17 +321,16 @@ describe('common/util', () => { message: 'Partial failure occurred', }; - sandbox - .stub(util.ApiError, 'createMultiErrorMessage') - .withArgs(error, errors) - .returns(fakeMessage); + 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); }); }); @@ -337,7 +339,7 @@ describe('common/util', () => { const error = new Error('Error.'); util.handleResp(error, fakeResponse, null, err => { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }); }); @@ -348,14 +350,14 @@ describe('common/util', () => { it('should parse response', done => { stub('parseHttpRespMessage', resp_ => { - assert.deepStrictEqual(resp_, fakeResponse); + expect(resp_).toEqual(fakeResponse); return { resp: fakeResponse, }; }); stub('parseHttpRespBody', body_ => { - assert.strictEqual(body_, fakeResponse.body); + expect(body_).toBe(fakeResponse.body); return { body: fakeResponse.body, }; @@ -366,9 +368,9 @@ describe('common/util', () => { fakeResponse, fakeResponse.body, (err, body, resp) => { - assert.deepStrictEqual(err, fakeError); - assert.deepStrictEqual(body, fakeResponse.body); - assert.deepStrictEqual(resp, fakeResponse); + expect(err).toEqual(fakeError); + expect(body).toEqual(fakeResponse.body); + expect(resp).toEqual(fakeResponse); done(); }, ); @@ -377,12 +379,12 @@ describe('common/util', () => { it('should parse response for error', done => { const error = new Error('Error.'); - sandbox.stub(util, 'parseHttpRespMessage').callsFake(() => { + jest.spyOn(util, 'parseHttpRespMessage').mockImplementation(() => { return {err: error} as ParsedHttpRespMessage; }); util.handleResp(null, fakeResponse, {}, err => { - assert.deepStrictEqual(err, error); + expect(err).toEqual(error); done(); }); }); @@ -395,7 +397,7 @@ describe('common/util', () => { }); util.handleResp(null, fakeResponse, {}, err => { - assert.deepStrictEqual(err, error); + expect(err).toEqual(error); done(); }); }); @@ -414,7 +416,7 @@ describe('common/util', () => { const unparseableBody = 'Unparseable body.'; util.handleResp(null, null, unparseableBody, (err, body) => { - assert(body.includes(unparseableBody)); + expect(body.includes(unparseableBody)).toBeTruthy(); done(); }); }); @@ -428,15 +430,15 @@ describe('common/util', () => { {body: unparseableBody, statusCode} as r.Response, unparseableBody, err => { - assert(err, 'there should be an error'); + expect(err).toBeTruthy(); const apiError = err! as ApiError; - assert.strictEqual(apiError.code, statusCode); + expect(apiError.code).toBe(statusCode); const response = apiError.response; if (!response) { assert.fail('there should be a response property on the error'); } else { - assert.strictEqual(response.body, unparseableBody); + expect(response.body).toBe(unparseableBody); } done(); @@ -449,14 +451,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 +477,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).toEqual(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,12 +504,12 @@ 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); + 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[]; @@ -516,7 +518,7 @@ describe('common/util', () => { (mp[0] as any)['Content-Type'], 'application/json', ); - assert.strictEqual(mp[0].body, JSON.stringify(metadata)); + expect(mp[0].body).toBe(JSON.stringify(metadata)); assert.strictEqual( // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -551,13 +553,13 @@ 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); + 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'); + expect(mp[1]['Content-Type']).toBe('application/json'); done(); }, @@ -571,7 +573,7 @@ describe('common/util', () => { const ws = duplexify(); ws.on('error', err => { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }); @@ -603,7 +605,7 @@ describe('common/util', () => { util.makeWritableStream(dup, {makeAuthenticatedRequest() {}}, util.noop); dup.write(Buffer.from('abcdefghijklmnopqrstuvwxyz'), 'utf-8', util.noop); - assert.strictEqual(happened, true); + expect(happened).toBe(true); done(); }); @@ -618,17 +620,17 @@ 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); + expect(err).toBe(error); done(); }); @@ -653,14 +655,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,7 +671,7 @@ describe('common/util', () => { }; dup.on('response', resp => { - assert.strictEqual(resp, fakeResponse); + expect(resp).toBe(fakeResponse); done(); }); @@ -688,14 +690,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,7 +708,7 @@ describe('common/util', () => { }; util.makeWritableStream(dup, options, (data: {}) => { - assert.strictEqual(data, fakeResponse); + expect(data).toBe(fakeResponse); done(); }); @@ -727,10 +729,10 @@ 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}); + jest + .spyOn(mockFakeGoogleAuth, 'GoogleAuth') + .mockImplementation((config_?: any) => { + expect(config_).toEqual({...config, authClient: undefined}); setImmediate(done); return authClient; }); @@ -739,16 +741,16 @@ describe('common/util', () => { }); it('should pass an `AuthClient` to `GoogleAuth` when provided', done => { - const customAuthClient = new fakeGoogleAuth.AuthClient(); + const customAuthClient = new mockFakeGoogleAuth.AuthClient() as any; const config: MakeAuthenticatedRequestFactoryConfig = { authClient: customAuthClient, }; - sandbox - .stub(fakeGoogleAuth, 'GoogleAuth') - .callsFake((config_: GoogleAuthOptions) => { - assert.deepStrictEqual(config_, config); + jest + .spyOn(mockFakeGoogleAuth, 'GoogleAuth') + .mockImplementation((config_?: any) => { + expect(config_).toEqual(config); setImmediate(done); return authClient; }); @@ -759,8 +761,8 @@ 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); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation((config_?: any) => { + expect(config_?.projectId).toBe(undefined); setImmediate(done); return authClient; }); @@ -771,8 +773,8 @@ 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); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => { + expect(config.projectId).toBe(DEFAULT_PROJECT_ID_TOKEN); setImmediate(done); return authClient; }); @@ -781,10 +783,7 @@ describe('common/util', () => { }); 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,7 +791,7 @@ describe('common/util', () => { done(); } - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').callsFake(() => { + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => { return {getCredentials}; }); @@ -802,9 +801,9 @@ describe('common/util', () => { it('should return the authClient', () => { const authClient = {getCredentials() {}}; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const mar = util.makeAuthenticatedRequestFactory({}); - assert.strictEqual(mar.authClient, authClient); + expect(mar.authClient).toBe(authClient); }); describe('customEndpoint (no authentication attempted)', () => { @@ -813,14 +812,14 @@ describe('common/util', () => { const config = {customEndpoint: true}; beforeEach(() => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config); }); it('should decorate the request', done => { const decoratedRequest = {}; stub('decorateRequest', reqOpts_ => { - assert.strictEqual(reqOpts_, fakeReqOpts); + expect(reqOpts_).toBe(fakeReqOpts); return decoratedRequest; }); @@ -829,8 +828,8 @@ describe('common/util', () => { err: Error, authenticatedReqOpts: DecorateRequestOptions, ) { - assert.ifError(err); - assert.strictEqual(authenticatedReqOpts, decoratedRequest); + expect(err).toBeFalsy(); + expect(authenticatedReqOpts).toBe(decoratedRequest); done(); }, }); @@ -843,7 +842,7 @@ describe('common/util', () => { }); makeAuthenticatedRequest(fakeReqOpts, { onAuthenticated(err: Error) { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }, }); @@ -856,8 +855,8 @@ describe('common/util', () => { err: Error, authenticatedReqOpts: DecorateRequestOptions, ) { - assert.ifError(err); - assert.deepStrictEqual(reqOpts, authenticatedReqOpts); + expect(err).toBeFalsy(); + expect(reqOpts).toEqual(authenticatedReqOpts); done(); }, }); @@ -867,7 +866,7 @@ describe('common/util', () => { const reqOpts = {a: 'b', c: 'd'}; stub('makeRequest', rOpts => { - assert.deepStrictEqual(rOpts, reqOpts); + expect(rOpts).toEqual(reqOpts); done(); }); @@ -881,7 +880,7 @@ describe('common/util', () => { const config = {customEndpoint: true, useAuthWithCustomEndpoint: true}; beforeEach(() => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config); }); @@ -889,13 +888,13 @@ describe('common/util', () => { const reqOpts = {a: 'b', c: 'd'}; stub('makeRequest', rOpts => { - assert.deepStrictEqual(rOpts, reqOpts); + expect(rOpts).toEqual(reqOpts); done(); }); authClient.authorizeRequest = async (opts: {}) => { - assert.strictEqual(opts, reqOpts); - done(); + expect(opts).toBe(reqOpts); + return opts; }; makeAuthenticatedRequest(reqOpts, assert.ifError); @@ -904,44 +903,46 @@ describe('common/util', () => { describe('authentication', () => { it('should pass correct args to authorizeRequest', done => { - const fake = extend(true, authClient, { + const fake = extend(true, {}, authClient, { + getProjectId: async () => 'fake-project-id', authorizeRequest: async (rOpts: {}) => { - assert.deepStrictEqual(rOpts, fakeReqOpts); + expect(rOpts).toEqual(fakeReqOpts); setImmediate(done); return rOpts; }, }); - retryRequestOverride = () => { + mockRetryRequestOverride = () => { return new stream.PassThrough(); }; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(fake); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => fake as any); const mar = util.makeAuthenticatedRequestFactory({}); - mar(fakeReqOpts); + mar(fakeReqOpts, assert.ifError); }); it('should return a stream if callback is missing', () => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').callsFake(() => { - return extend(true, authClient, { + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => { + return extend(true, {}, authClient, { + getProjectId: async () => 'fake-project-id', authorizeRequest: async (rOpts: {}) => { return rOpts; }, }); }); - retryRequestOverride = () => { + mockRetryRequestOverride = () => { return new stream.PassThrough(); }; const mar = util.makeAuthenticatedRequestFactory({}); const s = mar(fakeReqOpts); - assert(s instanceof stream.Stream); + expect(s instanceof stream.Stream).toBeTruthy(); }); describe('projectId', () => { const reqOpts = {} as DecorateRequestOptions; it('should default to authClient projectId', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); stub('decorateRequest', (reqOpts, projectId) => { - assert.strictEqual(projectId, AUTH_CLIENT_PROJECT_ID); + expect(projectId).toBe(AUTH_CLIENT_PROJECT_ID); setImmediate(done); }); @@ -955,7 +956,7 @@ describe('common/util', () => { }); it('should prefer user-provided projectId', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const config = { customEndpoint: true, @@ -963,7 +964,7 @@ describe('common/util', () => { }; stub('decorateRequest', (reqOpts, projectId) => { - assert.strictEqual(projectId, config.projectId); + expect(projectId).toBe(config.projectId); setImmediate(done); }); @@ -976,9 +977,9 @@ describe('common/util', () => { }); it('should use default `projectId` and not call `authClient#getProjectId` when !`projectIdRequired`', done => { - const getProjectIdSpy = sandbox.spy(authClient, 'getProjectId'); + const getProjectIdSpy = jest.spyOn(authClient, 'getProjectId'); - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const config = { customEndpoint: true, @@ -986,7 +987,7 @@ describe('common/util', () => { }; stub('decorateRequest', (reqOpts, projectId) => { - assert.strictEqual(projectId, DEFAULT_PROJECT_ID_TOKEN); + expect(projectId).toBe(DEFAULT_PROJECT_ID_TOKEN); }); const makeAuthenticatedRequest = @@ -994,41 +995,39 @@ describe('common/util', () => { makeAuthenticatedRequest(reqOpts, { onAuthenticated: e => { - assert.ifError(e); - assert(getProjectIdSpy.notCalled); + expect(e).toBeNull(); + expect(getProjectIdSpy).not.toHaveBeenCalled(); done(e); }, }); }); it('should fallback to checking for a `projectId` on when missing a `projectId` when !`projectIdRequired`', done => { - const getProjectIdSpy = sandbox.spy(authClient, 'getProjectId'); + const getProjectIdSpy = jest.spyOn(authClient, 'getProjectId'); - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const config = { customEndpoint: true, projectIdRequired: false, }; - const decorateRequestStub = sandbox.stub(util, 'decorateRequest'); - - decorateRequestStub.onFirstCall().callsFake(() => { - throw new MissingProjectIdError(); - }); - - decorateRequestStub.onSecondCall().callsFake((reqOpts, projectId) => { - assert.strictEqual(projectId, AUTH_CLIENT_PROJECT_ID); - return reqOpts; - }); + jest.spyOn(util, 'decorateRequest') + .mockImplementationOnce(() => { + throw new MissingProjectIdError(); + }) + .mockImplementationOnce((reqOpts: any, projectId: any) => { + expect(projectId).toBe(AUTH_CLIENT_PROJECT_ID); + return reqOpts; + }); const makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory(config); makeAuthenticatedRequest(reqOpts, { onAuthenticated: e => { - assert.ifError(e); - assert(getProjectIdSpy.calledOnce); + expect(e).toBeNull(); + expect(getProjectIdSpy).toHaveBeenCalledTimes(1); done(e); }, }); @@ -1045,7 +1044,7 @@ describe('common/util', () => { }); it('should attempt request anyway', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const makeAuthenticatedRequest = util.makeAuthenticatedRequestFactory( {}, ); @@ -1059,8 +1058,8 @@ describe('common/util', () => { makeAuthenticatedRequest(correctReqOpts, { onAuthenticated(err, reqOpts) { - assert.ifError(err); - assert.strictEqual(reqOpts, correctReqOpts); + expect(err).toBeFalsy(); + expect(reqOpts).toBe(correctReqOpts); assert.notStrictEqual(reqOpts, incorrectReqOpts); done(); }, @@ -1074,7 +1073,7 @@ describe('common/util', () => { authClient.authorizeRequest = async () => { throw authClientError; }; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const makeRequestArg1 = new Error('API 401 Error.') as ApiError; makeRequestArg1.code = 401; @@ -1090,9 +1089,9 @@ describe('common/util', () => { makeAuthenticatedRequest( {} as DecorateRequestOptions, (arg1, arg2, arg3) => { - assert.strictEqual(arg1, authClientError); - assert.strictEqual(arg2, makeRequestArg2); - assert.strictEqual(arg3, makeRequestArg3); + expect(arg1).toBe(authClientError); + expect(arg2).toBe(makeRequestArg2); + expect(arg3).toBe(makeRequestArg3); done(); }, ); @@ -1102,7 +1101,7 @@ describe('common/util', () => { authClient.authorizeRequest = async () => { return {}; }; - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const makeRequestArg1 = new Error('API 401 Error.') as ApiError; makeRequestArg1.code = 401; @@ -1118,9 +1117,9 @@ describe('common/util', () => { makeAuthenticatedRequest( {} as DecorateRequestOptions, (arg1, arg2, arg3) => { - assert.strictEqual(arg1, makeRequestArg1); - assert.strictEqual(arg2, makeRequestArg2); - assert.strictEqual(arg3, makeRequestArg3); + expect(arg1).toBe(makeRequestArg1); + expect(arg2).toBe(makeRequestArg2); + expect(arg3).toBe(makeRequestArg3); done(); }, ); @@ -1128,7 +1127,7 @@ describe('common/util', () => { it('should block decorateRequest error', done => { const decorateRequestError = new Error('Error.'); - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); stub('decorateRequest', () => { throw decorateRequestError; }); @@ -1139,41 +1138,41 @@ describe('common/util', () => { makeAuthenticatedRequest(fakeReqOpts, { onAuthenticated(err) { assert.notStrictEqual(err, decorateRequestError); - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }, }); }); it('should invoke the callback with error', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const mar = util.makeAuthenticatedRequestFactory({}); mar(fakeReqOpts, err => { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }); }); it('should exec onAuthenticated callback with error', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const mar = util.makeAuthenticatedRequestFactory({}); mar(fakeReqOpts, { onAuthenticated(err) { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }, }); }); it('should emit an error and end the stream', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); 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); + expect(err).toBe(error); setImmediate(() => { - assert.strictEqual(stream.destroyed, true); + expect(stream.destroyed).toBe(true); done(); }); }); @@ -1187,31 +1186,31 @@ describe('common/util', () => { }); it('should return authenticated request to callback', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); stub('decorateRequest', reqOpts_ => { - assert.deepStrictEqual(reqOpts_, reqOpts); + expect(reqOpts_).toEqual(reqOpts); return reqOpts; }); const mar = util.makeAuthenticatedRequestFactory({}); mar(reqOpts, { onAuthenticated(err, authenticatedReqOpts) { - assert.strictEqual(authenticatedReqOpts, reqOpts); + expect(authenticatedReqOpts).toBe(reqOpts); done(); }, }); }); it('should make request with correct options', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const config = {keyFile: 'foo'}; stub('decorateRequest', reqOpts_ => { - assert.deepStrictEqual(reqOpts_, reqOpts); + expect(reqOpts_).toEqual(reqOpts); return reqOpts; }); stub('makeRequest', (authenticatedReqOpts, cfg, cb) => { - assert.deepStrictEqual(authenticatedReqOpts, reqOpts); - assert.deepStrictEqual(cfg, config); + expect(authenticatedReqOpts).toEqual(reqOpts); + expect(cfg).toEqual(config); cb(); }); const mar = util.makeAuthenticatedRequestFactory(config); @@ -1219,18 +1218,18 @@ describe('common/util', () => { }); it('should return abort() from the active request', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const retryRequest = { abort: done, }; - sandbox.stub(util, 'makeRequest').returns(retryRequest); + jest.spyOn(util, 'makeRequest').mockReturnValue(retryRequest); const mar = util.makeAuthenticatedRequestFactory({}); const req = mar(reqOpts, assert.ifError) as Abortable; req.abort(); }); it('should only abort() once', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); const retryRequest = { abort: done, // Will throw if called more than once. }; @@ -1249,10 +1248,10 @@ describe('common/util', () => { }); it('should provide stream to makeRequest', done => { - sandbox.stub(fakeGoogleAuth, 'GoogleAuth').returns(authClient); + jest.spyOn(mockFakeGoogleAuth, 'GoogleAuth').mockImplementation(() => authClient as any); stub('makeRequest', (authenticatedReqOpts, cfg) => { setImmediate(() => { - assert.strictEqual(cfg.stream, stream); + expect(cfg.stream).toBe(stream); done(); }); }); @@ -1265,60 +1264,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 +1325,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,15 +1336,15 @@ describe('common/util', () => { function testDefaultRetryRequestConfig(done: () => void) { return (reqOpts_: DecorateRequestOptions, config: MakeRequestConfig) => { - assert.strictEqual(reqOpts_, reqOpts); - assert.strictEqual(config.retries, 3); + expect(reqOpts_).toBe(reqOpts); + expect(config.retries).toBe(3); const error = new Error('Error.'); stub('parseHttpRespMessage', () => { return {err: error}; }); stub('shouldRetryRequest', err => { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }); @@ -1362,8 +1361,8 @@ describe('common/util', () => { }; function testCustomFunctionRetryRequestConfig(done: () => void) { return (reqOpts_: DecorateRequestOptions, config: MakeRequestConfig) => { - assert.strictEqual(reqOpts_, reqOpts); - assert.strictEqual(config.retries, 3); + expect(reqOpts_).toBe(reqOpts); + expect(config.retries).toBe(3); extend({}, config, customRetryRequestFunctionConfig); const error = new Error(errorMessage); @@ -1371,11 +1370,11 @@ describe('common/util', () => { return {err: error}; }); stub('shouldRetryRequest', err => { - assert.strictEqual(err, error); + expect(err).toBe(error); done(); }); - assert.strictEqual(config.shouldRetryFn!(), true); + expect(config.shouldRetryFn!()).toBe(true); done(); }; } @@ -1386,7 +1385,7 @@ describe('common/util', () => { reqOpts: DecorateRequestOptions, config: retryRequest.Options, ) => { - assert.strictEqual(config.retries, 0); + expect(config.retries).toBe(0); done(); }; } @@ -1419,26 +1418,11 @@ 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, - ); + expect(config.retries).toBe(0); + expect(config.noResponseRetries).toBe(0); + expect(config.retryDelayMultiplier).toBe(retryOptionsConfig.retryOptions.retryDelayMultiplier); + expect(config.totalTimeout).toBe(retryOptionsConfig.retryOptions.totalTimeout); + expect(config.maxRetryDelay).toBe(retryOptionsConfig.retryOptions.maxRetryDelay); done(); }; } @@ -1446,7 +1430,7 @@ describe('common/util', () => { const customRetryRequestConfig = {maxRetries: 10}; function testCustomRetryRequestConfig(done: () => void) { return (reqOpts: DecorateRequestOptions, config: MakeRequestConfig) => { - assert.strictEqual(config.retries, customRetryRequestConfig.maxRetries); + expect(config.retries).toBe(customRetryRequestConfig.maxRetries); done(); }; } @@ -1462,19 +1446,19 @@ describe('common/util', () => { userStream .on('error', error_ => { - assert.strictEqual(error_, error); + expect(error_).toBe(error); requestStream.emit('response', response); }) .on('response', response_ => { - assert.strictEqual(response_, response); + expect(response_).toBe(response); requestStream.emit('complete', complete); }) .on('complete', complete_ => { - assert.strictEqual(complete_, complete); + expect(complete_).toBe(complete); done(); }); - retryRequestOverride = () => { + mockRetryRequestOverride = () => { setImmediate(() => { requestStream.emit('error', error); }); @@ -1488,8 +1472,8 @@ describe('common/util', () => { describe('GET requests', () => { it('should use retryRequest', done => { const userStream = duplexify(); - retryRequestOverride = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_, reqOpts); + mockRetryRequestOverride = (reqOpts_: DecorateRequestOptions) => { + expect(reqOpts_).toBe(reqOpts); setImmediate(done); return new stream.Stream(); }; @@ -1499,11 +1483,11 @@ 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); + expect(stream).toBe(retryRequestStream); done(); }; util.makeRequest(reqOpts, {stream: userStream}, util.noop); @@ -1512,7 +1496,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,23 +1515,23 @@ describe('common/util', () => { method: 'POST', } as DecorateRequestOptions; - retryRequestOverride = done; // will throw. - requestOverride = (reqOpts_: DecorateRequestOptions) => { - assert.strictEqual(reqOpts_, reqOpts); + mockRetryRequestOverride = done; // will throw. + mockRequestOverride = (reqOpts_: DecorateRequestOptions) => { + expect(reqOpts_).toBe(reqOpts); setImmediate(done); 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; + mockRequestOverride = () => requestStream; + mockRequestOverride.defaults = () => mockRequestOverride; userStream.setWritable = stream => { - assert.strictEqual(stream, requestStream); + expect(stream).toBe(requestStream); done(); }; util.makeRequest( @@ -1560,13 +1544,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,7 +1561,7 @@ 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, @@ -1587,7 +1571,7 @@ describe('common/util', () => { }); it('should allow setting a custom retry function', done => { - retryRequestOverride = testCustomFunctionRetryRequestConfig(done); + mockRetryRequestOverride = testCustomFunctionRetryRequestConfig(done); util.makeRequest( reqOpts, customRetryRequestFunctionConfig, @@ -1596,17 +1580,17 @@ describe('common/util', () => { }); it('should allow turning off retries to retryRequest', done => { - retryRequestOverride = testNoRetryRequestConfig(done); + mockRetryRequestOverride = testNoRetryRequestConfig(done); util.makeRequest(reqOpts, noRetryRequestConfig, assert.ifError); }); it('should override number of retries to retryRequest', done => { - retryRequestOverride = testCustomRetryRequestConfig(done); + mockRetryRequestOverride = testCustomRetryRequestConfig(done); util.makeRequest(reqOpts, customRetryRequestConfig, assert.ifError); }); it('should use retryOptions if provided', done => { - retryRequestOverride = testRetryOptions(done); + mockRetryRequestOverride = testRetryOptions(done); util.makeRequest(reqOpts, retryOptionsConfig, assert.ifError); }); @@ -1625,7 +1609,7 @@ describe('common/util', () => { }); it('should allow request options to control retry setting', done => { - retryRequestOverride = testCustomRetryRequestConfig(done); + mockRetryRequestOverride = testCustomRetryRequestConfig(done); const reqOptsWithRetrySettings = extend( {}, reqOpts, @@ -1640,18 +1624,18 @@ describe('common/util', () => { it('should return the instance of retryRequest', () => { const requestInstance = {}; - retryRequestOverride = () => { + mockRetryRequestOverride = () => { return requestInstance; }; const res = util.makeRequest(reqOpts, {}, assert.ifError); - assert.strictEqual(res, requestInstance); + 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,9 +1644,9 @@ describe('common/util', () => { }; stub('handleResp', (err, resp, body_) => { - assert.strictEqual(err, error); - assert.strictEqual(resp, fakeResponse); - assert.strictEqual(body_, body); + expect(err).toBe(error); + expect(resp).toBe(fakeResponse); + expect(body_).toBe(body); done(); }); @@ -1681,7 +1665,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.autoPaginate, undefined); + expect(decoratedReqOpts.autoPaginate).toBe(undefined); }); it('should delete qs.autoPaginateVal', () => { @@ -1692,7 +1676,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.autoPaginateVal, undefined); + expect(decoratedReqOpts.autoPaginateVal).toBe(undefined); }); it('should delete objectMode', () => { @@ -1703,7 +1687,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.objectMode, undefined); + expect(decoratedReqOpts.objectMode).toBe(undefined); }); it('should delete qs.autoPaginate', () => { @@ -1716,7 +1700,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.qs.autoPaginate, undefined); + expect(decoratedReqOpts.qs.autoPaginate).toBe(undefined); }); it('should delete qs.autoPaginateVal', () => { @@ -1729,7 +1713,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.qs.autoPaginateVal, undefined); + expect(decoratedReqOpts.qs.autoPaginateVal).toBe(undefined); }); it('should delete json.autoPaginate', () => { @@ -1742,7 +1726,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.json.autoPaginate, undefined); + expect(decoratedReqOpts.json.autoPaginate).toBe(undefined); }); it('should delete json.autoPaginateVal', () => { @@ -1755,7 +1739,7 @@ describe('common/util', () => { projectId, ); - assert.strictEqual(decoratedReqOpts.json.autoPaginateVal, undefined); + expect(decoratedReqOpts.json.autoPaginateVal).toBe(undefined); }); it('should replace project ID tokens for qs object', () => { @@ -1766,17 +1750,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 +1776,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 +1797,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 +1817,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 +1845,33 @@ describe('common/util', () => { describe('Service objects', () => { it('should match by constructor name', () => { - assert(util.isCustomType(pubsub, 'pubsub')); + expect(util.isCustomType(pubsub, 'pubsub')).toBeTruthy(); }); it('should support any casing', () => { - assert(util.isCustomType(pubsub, 'PubSub')); + expect(util.isCustomType(pubsub, 'PubSub')).toBeTruthy(); }); it('should not match if the wrong Service', () => { - assert(!util.isCustomType(subscription, 'BigQuery')); + expect(util.isCustomType(subscription, 'BigQuery')).toBeFalsy(); }); }); 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')).toBeTruthy(); + expect(util.isCustomType(subscription, 'pubsub/subscription')).toBeTruthy(); - assert(util.isCustomType(subscription, 'middlelayer')); - assert(util.isCustomType(subscription, 'middlelayer/subscription')); + expect(util.isCustomType(subscription, 'middlelayer')).toBeTruthy(); + expect(util.isCustomType(subscription, 'middlelayer/subscription')).toBeTruthy(); }); it('should support any casing', () => { - assert(util.isCustomType(subscription, 'PubSub/Subscription')); + expect(util.isCustomType(subscription, 'PubSub/Subscription')).toBeTruthy(); }); it('should not match if the wrong ServiceObject', () => { - assert(!util.isCustomType(subscription, 'pubsub/topic')); + expect(util.isCustomType(subscription, 'pubsub/topic')).toBeFalsy(); }); }); }); @@ -1899,7 +1883,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 +1891,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(optionsOrCallback).toBe(cb); + expect(opts).toEqual({}); }); it('should allow passing both opts and callback', () => { @@ -1918,8 +1902,8 @@ describe('common/util', () => { optionsOrCallback, callback, ); - assert.strictEqual(opts, optionsOrCallback); - assert.strictEqual(cb, callback); + expect(opts).toBe(optionsOrCallback); + expect(cb).toBe(callback); }); }); }); From bc1fee44f2a024a98ea3c0cb999aa43cf9183551 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 24 Jun 2026 18:46:59 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- core/common/.mocharc.js | 29 +++++++++++++++++++++++++++++ core/common/.nycrc | 24 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 core/common/.mocharc.js create mode 100644 core/common/.nycrc diff --git a/core/common/.mocharc.js b/core/common/.mocharc.js new file mode 100644 index 000000000000..2431859019f8 --- /dev/null +++ b/core/common/.mocharc.js @@ -0,0 +1,29 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// 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 +// +// 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 diff --git a/core/common/.nycrc b/core/common/.nycrc new file mode 100644 index 000000000000..b18d5472b62b --- /dev/null +++ b/core/common/.nycrc @@ -0,0 +1,24 @@ +{ + "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 +} From bb808929b3447c96ed638dd0ca5394a6c5894e39 Mon Sep 17 00:00:00 2001 From: Gabe Pearhill Date: Wed, 24 Jun 2026 13:49:46 -0700 Subject: [PATCH 3/3] fix process spawning in system tests --- core/common/system-test/install.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/common/system-test/install.ts b/core/common/system-test/install.ts index 4b0991bc6b64..04be9e2da437 100644 --- a/core/common/system-test/install.ts +++ b/core/common/system-test/install.ts @@ -35,7 +35,12 @@ const spawnp = ( options: cp.SpawnOptions = {}, ) => { return new Promise((resolve, reject) => { - cp.spawn(command, args, Object.assign(options, {stdio: 'inherit'})) + const spawnOptions = { + ...options, + stdio: 'inherit' as const, + shell: os.platform() === 'win32' ? true : options.shell, + }; + cp.spawn(command, args, spawnOptions) .on('close', code => { if (code === 0) { resolve();