diff --git a/package.json b/package.json index 67ba9a1..edee8f6 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "linkinator": "^1.5.0", "mocha": "^6.1.4", "mockery": "^2.1.0", - "nock": "^10.0.0", + "nock": "^11.0.0", "sinon": "^7.3.2", "source-map-support": "^0.5.6", "typescript": "~3.5.0" diff --git a/src/index.ts b/src/index.ts index 770fa09..0ddf074 100644 --- a/src/index.ts +++ b/src/index.ts @@ -137,6 +137,9 @@ export interface UploadConfig { } export interface ConfigMetadata { + // tslint:disable-next-line no-any + [key: string]: any; + /** * Set the length of the file being uploaded. */ @@ -505,21 +508,18 @@ export class Upload extends Pumpify { reqOpts.params = reqOpts.params || {}; reqOpts.params.userProject = this.userProject; } - reqOpts.validateStatus = () => true; + // Let gaxios know we will handle a 308 error code ourselves. + reqOpts.validateStatus = (status: number) => { + return ( + (status >= 200 && status < 300) || + status === RESUMABLE_INCOMPLETE_STATUS_CODE + ); + }; const res = await this.authClient.request(reqOpts); if (res.data && res.data.error) { throw res.data.error; } - // If no error was returned, but the response had an invalid status - // code, create a new error to be passed to the callback. - if ( - (res.status < 200 || res.status >= 300) && - res.status !== RESUMABLE_INCOMPLETE_STATUS_CODE - ) { - const e = new Error(`The request failed with a ${res.status}.`); - (e as ErrorWithCode).code = res.status; - } return res; } diff --git a/system-test/kitchen.ts b/system-test/kitchen.ts index 0692c03..c7d5d5a 100644 --- a/system-test/kitchen.ts +++ b/system-test/kitchen.ts @@ -8,19 +8,24 @@ import * as assert from 'assert'; import * as fs from 'fs'; import {Readable} from 'stream'; -import {createURI, upload} from '../src'; +import {createURI, ErrorWithCode, upload} from '../src'; const bucketName = process.env.BUCKET_NAME || 'gcs-resumable-upload-test'; +const fileName = 'daw.jpg'; describe('end to end', () => { + beforeEach(() => { + upload({bucket: bucketName, file: fileName}).deleteConfig(); + }); + it('should work', done => { let uploadSucceeded = false; - fs.createReadStream('daw.jpg') + fs.createReadStream(fileName) .on('error', done) .pipe( upload({ bucket: bucketName, - file: 'daw.jpg', + file: fileName, metadata: {contentType: 'image/jpg'}, }) ) @@ -35,7 +40,7 @@ describe('end to end', () => { }); it('should resume an interrupted upload', done => { - fs.stat('daw.jpg', (err, fd) => { + fs.stat(fileName, (err, fd) => { assert.ifError(err); const size = fd.size; @@ -51,11 +56,11 @@ describe('end to end', () => { const ws = upload({ bucket: bucketName, - file: 'daw.jpg', + file: fileName, metadata: {contentType: 'image/jpg'}, }); - fs.createReadStream('daw.jpg') + fs.createReadStream(fileName) .on('error', callback) .on('data', function(this: Readable, chunk) { sizeStreamed += chunk.length; @@ -88,10 +93,30 @@ describe('end to end', () => { createURI( { bucket: bucketName, - file: 'daw.jpg', + file: fileName, metadata: {contentType: 'image/jpg'}, }, done ); }); + + it('should return a non-resumable failed upload', done => { + const metadata = { + metadata: {largeString: 'a'.repeat(2.1e6)}, + }; + + fs.createReadStream(fileName) + .on('error', done) + .pipe( + upload({ + bucket: bucketName, + file: fileName, + metadata, + }) + ) + .on('error', (err: ErrorWithCode) => { + assert.strictEqual(err.code, '400'); + done(); + }); + }); }); diff --git a/test/test.ts b/test/test.ts index 2aefab3..d6f2b6f 100644 --- a/test/test.ts +++ b/test/test.ts @@ -841,13 +841,25 @@ describe('gcs-resumable-upload', () => { scopes.forEach(x => x.done()); }); - it('should execute the callback with error & response if one occurred', async () => { - const scope = mockAuthorizeRequest(500, ':('); - await assertRejects( - up.makeRequest({}), - /Request failed with status code 500/ - ); - scope.done(); + it('should set validate status', done => { + up.authClient = { + request: (reqOpts: GaxiosOptions) => { + assert.strictEqual(reqOpts.validateStatus!(100), false); + assert.strictEqual(reqOpts.validateStatus!(199), false); + assert.strictEqual(reqOpts.validateStatus!(300), false); + assert.strictEqual(reqOpts.validateStatus!(400), false); + assert.strictEqual(reqOpts.validateStatus!(500), false); + + assert.strictEqual(reqOpts.validateStatus!(200), true); + assert.strictEqual(reqOpts.validateStatus!(299), true); + assert.strictEqual(reqOpts.validateStatus!(308), true); + + done(); + + return {}; + }, + }; + up.makeRequest(REQ_OPTS); }); it('should make the correct request', async () => { @@ -863,17 +875,6 @@ describe('gcs-resumable-upload', () => { assert.deepStrictEqual(res.headers, {}); }); - it('should execute the callback with error & response', async () => { - const response = {body: 'wooo'}; - mockAuthorizeRequest(); - const scope = nock(REQ_OPTS.url!) - .get(queryPath) - .reply(500, response.body); - const resp = await up.makeRequest(REQ_OPTS); - assert.strictEqual(resp.data, response.body); - scope.done(); - }); - it('should execute the callback with a body error & response', async () => { const error = new GaxiosError( 'Error message',