From ad3fac577cca737976ed82cd9d55027eb8af654c Mon Sep 17 00:00:00 2001 From: Aras Abbasi Date: Sun, 7 Apr 2024 14:08:01 +0200 Subject: [PATCH] mock: improve mock interceptor (#3062) --- lib/mock/mock-interceptor.js | 35 ++++++++++++++++++----------------- test/mock-interceptor.js | 20 +++++++++++++++++--- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/lib/mock/mock-interceptor.js b/lib/mock/mock-interceptor.js index d071a90db06..c6b16b35f9c 100644 --- a/lib/mock/mock-interceptor.js +++ b/lib/mock/mock-interceptor.js @@ -90,7 +90,7 @@ class MockInterceptor { this[kContentLength] = false } - createMockScopeDispatchData (statusCode, data, responseOptions = {}) { + createMockScopeDispatchData ({ statusCode, data, responseOptions }) { const responseData = getResponseData(data) const contentLength = this[kContentLength] ? { 'content-length': responseData.length } : {} const headers = { ...this[kDefaultHeaders], ...contentLength, ...responseOptions.headers } @@ -99,14 +99,11 @@ class MockInterceptor { return { statusCode, data, headers, trailers } } - validateReplyParameters (statusCode, data, responseOptions) { - if (typeof statusCode === 'undefined') { + validateReplyParameters (replyParameters) { + if (typeof replyParameters.statusCode === 'undefined') { throw new InvalidArgumentError('statusCode must be defined') } - if (typeof data === 'undefined') { - throw new InvalidArgumentError('data must be defined') - } - if (typeof responseOptions !== 'object' || responseOptions === null) { + if (typeof replyParameters.responseOptions !== 'object' || replyParameters.responseOptions === null) { throw new InvalidArgumentError('responseOptions must be an object') } } @@ -114,28 +111,28 @@ class MockInterceptor { /** * Mock an undici request with a defined reply. */ - reply (replyData) { + reply (replyOptionsCallbackOrStatusCode) { // Values of reply aren't available right now as they // can only be available when the reply callback is invoked. - if (typeof replyData === 'function') { + if (typeof replyOptionsCallbackOrStatusCode === 'function') { // We'll first wrap the provided callback in another function, // this function will properly resolve the data from the callback // when invoked. const wrappedDefaultsCallback = (opts) => { // Our reply options callback contains the parameter for statusCode, data and options. - const resolvedData = replyData(opts) + const resolvedData = replyOptionsCallbackOrStatusCode(opts) // Check if it is in the right format - if (typeof resolvedData !== 'object') { + if (typeof resolvedData !== 'object' || resolvedData === null) { throw new InvalidArgumentError('reply options callback must return an object') } - const { statusCode, data = '', responseOptions = {} } = resolvedData - this.validateReplyParameters(statusCode, data, responseOptions) + const replyParameters = { data: '', responseOptions: {}, ...resolvedData } + this.validateReplyParameters(replyParameters) // Since the values can be obtained immediately we return them // from this higher order function that will be resolved later. return { - ...this.createMockScopeDispatchData(statusCode, data, responseOptions) + ...this.createMockScopeDispatchData(replyParameters) } } @@ -148,11 +145,15 @@ class MockInterceptor { // we should have 1-3 parameters. So we spread the arguments of // this function to obtain the parameters, since replyData will always // just be the statusCode. - const [statusCode, data = '', responseOptions = {}] = [...arguments] - this.validateReplyParameters(statusCode, data, responseOptions) + const replyParameters = { + statusCode: replyOptionsCallbackOrStatusCode, + data: arguments[1] === undefined ? '' : arguments[1], + responseOptions: arguments[2] === undefined ? {} : arguments[2] + } + this.validateReplyParameters(replyParameters) // Send in-already provided data like usual - const dispatchData = this.createMockScopeDispatchData(statusCode, data, responseOptions) + const dispatchData = this.createMockScopeDispatchData(replyParameters) const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], dispatchData) return new MockScope(newMockDispatch) } diff --git a/test/mock-interceptor.js b/test/mock-interceptor.js index 961a7576dc1..0d16290f579 100644 --- a/test/mock-interceptor.js +++ b/test/mock-interceptor.js @@ -107,7 +107,7 @@ describe('MockInterceptor - reply options callback', () => { }) test('should error if passed options invalid', async (t) => { - t = tspl(t, { plan: 3 }) + t = tspl(t, { plan: 4 }) const baseUrl = 'http://localhost:9999' const mockAgent = new MockAgent() @@ -116,10 +116,15 @@ describe('MockInterceptor - reply options callback', () => { const mockPool = mockAgent.get(baseUrl) mockPool.intercept({ - path: '/test', + path: '/test-return-undefined', method: 'GET' }).reply(() => { }) + mockPool.intercept({ + path: '/test-return-null', + method: 'GET' + }).reply(() => { return null }) + mockPool.intercept({ path: '/test3', method: 'GET' @@ -138,7 +143,16 @@ describe('MockInterceptor - reply options callback', () => { })) t.throws(() => mockPool.dispatch({ - path: '/test', + path: '/test-return-undefined', + method: 'GET' + }, { + onHeaders: () => { }, + onData: () => { }, + onComplete: () => { } + }), new InvalidArgumentError('reply options callback must return an object')) + + t.throws(() => mockPool.dispatch({ + path: '/test-return-null', method: 'GET' }, { onHeaders: () => { },