From 39c43ba08ebaafbce50e7a1e34bc303d9dbc6e1f Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 28 Jul 2021 10:53:27 +0100 Subject: [PATCH 1/3] Add --idle-timeout option --- src/Request.ts | 44 ++++++-- src/commands/CommandDefinitions.ts | 3 +- src/commands/UploadBrowserCommand.ts | 2 + src/commands/UploadNodeCommand.ts | 2 + src/commands/UploadReactNativeCommand.ts | 2 + src/uploaders/BrowserUploader.ts | 12 ++- src/uploaders/NodeUploader.ts | 8 +- src/uploaders/ReactNativeUploader.ts | 11 +- .../__test__/BrowserUploader.test.ts | 63 +++++++---- src/uploaders/__test__/NodeUploader.test.ts | 57 ++++++---- .../__test__/ReactNativeUploader.test.ts | 100 ++++++++++-------- 11 files changed, 200 insertions(+), 104 deletions(-) diff --git a/src/Request.ts b/src/Request.ts index ff0f96b..cd440f9 100644 --- a/src/Request.ts +++ b/src/Request.ts @@ -37,16 +37,25 @@ interface ReactNativePayload { bundle: File } +interface RequestOptions { + idleTimeout?: number +} + const MAX_ATTEMPTS = 5 const RETRY_INTERVAL_MS = parseInt(process.env.BUGSNAG_RETRY_INTERVAL_MS as string) || 1000 -const TIMEOUT_MS = parseInt(process.env.BUGSNAG_TIMEOUT_MS as string) || 30000 - -export default async function request (endpoint: string, payload: Payload, requestOpts: http.RequestOptions): Promise { +const DEFAULT_TIMEOUT_MS = parseInt(process.env.BUGSNAG_TIMEOUT_MS as string) || 30000 + +export default async function request ( + endpoint: string, + payload: Payload, + requestOpts: http.RequestOptions, + options: RequestOptions = {} +): Promise { let attempts = 0 const go = async (): Promise => { try { attempts++ - await send(endpoint, payload, requestOpts) + await send(endpoint, payload, requestOpts, options) } catch (err) { if (err && err.isRetryable !== false && attempts < MAX_ATTEMPTS) { await new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL_MS)) @@ -109,7 +118,12 @@ function appendReactNativeFormData(formData: FormData, payload: ReactNativePaylo return formData } -export async function send (endpoint: string, payload: Payload, requestOpts: http.RequestOptions): Promise { +export async function send ( + endpoint: string, + payload: Payload, + requestOpts: http.RequestOptions, + options: RequestOptions = {} +): Promise { return new Promise((resolve, reject) => { const formData = createFormData(payload) @@ -153,7 +167,7 @@ export async function send (endpoint: string, payload: Payload, requestOpts: htt formData.pipe(req) addErrorHandler(req, reject) - addTimeout(req, reject) + addTimeout(req, reject, options) }) } @@ -169,7 +183,7 @@ export function isRetryable (status?: number): boolean { ) } -export function fetch(endpoint: string): Promise { +export function fetch(endpoint: string, options: RequestOptions = {}): Promise { return new Promise((resolve, reject) => { const parsedUrl = url.parse(endpoint) @@ -197,7 +211,7 @@ export function fetch(endpoint: string): Promise { }) addErrorHandler(req, reject) - addTimeout(req, reject) + addTimeout(req, reject, options) }) } @@ -218,8 +232,18 @@ function addErrorHandler(req: http.ClientRequest, reject: (reason: NetworkError) }) } -function addTimeout(req: http.ClientRequest, reject: (reason: NetworkError) => void): void { - req.setTimeout(TIMEOUT_MS, () => { +const minutesToMilliseconds = (minutes: number): number => minutes * 60 * 1000 + +function addTimeout( + req: http.ClientRequest, + reject: (reason: NetworkError) => void, + options: RequestOptions +): void { + const timeout = options.idleTimeout + ? minutesToMilliseconds(options.idleTimeout) + : DEFAULT_TIMEOUT_MS + + req.setTimeout(timeout, () => { const err = new NetworkError('Connection timed out') err.code = NetworkErrorCode.TIMEOUT reject(err) diff --git a/src/commands/CommandDefinitions.ts b/src/commands/CommandDefinitions.ts index 2356cd0..7ccda55 100644 --- a/src/commands/CommandDefinitions.ts +++ b/src/commands/CommandDefinitions.ts @@ -5,5 +5,6 @@ export const commonCommandDefs = [ { name: 'project-root', type: String, description: 'the top level directory of your project (defaults to the current directory)' }, { name: 'endpoint', type: String, description: 'customize the endpoint for Bugsnag On-Premise' }, { name: 'quiet', type: Boolean, description: 'less verbose logging' }, - { name: 'code-bundle-id', type: String } + { name: 'code-bundle-id', type: String }, + { name: 'idle-timeout', type: Number, description: 'idle timeout for HTTP requests in minutes' } ] diff --git a/src/commands/UploadBrowserCommand.ts b/src/commands/UploadBrowserCommand.ts index 40b5543..094ef8e 100644 --- a/src/commands/UploadBrowserCommand.ts +++ b/src/commands/UploadBrowserCommand.ts @@ -50,6 +50,7 @@ export default async function uploadBrowser (argv: string[], opts: Record, unknownArgs: Record) { @@ -50,6 +51,7 @@ export async function uploadOne ({ sourceMap, appVersion, codeBundleId, + idleTimeout, overwrite = false, projectRoot = process.cwd(), endpoint = DEFAULT_UPLOAD_ORIGIN, @@ -110,7 +112,7 @@ export async function uploadOne ({ minifiedFile: new File(fullBundlePath, bundleContent), sourceMap: new File(fullSourceMapPath, JSON.stringify(transformedSourceMap)), overwrite: overwrite - }, requestOpts) + }, requestOpts, { idleTimeout }) logger.success(`Success, uploaded ${sourceMap} and ${bundle} to ${url} in ${(new Date()).getTime() - start}ms`) } catch (e) { if (e.cause) { @@ -133,6 +135,7 @@ interface UploadMultipleOpts { detectAppVersion?: boolean requestOpts?: http.RequestOptions logger?: Logger + idleTimeout?: number } function validateMultipleOpts (opts: Record, unknownArgs: Record) { @@ -148,6 +151,7 @@ export async function uploadMultiple ({ directory, appVersion, codeBundleId, + idleTimeout, overwrite = false, projectRoot = process.cwd(), endpoint = DEFAULT_UPLOAD_ORIGIN, @@ -235,7 +239,7 @@ export async function uploadMultiple ({ minifiedFile: (bundleContent && fullBundlePath) ? new File(fullBundlePath, bundleContent) : undefined, sourceMap: new File(fullSourceMapPath, JSON.stringify(transformedSourceMap)), overwrite: overwrite - }, requestOpts) + }, requestOpts, { idleTimeout }) const uploadedFiles = (bundleContent && fullBundlePath) ? `${sourceMap} and ${bundlePath}` : sourceMap diff --git a/src/uploaders/ReactNativeUploader.ts b/src/uploaders/ReactNativeUploader.ts index 50ab138..ded157a 100644 --- a/src/uploaders/ReactNativeUploader.ts +++ b/src/uploaders/ReactNativeUploader.ts @@ -35,6 +35,7 @@ interface CommonUploadOpts { endpoint?: string requestOpts?: http.RequestOptions logger?: Logger + idleTimeout?: number } interface UploadSingleOpts extends CommonUploadOpts { @@ -60,6 +61,7 @@ export async function uploadOne ({ codeBundleId, appVersionCode, appBundleVersion, + idleTimeout, overwrite = true, projectRoot = process.cwd(), endpoint = DEFAULT_UPLOAD_ORIGIN, @@ -114,7 +116,7 @@ export async function uploadOne ({ dev, ...marshalledVersions, overwrite - }, requestOpts) + }, requestOpts, { idleTimeout }) logger.success(`Success, uploaded ${sourceMap} and ${bundle} to ${url} in ${(new Date()).getTime() - start}ms`) } catch (e) { if (e.cause) { @@ -147,6 +149,7 @@ export async function fetchAndUploadOne ({ codeBundleId, appVersionCode, appBundleVersion, + idleTimeout, overwrite = true, projectRoot = process.cwd(), endpoint = DEFAULT_UPLOAD_ORIGIN, @@ -194,7 +197,7 @@ export async function fetchAndUploadOne ({ try { logger.debug(`Fetching source map from ${sourceMapUrl}`) - sourceMap = await fetch(sourceMapUrl) + sourceMap = await fetch(sourceMapUrl, { idleTimeout }) } catch (e) { logger.error( formatFetchError(e, bundlerUrl, bundlerEntryPoint), e @@ -204,7 +207,7 @@ export async function fetchAndUploadOne ({ try { logger.debug(`Fetching bundle from ${bundleUrl}`) - bundle = await fetch(bundleUrl) + bundle = await fetch(bundleUrl, { idleTimeout }) } catch (e) { logger.error( formatFetchError(e, bundlerUrl, bundlerEntryPoint), e @@ -231,7 +234,7 @@ export async function fetchAndUploadOne ({ dev, ...marshalledVersions, overwrite - }, requestOpts) + }, requestOpts, { idleTimeout }) logger.success(`Success, uploaded ${entryPoint}.js.map to ${url} in ${(new Date()).getTime() - start}ms`) } catch (e) { if (e.cause) { diff --git a/src/uploaders/__test__/BrowserUploader.test.ts b/src/uploaders/__test__/BrowserUploader.test.ts index f6e08ca..d2b3022 100644 --- a/src/uploaders/__test__/BrowserUploader.test.ts +++ b/src/uploaders/__test__/BrowserUploader.test.ts @@ -38,7 +38,8 @@ test('uploadOne(): dispatches a request with the correct params', async () => { overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -63,7 +64,8 @@ test('uploadOne(): dispatches a request with the correct params (no bundle)', as overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -82,7 +84,8 @@ test('uploadOne(): codeBundleId', async () => { expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r0001' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -348,7 +351,8 @@ test('uploadOne(): custom endpoint (origin only)', async () => { overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -374,7 +378,8 @@ test('uploadOne(): custom endpoint (absolute)', async () => { overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -426,7 +431,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/2.e5bb21a6.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -444,7 +450,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/3.1b8b4fc7.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -462,7 +469,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/main.286ac573.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -480,7 +488,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/runtime-main.ad66c902.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -512,7 +521,8 @@ test('uploadMultiple(): success passing appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/2.e5bb21a6.chunk.js', appVersion: '4.5.6' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -530,7 +540,8 @@ test('uploadMultiple(): success passing appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/3.1b8b4fc7.chunk.js', appVersion: '4.5.6' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -548,7 +559,8 @@ test('uploadMultiple(): success passing appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/main.286ac573.chunk.js', appVersion: '4.5.6' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -566,7 +578,8 @@ test('uploadMultiple(): success passing appVersion', async () => { minifiedUrl: 'http://mybundle.jim/static/js/runtime-main.ad66c902.js', appVersion: '4.5.6' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -586,22 +599,26 @@ test('uploadMultiple(): success with codeBundleId', async () => { expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012'}), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -634,7 +651,8 @@ test('uploadMultiple(): success using absolute path for "directory"', async () = minifiedUrl: 'http://mybundle.jim/static/js/2.e5bb21a6.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -652,7 +670,8 @@ test('uploadMultiple(): success using absolute path for "directory"', async () = minifiedUrl: 'http://mybundle.jim/static/js/3.1b8b4fc7.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -670,7 +689,8 @@ test('uploadMultiple(): success using absolute path for "directory"', async () = minifiedUrl: 'http://mybundle.jim/static/js/main.286ac573.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -688,7 +708,8 @@ test('uploadMultiple(): success using absolute path for "directory"', async () = minifiedUrl: 'http://mybundle.jim/static/js/runtime-main.ad66c902.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) diff --git a/src/uploaders/__test__/NodeUploader.test.ts b/src/uploaders/__test__/NodeUploader.test.ts index 1985c8a..952afc2 100644 --- a/src/uploaders/__test__/NodeUploader.test.ts +++ b/src/uploaders/__test__/NodeUploader.test.ts @@ -37,7 +37,8 @@ test('uploadOne(): dispatches a request with the correct params', async () => { overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -62,7 +63,8 @@ test('uploadOne(): dispatches a request with the correct params and detected app overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -183,7 +185,8 @@ test('uploadOne(): custom endpoint (origin only)', async () => { overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -207,7 +210,8 @@ test('uploadOne(): custom endpoint (absolute)', async () => { overwrite: false, sourceMap: expect.any(Object) }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -251,7 +255,8 @@ test('uploadOne(): codeBundleId', async () => { sourceMap: expect.any(Object), codeBundleId: 'r0001' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -282,7 +287,8 @@ test('uploadMultiple(): success', async () => { minifiedUrl: 'dist/a.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -300,7 +306,8 @@ test('uploadMultiple(): success', async () => { minifiedUrl: 'dist/b.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -318,7 +325,8 @@ test('uploadMultiple(): success', async () => { minifiedUrl: 'dist/index.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -349,7 +357,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'build/static/js/2.e5bb21a6.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -367,7 +376,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'build/static/js/3.1b8b4fc7.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -385,7 +395,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'build/static/js/main.286ac573.chunk.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -403,7 +414,8 @@ test('uploadMultiple(): success with detected appVersion', async () => { minifiedUrl: 'build/static/js/runtime-main.ad66c902.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -421,22 +433,26 @@ test('uploadMultiple(): success with codeBundleId', async () => { expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012'}), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', expect.objectContaining({ codeBundleId: 'r00012' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -467,7 +483,8 @@ test('uploadMultiple(): success using absolute path for "directory"', async () = minifiedUrl: 'dist/a.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -485,7 +502,8 @@ test('uploadMultiple(): success using absolute path for "directory"', async () = minifiedUrl: 'dist/b.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest).toHaveBeenCalledWith( 'https://upload.bugsnag.com/sourcemap', @@ -503,7 +521,8 @@ test('uploadMultiple(): success using absolute path for "directory"', async () = minifiedUrl: 'dist/index.js', appVersion: '1.2.3' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) diff --git a/src/uploaders/__test__/ReactNativeUploader.test.ts b/src/uploaders/__test__/ReactNativeUploader.test.ts index 8c201f3..399f087 100644 --- a/src/uploaders/__test__/ReactNativeUploader.test.ts +++ b/src/uploaders/__test__/ReactNativeUploader.test.ts @@ -45,7 +45,8 @@ test('uploadOne(): dispatches a request with the correct params for Android with sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -76,7 +77,8 @@ test('uploadOne(): dispatches a request with the correct params for iOS with app sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -109,7 +111,8 @@ test('uploadOne(): dispatches a request with the correct params for Android with sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -142,7 +145,8 @@ test('uploadOne(): dispatches a request with the correct params for iOS with app sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -174,7 +178,8 @@ test('uploadOne(): dispatches a request with the correct params for Android with sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest.mock.calls[0][1]).toEqual(expect.not.objectContaining({ appVersionCode: '3.2.1' })) }) @@ -207,7 +212,8 @@ test('uploadOne(): dispatches a request with the correct params for iOS with cod sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockedRequest.mock.calls[0][1]).toEqual(expect.not.objectContaining({ appBundleVersion: '4.5.6' })) }) @@ -328,7 +334,8 @@ test('uploadOne(): custom endpoint (absolute)', async () => { expect(mockedRequest).toHaveBeenCalledWith( 'https://bugsnag.my-company.com/source-map-custom', expect.objectContaining({ apiKey: '123' }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -382,8 +389,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for Andr }) expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=false') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=false') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=false', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=false', { idleTimeout: undefined }) expect(mockedRequest).toHaveBeenCalledTimes(1) expect(mockedRequest).toHaveBeenCalledWith( @@ -397,7 +404,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for Andr sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining( @@ -429,8 +437,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for iOS }) expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=ios&dev=false') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=ios&dev=false') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=ios&dev=false', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=ios&dev=false', { idleTimeout: undefined }) expect(mockedRequest).toHaveBeenCalledTimes(1) expect(mockedRequest).toHaveBeenCalledWith( @@ -444,7 +452,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for iOS sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining( @@ -475,8 +484,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for Andr }) expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).toHaveBeenCalledTimes(1) expect(mockedRequest).toHaveBeenCalledWith( @@ -490,7 +499,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for Andr sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -517,8 +527,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for iOS }) expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=ios&dev=true') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=ios&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=ios&dev=true', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=ios&dev=true', { idleTimeout: undefined }) expect(mockedRequest).toHaveBeenCalledTimes(1) expect(mockedRequest).toHaveBeenCalledWith( @@ -532,7 +542,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params for iOS sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) }) @@ -561,8 +572,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params with cus }) expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/cool-app.js.map?platform=ios&dev=false') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/cool-app.bundle?platform=ios&dev=false') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/cool-app.js.map?platform=ios&dev=false', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/cool-app.bundle?platform=ios&dev=false', { idleTimeout: undefined }) expect(mockedRequest).toHaveBeenCalledTimes(1) expect(mockedRequest).toHaveBeenCalledWith( @@ -576,7 +587,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params with cus sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining( @@ -609,8 +621,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params with cus }) expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/cool-app.js.map?platform=ios&dev=false') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/cool-app.bundle?platform=ios&dev=false') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/cool-app.js.map?platform=ios&dev=false', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/cool-app.bundle?platform=ios&dev=false', { idleTimeout: undefined }) expect(mockedRequest).toHaveBeenCalledTimes(1) expect(mockedRequest).toHaveBeenCalledWith( @@ -624,7 +636,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params with cus sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining( @@ -657,8 +670,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params with cus }) expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/cool-app.js.map?platform=ios&dev=false') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/cool-app.bundle?platform=ios&dev=false') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/cool-app.js.map?platform=ios&dev=false', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/cool-app.bundle?platform=ios&dev=false', { idleTimeout: undefined }) expect(mockedRequest).toHaveBeenCalledTimes(1) expect(mockedRequest).toHaveBeenCalledWith( @@ -672,7 +685,8 @@ test('fetchAndUploadOne(): dispatches a request with the correct params with cus sourceMap: expect.any(File), bundle: expect.any(File), }), - expect.objectContaining({}) + {}, + { idleTimeout: undefined } ) expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining( @@ -703,7 +717,7 @@ test('fetchAndUploadOne(): Fetch mode failure to get source map (generic Error)' }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(1) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('An unexpected error occurred during the request to http://react-native-bundler:1234'), @@ -735,7 +749,7 @@ test('fetchAndUploadOne(): Fetch mode failure to get source map (generic Network }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(1) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('An unexpected error occurred during the request to http://react-native-bundler:1234'), @@ -769,7 +783,7 @@ test('fetchAndUploadOne(): Fetch mode failure to get source map (connection refu }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(1) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('Unable to connect to http://react-native-bundler:1234. Is the server running?'), @@ -803,7 +817,7 @@ test('fetchAndUploadOne(): Fetch mode failure to get source map (server error)', }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(1) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining("Received an error from the server at http://react-native-bundler:1234. Does the entry point file 'index.js' exist?"), @@ -837,7 +851,7 @@ test('fetchAndUploadOne(): Fetch mode failure to get source map (timeout)', asyn }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(1) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('The request to http://react-native-bundler:1234 timed out.'), @@ -873,8 +887,8 @@ test('fethchAndUploadOne(): Fetch mode failure to get bundle (generic Error)', a }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('An unexpected error occurred during the request to http://react-native-bundler:1234'), @@ -910,8 +924,8 @@ test('fetchAndUploadOne(): Fetch mode failure to get bundle (generic NetworkErro }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('An unexpected error occurred during the request to http://react-native-bundler:1234'), @@ -949,8 +963,8 @@ test('fetchAndUploadOne(): Fetch mode failure to get bundle (connection refused) }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('Unable to connect to http://react-native-bundler:1234. Is the server running?'), @@ -988,8 +1002,8 @@ test('fetchAndUploadOne(): Fetch mode failure to get bundle (server error)', asy }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining("Received an error from the server at http://react-native-bundler:1234. Does the entry point file 'index.js' exist?"), @@ -1027,8 +1041,8 @@ test('fetchAndUploadOne(): Fetch mode failure to get bundle (timeout)', async () }) } catch (e) { expect(mockedFetch).toHaveBeenCalledTimes(2) - expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true') - expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true') + expect(mockedFetch).toHaveBeenNthCalledWith(1, 'http://react-native-bundler:1234/index.js.map?platform=android&dev=true', { idleTimeout: undefined }) + expect(mockedFetch).toHaveBeenNthCalledWith(2, 'http://react-native-bundler:1234/index.bundle?platform=android&dev=true', { idleTimeout: undefined }) expect(mockedRequest).not.toHaveBeenCalled() expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining('The request to http://react-native-bundler:1234 timed out.'), From 710bd24fef6d4c996e74e2955bbbc9c7be237573 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 28 Jul 2021 10:53:59 +0100 Subject: [PATCH 2/3] Add Maze Runner tests for --idle-timeout --- features/browser-upload-multiple.feature | 23 +++++++++++++++++++++ features/browser-upload-one.feature | 26 +++++++++++++++++++++++- features/node-upload-multiple.feature | 22 ++++++++++++++++++++ features/node-upload-one.feature | 23 +++++++++++++++++++++ features/react-native-fetch.feature | 21 +++++++++++++++++++ features/react-native-upload-one.feature | 23 +++++++++++++++++++++ 6 files changed, 137 insertions(+), 1 deletion(-) diff --git a/features/browser-upload-multiple.feature b/features/browser-upload-multiple.feature index d947fea..73e9f69 100644 --- a/features/browser-upload-multiple.feature +++ b/features/browser-upload-multiple.feature @@ -343,3 +343,26 @@ Feature: Browser source map upload multiple And the sourcemap payload field "sourceMap" matches the source map "main-b3944033.json" for "multiple-source-map-webpack" And the sourcemap payload field "minifiedFile" matches the minified file "main-b3944033.js" for "multiple-source-map-webpack" And the Content-Type header is valid multipart form-data + + Scenario: A request can set a timeout using the --idle-timeout flag + When I set the response delay to 5000 milliseconds + And I run the service "multiple-source-map-webpack" with the command + """ + bugsnag-source-maps upload-browser + --api-key 123 + --app-version 2.0.0 + --directory dist + --base-url "http://myapp.url/static/js/" + --endpoint http://maze-runner:9339 + --idle-timeout 0.0008 # approx 50ms in minutes + """ + Then the last run docker command did not exit successfully + And the last run docker command output "The request timed out." + When I wait to receive 5 sourcemaps + Then the Content-Type header is valid multipart form-data for all requests + And the sourcemap payload field "apiKey" equals "123" for all requests + And the sourcemap payload field "appVersion" equals "2.0.0" for all requests + And the sourcemap payload field "overwrite" is null for all requests + And the sourcemap payload field "minifiedUrl" equals "http://myapp.url/static/js/main-b3944033.js" for all requests + And the sourcemap payload field "sourceMap" matches the source map "main-b3944033.json" for "multiple-source-map-webpack" for all requests + And the sourcemap payload field "minifiedFile" matches the minified file "main-b3944033.js" for "multiple-source-map-webpack" for all requests diff --git a/features/browser-upload-one.feature b/features/browser-upload-one.feature index bfd5d25..0122304 100644 --- a/features/browser-upload-one.feature +++ b/features/browser-upload-one.feature @@ -164,7 +164,7 @@ Feature: Browser source map upload one --bundle-url "http://myapp.url/static/js/main.js" --endpoint http://maze-runner:9339 """ - And I wait to receive 5 sourcemap + And I wait to receive 5 sourcemaps Then the last run docker command did not exit successfully And the last run docker command output "A server side error occurred while processing the upload." And the last run docker command output "HTTP status 500 received from upload API" @@ -271,3 +271,27 @@ Feature: Browser source map upload one And the sourcemap payload field "sourceMap" matches the expected source map for "single-source-map-webpack" And the sourcemap payload field "minifiedFile" matches the expected minified file for "single-source-map-webpack" And the Content-Type header is valid multipart form-data + + Scenario: A request can set a timeout using the --idle-timeout flag + When I set the response delay to 5000 milliseconds + And I run the service "single-source-map-webpack" with the command + """ + bugsnag-source-maps upload-browser + --api-key 123 + --app-version 2.0.0 + --source-map dist/main.js.map + --bundle dist/main.js + --bundle-url "http://myapp.url/static/js/main.js" + --endpoint http://maze-runner:9339 + --idle-timeout 0.0008 # approx 50ms in minutes + """ + Then the last run docker command did not exit successfully + And the last run docker command output "The request timed out." + When I wait to receive 5 sourcemaps + And the Content-Type header is valid multipart form-data for all requests + And the sourcemap payload field "apiKey" equals "123" for all requests + And the sourcemap payload field "appVersion" equals "2.0.0" for all requests + And the sourcemap payload field "overwrite" is null for all requests + And the sourcemap payload field "minifiedUrl" equals "http://myapp.url/static/js/main.js" for all requests + And the sourcemap payload field "sourceMap" matches the expected source map for "single-source-map-webpack" for all requests + And the sourcemap payload field "minifiedFile" matches the expected minified file for "single-source-map-webpack" for all requests diff --git a/features/node-upload-multiple.feature b/features/node-upload-multiple.feature index 22fc03a..3c7ad9c 100644 --- a/features/node-upload-multiple.feature +++ b/features/node-upload-multiple.feature @@ -330,3 +330,25 @@ Feature: Node source map upload multiple And the sourcemap payload field "sourceMap" matches the source map "main-b3944033.json" for "multiple-source-map-webpack" And the sourcemap payload field "minifiedFile" matches the minified file "main-b3944033.js" for "multiple-source-map-webpack" And the Content-Type header is valid multipart form-data + + Scenario: A request can set a timeout using the --idle-timeout flag + When I set the response delay to 5000 milliseconds + And I run the service "multiple-source-map-webpack" with the command + """ + bugsnag-source-maps upload-node + --api-key 123 + --app-version 2.0.0 + --directory dist + --endpoint http://maze-runner:9339 + --idle-timeout 0.0008 # approx 50ms in minutes + """ + Then the last run docker command did not exit successfully + And the last run docker command output "The request timed out." + When I wait to receive 5 sourcemaps + And the Content-Type header is valid multipart form-data for all requests + And the sourcemap payload field "apiKey" equals "123" for all requests + And the sourcemap payload field "appVersion" equals "2.0.0" for all requests + And the sourcemap payload field "overwrite" is null for all requests + And the sourcemap payload field "minifiedUrl" equals "dist/main-b3944033.js" for all requests + And the sourcemap payload field "sourceMap" matches the source map "main-b3944033.json" for "multiple-source-map-webpack" for all requests + And the sourcemap payload field "minifiedFile" matches the minified file "main-b3944033.js" for "multiple-source-map-webpack" for all requests diff --git a/features/node-upload-one.feature b/features/node-upload-one.feature index df722dc..3e0c04c 100644 --- a/features/node-upload-one.feature +++ b/features/node-upload-one.feature @@ -255,3 +255,26 @@ Feature: Node source map upload one And the sourcemap payload field "sourceMap" matches the expected source map for "single-source-map-webpack" And the sourcemap payload field "minifiedFile" matches the expected minified file for "single-source-map-webpack" And the Content-Type header is valid multipart form-data + + Scenario: A request can set a timeout using the --idle-timeout flag + When I set the response delay to 5000 milliseconds + And I run the service "single-source-map-webpack" with the command + """ + bugsnag-source-maps upload-node + --api-key 123 + --app-version 2.0.0 + --source-map dist/main.js.map + --bundle dist/main.js + --endpoint http://maze-runner:9339 + --idle-timeout 0.0008 # approx 50ms in minutes + """ + Then the last run docker command did not exit successfully + And the last run docker command output "The request timed out." + When I wait to receive 5 sourcemaps + And the Content-Type header is valid multipart form-data for all requests + And the sourcemap payload field "apiKey" equals "123" for all requests + And the sourcemap payload field "appVersion" equals "2.0.0" for all requests + And the sourcemap payload field "overwrite" is null for all requests + And the sourcemap payload field "minifiedUrl" equals "dist/main.js" for all requests + And the sourcemap payload field "sourceMap" matches the expected source map for "single-source-map-webpack" for all requests + And the sourcemap payload field "minifiedFile" matches the expected minified file for "single-source-map-webpack" for all requests diff --git a/features/react-native-fetch.feature b/features/react-native-fetch.feature index d75af6e..ac86adc 100644 --- a/features/react-native-fetch.feature +++ b/features/react-native-fetch.feature @@ -190,3 +190,24 @@ Feature: React native source map fetch mode And the sourcemap payload field "sourceMap" matches the expected source map for "fetch-react-native-0-60-ios" And the sourcemap payload field "bundle" matches the expected bundle for "fetch-react-native-0-60-ios" And the Content-Type header is valid multipart form-data + + Scenario: A request can set a timeout using the --idle-timeout flag + When I start the service "react-native-0-60-bundler" + And I wait for 2 seconds + And I set the response delay to 5000 milliseconds + And I run the service "react-native-0-60-fetch" with the command + """ + bugsnag-source-maps upload-react-native + --api-key 123 + --app-version 2.0.0 + --endpoint http://maze-runner:9339 + --fetch + --bundler-url http://react-native-0-60-bundler:9449 + --platform ios + --idle-timeout 0.0008 # approx 50ms in minutes + """ + Then the last run docker command did not exit successfully + And the last run docker command output "The request to http://react-native-0-60-bundler:9449 timed out." + # this is fetch mode so no sourcemaps should be uploaded because the request + # to the RN bundle server should timeout due to the idle-timeout + And I should receive no requests diff --git a/features/react-native-upload-one.feature b/features/react-native-upload-one.feature index b58a83e..08b4a43 100644 --- a/features/react-native-upload-one.feature +++ b/features/react-native-upload-one.feature @@ -176,3 +176,26 @@ Feature: React native source map upload one And the sourcemap payload field "sourceMap" matches the expected source map for "single-source-map-react-native-0-60-ios" And the sourcemap payload field "bundle" matches the expected bundle for "single-source-map-react-native-0-60-ios" And the Content-Type header is valid multipart form-data + + Scenario: A request will be retried up to 5 times on a server failure (500 status code) + When I set the response delay to 5000 milliseconds + And I run the service "single-source-map-react-native-0-60-ios" with the command + """ + bugsnag-source-maps upload-react-native + --api-key 123 + --app-version 2.0.0 + --endpoint http://maze-runner:9339 + --source-map build/source-map.json + --bundle build/bundle.js + --platform ios + --idle-timeout 0.0008 # approx 50ms in minutes + """ + Then the last run docker command did not exit successfully + And the last run docker command output "The request timed out." + When I wait to receive 5 sourcemaps + And the Content-Type header is valid multipart form-data for all requests + And the sourcemap payload field "apiKey" equals "123" for all requests + And the sourcemap payload field "appVersion" equals "2.0.0" for all requests + And the sourcemap payload field "overwrite" equals "true" for all requests + And the sourcemap payload field "sourceMap" matches the expected source map for "single-source-map-react-native-0-60-ios" for all requests + And the sourcemap payload field "bundle" matches the expected bundle for "single-source-map-react-native-0-60-ios" for all requests From bffb7ff973c1ffb71537ad255ae3d79aa651752b Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 28 Jul 2021 10:54:32 +0100 Subject: [PATCH 3/3] Increase the default timeout to 10 minutes --- src/Request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Request.ts b/src/Request.ts index cd440f9..0c26213 100644 --- a/src/Request.ts +++ b/src/Request.ts @@ -43,7 +43,7 @@ interface RequestOptions { const MAX_ATTEMPTS = 5 const RETRY_INTERVAL_MS = parseInt(process.env.BUGSNAG_RETRY_INTERVAL_MS as string) || 1000 -const DEFAULT_TIMEOUT_MS = parseInt(process.env.BUGSNAG_TIMEOUT_MS as string) || 30000 +const DEFAULT_TIMEOUT_MS = parseInt(process.env.BUGSNAG_TIMEOUT_MS as string) || 60000 export default async function request ( endpoint: string,