From dc4a2374b7084e719a05b3b66d8a7e97886884de Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:03:50 -0500 Subject: [PATCH 01/17] Modify description and typescript --- src/Adapters/Files/FilesAdapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js index 57f816521b..8f15fcbac1 100644 --- a/src/Adapters/Files/FilesAdapter.js +++ b/src/Adapters/Files/FilesAdapter.js @@ -30,13 +30,14 @@ export class FilesAdapter { * @param {string} contentType - the supposed contentType * @discussion the contentType can be undefined if the controller was not able to determine it * @param {object} options - (Optional) options to be passed to file adapter (S3 File Adapter Only) + * @param {Config} config -(Optional) server configuration * - tags: object containing key value pairs that will be stored with file * - metadata: object containing key value pairs that will be sotred with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html) * @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility * - * @return {Promise} a promise that should fail if the storage didn't succeed + * @return {Promise|Promise<{url?: string, name?: string}>} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename */ - createFile(filename: string, data, contentType: string, options: Object): Promise {} + createFile(filename: string, data, contentType: string, options: Object, config: Config): Promise {} /** Whether this adapter supports receiving Readable streams in createFile(). * If false (default), streams are buffered to a Buffer before being passed. From 1793ac3d5441aa583ddebc2978e8973f31c69f55 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:06:12 -0500 Subject: [PATCH 02/17] Add optional location element too --- src/Adapters/Files/FilesAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js index 8f15fcbac1..6ba8bc488d 100644 --- a/src/Adapters/Files/FilesAdapter.js +++ b/src/Adapters/Files/FilesAdapter.js @@ -35,7 +35,7 @@ export class FilesAdapter { * - metadata: object containing key value pairs that will be sotred with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html) * @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility * - * @return {Promise|Promise<{url?: string, name?: string}>} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename + * @return {Promise|Promise<{url?: string, name?: string, location?: string}>} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename and/or location (if relevant) */ createFile(filename: string, data, contentType: string, options: Object, config: Config): Promise {} From 638e99096daaceb9d7003a5f430d38369a031635 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:17:35 -0500 Subject: [PATCH 03/17] Update FilesAdapter.js --- src/Adapters/Files/FilesAdapter.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js index 6ba8bc488d..196d9b3186 100644 --- a/src/Adapters/Files/FilesAdapter.js +++ b/src/Adapters/Files/FilesAdapter.js @@ -30,10 +30,11 @@ export class FilesAdapter { * @param {string} contentType - the supposed contentType * @discussion the contentType can be undefined if the controller was not able to determine it * @param {object} options - (Optional) options to be passed to file adapter (S3 File Adapter Only) - * @param {Config} config -(Optional) server configuration * - tags: object containing key value pairs that will be stored with file - * - metadata: object containing key value pairs that will be sotred with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html) - * @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility + * - metadata: object containing key value pairs that will be stored with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html) + * @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility + * @param {Config} config -(Optional) server configuration + * @discussion config is not supported by all file adapters. Check the your adapter's documentation for compatibility * * @return {Promise|Promise<{url?: string, name?: string, location?: string}>} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename and/or location (if relevant) */ From 0084ce49a073c5d4d60d8d5fc9a4a62b3a305d82 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:26:50 -0500 Subject: [PATCH 04/17] Remove trialing space --- src/Adapters/Files/FilesAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js index 196d9b3186..9655e037ff 100644 --- a/src/Adapters/Files/FilesAdapter.js +++ b/src/Adapters/Files/FilesAdapter.js @@ -32,7 +32,7 @@ export class FilesAdapter { * @param {object} options - (Optional) options to be passed to file adapter (S3 File Adapter Only) * - tags: object containing key value pairs that will be stored with file * - metadata: object containing key value pairs that will be stored with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html) - * @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility + * @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility * @param {Config} config -(Optional) server configuration * @discussion config is not supported by all file adapters. Check the your adapter's documentation for compatibility * From 090f8587167988703857aa303deabe47e41e4659 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:33:58 -0500 Subject: [PATCH 05/17] change to undefined as a fallback --- src/Adapters/Files/FilesAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js index 9655e037ff..ddafa34063 100644 --- a/src/Adapters/Files/FilesAdapter.js +++ b/src/Adapters/Files/FilesAdapter.js @@ -36,7 +36,7 @@ export class FilesAdapter { * @param {Config} config -(Optional) server configuration * @discussion config is not supported by all file adapters. Check the your adapter's documentation for compatibility * - * @return {Promise|Promise<{url?: string, name?: string, location?: string}>} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename and/or location (if relevant) + * @return {Promise<{url?: string, name?: string, location?: string}>|Promise} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename and/or location (if relevant) */ createFile(filename: string, data, contentType: string, options: Object, config: Config): Promise {} From bfc413a9007be23b56ea6bb7286412ffdc93af5c Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:48:38 -0500 Subject: [PATCH 06/17] Add tests for different return conditions --- spec/FilesController.spec.js | 107 +++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 30acf7d13c..2792290c58 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -218,4 +218,111 @@ describe('FilesController', () => { expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); + + it('should return valid filename or url from createFile response when provided', async () => { + const config = Config.get(Parse.applicationId); + + // Test case 1: adapter returns new filename and url + const adapterWithReturn = { + createFile: () => { + return Promise.resolve({ + name: 'newfilename.txt', + url: 'http://new.url/newfilename.txt' + }); + }, + getFileLocation: () => { + return Promise.resolve('http://default.url/file.txt'); + }, + validateFilename: () => null + }; + + const controllerWithReturn = new FilesController(adapterWithReturn); + const result1 = await controllerWithReturn.createFile( + config, + 'originalfile.txt', + 'data', + 'text/plain' + ); + + expect(result1.name).toBe('newfilename.txt'); + expect(result1.url).toBe('http://new.url/newfilename.txt'); + + // Test case 2: adapter returns nothing, falling back to default behavior + const adapterWithoutReturn = { + createFile: () => { + return Promise.resolve(); + }, + getFileLocation: (config, filename) => { + return Promise.resolve(`http://default.url/${filename}`); + }, + validateFilename: () => null + }; + + const controllerWithoutReturn = new FilesController(adapterWithoutReturn); + const result2 = await controllerWithoutReturn.createFile( + config, + 'originalfile.txt', + 'data', + 'text/plain', + {}, + { preserveFileName: true } // To make filename predictable + ); + + expect(result2.name).toBe('originalfile.txt'); + expect(result2.url).toBe('http://default.url/originalfile.txt'); + + // Test case 3: adapter returns partial info (only url) + // This is a valid scenario, as the adapter may return a modified filename + // but may result in a mismatch between the filename and the resource URL + const adapterWithOnlyURL = { + createFile: () => { + return Promise.resolve({ + url: 'http://new.url/partialfile.txt' + }); + }, + getFileLocation: () => { + return Promise.resolve('http://default.url/file.txt'); + }, + validateFilename: () => null + }; + + const controllerWithPartial = new FilesController(adapterWithOnlyURL); + const result3 = await controllerWithPartial.createFile( + config, + 'originalfile.txt', + 'data', + 'text/plain', + {}, + { preserveFileName: true } // To make filename predictable + ); + + expect(result3.name).toBe('originalfile.txt'); + expect(result3.url).toBe('http://new.url/partialfile.txt'); // Technically, the resource does not need to match the filename + + // Test case 4: adapter returns only filename + const adapterWithOnlyFilename = { + createFile: () => { + return Promise.resolve({ + name: 'newname.txt' + }); + }, + getFileLocation: (config, filename) => { + return Promise.resolve(`http://default.url/${filename}`); + }, + validateFilename: () => null + }; + + const controllerWithOnlyFilename = new FilesController(adapterWithOnlyFilename); + const result4 = await controllerWithOnlyFilename.createFile( + config, + 'originalfile.txt', + 'data', + 'text/plain', + {}, + { preserveFileName: true } + ); + + expect(result4.name).toBe('newname.txt'); + expect(result4.url).toBe('http://default.url/newname.txt'); + }); }); From 711930decfa0c2d05adfc48042212e6207f0caea Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:51:59 -0500 Subject: [PATCH 07/17] Update description of config slightly --- src/Adapters/Files/FilesAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js index ddafa34063..3949705fad 100644 --- a/src/Adapters/Files/FilesAdapter.js +++ b/src/Adapters/Files/FilesAdapter.js @@ -33,8 +33,8 @@ export class FilesAdapter { * - tags: object containing key value pairs that will be stored with file * - metadata: object containing key value pairs that will be stored with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html) * @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility - * @param {Config} config -(Optional) server configuration - * @discussion config is not supported by all file adapters. Check the your adapter's documentation for compatibility + * @param {Config} config - (Optional) server configuration + * @discussion config may be passed to adapter to allow for more complex configuration and internal call of getFileLocation (if needed). This argument is not supported by all file adapters. Check the your adapter's documentation for compatibility * * @return {Promise<{url?: string, name?: string, location?: string}>|Promise} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename and/or location (if relevant) */ From af4cf362c08ebd4d5e28f7d10400f58d6864b310 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:58:30 -0500 Subject: [PATCH 08/17] Modify mock adapter initialization to use struct in beginning --- spec/FilesController.spec.js | 96 ++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 2792290c58..71453523e3 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -221,101 +221,91 @@ describe('FilesController', () => { it('should return valid filename or url from createFile response when provided', async () => { const config = Config.get(Parse.applicationId); - + // Test case 1: adapter returns new filename and url - const adapterWithReturn = { - createFile: () => { - return Promise.resolve({ - name: 'newfilename.txt', - url: 'http://new.url/newfilename.txt' - }); - }, - getFileLocation: () => { - return Promise.resolve('http://default.url/file.txt'); - }, - validateFilename: () => null + const adapterWithReturn = { ...mockAdapter }; + adapterWithReturn.createFile = () => { + return Promise.resolve({ + name: 'newFilename.txt', + url: 'http://new.url/newFilename.txt' + }); + }; + adapterWithReturn.getFileLocation = () => { + return Promise.resolve('http://default.url/file.txt'); }; - const controllerWithReturn = new FilesController(adapterWithReturn); const result1 = await controllerWithReturn.createFile( config, - 'originalfile.txt', + 'originalFile.txt', 'data', 'text/plain' ); - - expect(result1.name).toBe('newfilename.txt'); - expect(result1.url).toBe('http://new.url/newfilename.txt'); + expect(result1.name).toBe('newFilename.txt'); + expect(result1.url).toBe('http://new.url/newFilename.txt'); // Test case 2: adapter returns nothing, falling back to default behavior - const adapterWithoutReturn = { - createFile: () => { - return Promise.resolve(); - }, - getFileLocation: (config, filename) => { - return Promise.resolve(`http://default.url/${filename}`); - }, - validateFilename: () => null + const adapterWithoutReturn = { ...mockAdapter }; + adapterWithoutReturn.createFile = () => { + return Promise.resolve(); }; - + adapterWithoutReturn.getFileLocation = (config, filename) => { + return Promise.resolve(`http://default.url/${filename}`); + }; + const controllerWithoutReturn = new FilesController(adapterWithoutReturn); const result2 = await controllerWithoutReturn.createFile( config, - 'originalfile.txt', + 'originalFile.txt', 'data', 'text/plain', {}, { preserveFileName: true } // To make filename predictable ); - expect(result2.name).toBe('originalfile.txt'); - expect(result2.url).toBe('http://default.url/originalfile.txt'); + expect(result2.name).toBe('originalFile.txt'); + expect(result2.url).toBe('http://default.url/originalFile.txt'); // Test case 3: adapter returns partial info (only url) // This is a valid scenario, as the adapter may return a modified filename // but may result in a mismatch between the filename and the resource URL - const adapterWithOnlyURL = { - createFile: () => { - return Promise.resolve({ - url: 'http://new.url/partialfile.txt' - }); - }, - getFileLocation: () => { - return Promise.resolve('http://default.url/file.txt'); - }, - validateFilename: () => null + const adapterWithOnlyURL = { ...mockAdapter }; + adapterWithOnlyURL.createFile = () => { + return Promise.resolve({ + url: 'http://new.url/partialFile.txt' + }); + }; + adapterWithOnlyURL.getFileLocation = () => { + return Promise.resolve('http://default.url/file.txt'); }; const controllerWithPartial = new FilesController(adapterWithOnlyURL); const result3 = await controllerWithPartial.createFile( config, - 'originalfile.txt', + 'originalFile.txt', 'data', 'text/plain', {}, { preserveFileName: true } // To make filename predictable ); - expect(result3.name).toBe('originalfile.txt'); - expect(result3.url).toBe('http://new.url/partialfile.txt'); // Technically, the resource does not need to match the filename + expect(result3.name).toBe('originalFile.txt'); + expect(result3.url).toBe('http://new.url/partialFile.txt'); // Technically, the resource does not need to match the filename // Test case 4: adapter returns only filename - const adapterWithOnlyFilename = { - createFile: () => { - return Promise.resolve({ - name: 'newname.txt' - }); - }, - getFileLocation: (config, filename) => { - return Promise.resolve(`http://default.url/${filename}`); - }, - validateFilename: () => null + const adapterWithOnlyFilename = { ...mockAdapter }; + adapterWithOnlyFilename.createFile = () => { + return Promise.resolve({ + name: 'newname.txt' + }); + }; + adapterWithOnlyFilename.getFileLocation = (config, filename) => { + return Promise.resolve(`http://default.url/${filename}`); }; const controllerWithOnlyFilename = new FilesController(adapterWithOnlyFilename); const result4 = await controllerWithOnlyFilename.createFile( config, - 'originalfile.txt', + 'originalFile.txt', 'data', 'text/plain', {}, From e13f0238533640bcba431633203758a625636e9d Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:08:42 -0500 Subject: [PATCH 09/17] Update to include config in arguments for createFileSpy --- spec/CloudCode.spec.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 941d896aae..dcc9f52ec4 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -4144,11 +4144,13 @@ describe('saveFile hooks', () => { foo: 'bar', }, }; + const config = Config.get('test'); expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, 'text/plain', - newOptions + newOptions, + config ); }); @@ -4176,11 +4178,13 @@ describe('saveFile hooks', () => { foo: 'bar', }, }; + const config = Config.get('test'); expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, newContentType, - newOptions + newOptions, + config ); const expectedFileName = 'donald_duck.pdf'; expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length); @@ -4206,11 +4210,13 @@ describe('saveFile hooks', () => { metadata: { foo: 'bar' }, tags: { bar: 'foo' }, }; + const config = Config.get('test'); expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Buffer), 'text/plain', - options + options, + config ); }); From e23193b4238d010074ac24ef2c51ed3584a0d833 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Thu, 6 Feb 2025 20:46:14 -0500 Subject: [PATCH 10/17] Remove space + move preserve filename args to files controller initializer --- spec/FilesController.spec.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 71453523e3..b0442e74b5 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -233,7 +233,8 @@ describe('FilesController', () => { adapterWithReturn.getFileLocation = () => { return Promise.resolve('http://default.url/file.txt'); }; - const controllerWithReturn = new FilesController(adapterWithReturn); + const controllerWithReturn = new FilesController(adapterWithReturn, null, { preserveFileName: true }); + // preserveFileName is true to make filename behaviors predictable const result1 = await controllerWithReturn.createFile( config, 'originalFile.txt', @@ -252,14 +253,13 @@ describe('FilesController', () => { return Promise.resolve(`http://default.url/${filename}`); }; - const controllerWithoutReturn = new FilesController(adapterWithoutReturn); + const controllerWithoutReturn = new FilesController(adapterWithoutReturn, null, { preserveFileName: true }); const result2 = await controllerWithoutReturn.createFile( config, 'originalFile.txt', 'data', 'text/plain', - {}, - { preserveFileName: true } // To make filename predictable + {} ); expect(result2.name).toBe('originalFile.txt'); @@ -278,14 +278,13 @@ describe('FilesController', () => { return Promise.resolve('http://default.url/file.txt'); }; - const controllerWithPartial = new FilesController(adapterWithOnlyURL); + const controllerWithPartial = new FilesController(adapterWithOnlyURL, null, { preserveFileName: true }); const result3 = await controllerWithPartial.createFile( config, 'originalFile.txt', 'data', 'text/plain', - {}, - { preserveFileName: true } // To make filename predictable + {} ); expect(result3.name).toBe('originalFile.txt'); @@ -302,14 +301,13 @@ describe('FilesController', () => { return Promise.resolve(`http://default.url/${filename}`); }; - const controllerWithOnlyFilename = new FilesController(adapterWithOnlyFilename); + const controllerWithOnlyFilename = new FilesController(adapterWithOnlyFilename, null, { preserveFileName: true }); const result4 = await controllerWithOnlyFilename.createFile( config, 'originalFile.txt', 'data', 'text/plain', - {}, - { preserveFileName: true } + {} ); expect(result4.name).toBe('newname.txt'); From 0b3084c99351f76f26c2fca3e9315e7da0ce924b Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:08:19 -0400 Subject: [PATCH 11/17] Split into separate test --- spec/FilesController.spec.js | 55 +++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index b0442e74b5..49c91d98d9 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -219,10 +219,8 @@ describe('FilesController', () => { done(); }); - it('should return valid filename or url from createFile response when provided', async () => { + it('should return filename and url when adapter returns both', async () => { const config = Config.get(Parse.applicationId); - - // Test case 1: adapter returns new filename and url const adapterWithReturn = { ...mockAdapter }; adapterWithReturn.createFile = () => { return Promise.resolve({ @@ -234,17 +232,20 @@ describe('FilesController', () => { return Promise.resolve('http://default.url/file.txt'); }; const controllerWithReturn = new FilesController(adapterWithReturn, null, { preserveFileName: true }); - // preserveFileName is true to make filename behaviors predictable - const result1 = await controllerWithReturn.createFile( + + const result = await controllerWithReturn.createFile( config, 'originalFile.txt', 'data', 'text/plain' ); - expect(result1.name).toBe('newFilename.txt'); - expect(result1.url).toBe('http://new.url/newFilename.txt'); - // Test case 2: adapter returns nothing, falling back to default behavior + expect(result.name).toBe('newFilename.txt'); + expect(result.url).toBe('http://new.url/newFilename.txt'); + }); + + it('should use original filename and generate url when adapter returns nothing', async () => { + const config = Config.get(Parse.applicationId); const adapterWithoutReturn = { ...mockAdapter }; adapterWithoutReturn.createFile = () => { return Promise.resolve(); @@ -254,20 +255,20 @@ describe('FilesController', () => { }; const controllerWithoutReturn = new FilesController(adapterWithoutReturn, null, { preserveFileName: true }); - const result2 = await controllerWithoutReturn.createFile( + const result = await controllerWithoutReturn.createFile( config, 'originalFile.txt', 'data', 'text/plain', {} ); - - expect(result2.name).toBe('originalFile.txt'); - expect(result2.url).toBe('http://default.url/originalFile.txt'); - // Test case 3: adapter returns partial info (only url) - // This is a valid scenario, as the adapter may return a modified filename - // but may result in a mismatch between the filename and the resource URL + expect(result.name).toBe('originalFile.txt'); + expect(result.url).toBe('http://default.url/originalFile.txt'); + }); + + it('should use original filename when adapter returns only url', async () => { + const config = Config.get(Parse.applicationId); const adapterWithOnlyURL = { ...mockAdapter }; adapterWithOnlyURL.createFile = () => { return Promise.resolve({ @@ -277,20 +278,22 @@ describe('FilesController', () => { adapterWithOnlyURL.getFileLocation = () => { return Promise.resolve('http://default.url/file.txt'); }; - + const controllerWithPartial = new FilesController(adapterWithOnlyURL, null, { preserveFileName: true }); - const result3 = await controllerWithPartial.createFile( + const result = await controllerWithPartial.createFile( config, 'originalFile.txt', 'data', 'text/plain', {} ); - - expect(result3.name).toBe('originalFile.txt'); - expect(result3.url).toBe('http://new.url/partialFile.txt'); // Technically, the resource does not need to match the filename - // Test case 4: adapter returns only filename + expect(result.name).toBe('originalFile.txt'); + expect(result.url).toBe('http://new.url/partialFile.txt'); + }); + + it('should use adapter filename and generate url when adapter returns only filename', async () => { + const config = Config.get(Parse.applicationId); const adapterWithOnlyFilename = { ...mockAdapter }; adapterWithOnlyFilename.createFile = () => { return Promise.resolve({ @@ -300,17 +303,17 @@ describe('FilesController', () => { adapterWithOnlyFilename.getFileLocation = (config, filename) => { return Promise.resolve(`http://default.url/${filename}`); }; - + const controllerWithOnlyFilename = new FilesController(adapterWithOnlyFilename, null, { preserveFileName: true }); - const result4 = await controllerWithOnlyFilename.createFile( + const result = await controllerWithOnlyFilename.createFile( config, 'originalFile.txt', 'data', 'text/plain', {} ); - - expect(result4.name).toBe('newname.txt'); - expect(result4.url).toBe('http://default.url/newname.txt'); + + expect(result.name).toBe('newname.txt'); + expect(result.url).toBe('http://default.url/newname.txt'); }); }); From a1f4944d3176a1bb5b7b8a548a6dda016124a257 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:11:31 -0400 Subject: [PATCH 12/17] Limit the file adapter config to just the minimum it needs to get location Strip down the config variables that get sent along with the file --- spec/CloudCode.spec.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index dcc9f52ec4..349712f244 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -4145,12 +4145,17 @@ describe('saveFile hooks', () => { }, }; const config = Config.get('test'); + const expectedConfig = { + applicationId: config.applicationId, + mount: config.mount, + fileKey: config.fileKey + }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, 'text/plain', newOptions, - config + expectedConfig ); }); @@ -4179,12 +4184,17 @@ describe('saveFile hooks', () => { }, }; const config = Config.get('test'); + const expectedConfig = { + applicationId: config.applicationId, + mount: config.mount, + fileKey: config.fileKey + }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, newContentType, newOptions, - config + expectedConfig ); const expectedFileName = 'donald_duck.pdf'; expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length); @@ -4211,12 +4221,17 @@ describe('saveFile hooks', () => { tags: { bar: 'foo' }, }; const config = Config.get('test'); + const expectedConfig = { + applicationId: config.applicationId, + mount: config.mount, + fileKey: config.fileKey + }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Buffer), 'text/plain', options, - config + expectedConfig ); }); From f9985ba197cfd3289437dd1666f92d5e239eed23 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:24:04 -0400 Subject: [PATCH 13/17] Match the actual test keys --- spec/CloudCode.spec.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 349712f244..e3d689a14f 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -4144,11 +4144,11 @@ describe('saveFile hooks', () => { foo: 'bar', }, }; - const config = Config.get('test'); + // Get the actual config values that will be used const expectedConfig = { - applicationId: config.applicationId, - mount: config.mount, - fileKey: config.fileKey + applicationId: 'test', + mount: 'http://localhost:8378/1', + fileKey: 'test' }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), @@ -4183,11 +4183,11 @@ describe('saveFile hooks', () => { foo: 'bar', }, }; - const config = Config.get('test'); + // Get the actual config values that will be used const expectedConfig = { - applicationId: config.applicationId, - mount: config.mount, - fileKey: config.fileKey + applicationId: 'test', + mount: 'http://localhost:8378/1', + fileKey: 'test' }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), @@ -4220,11 +4220,11 @@ describe('saveFile hooks', () => { metadata: { foo: 'bar' }, tags: { bar: 'foo' }, }; - const config = Config.get('test'); + // Get the actual config values that will be used const expectedConfig = { - applicationId: config.applicationId, - mount: config.mount, - fileKey: config.fileKey + applicationId: 'test', + mount: 'http://localhost:8378/1', + fileKey: 'test' }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), From ce562cf2b31da4da5f40500ba6de0b6111e4105d Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:32:39 -0400 Subject: [PATCH 14/17] Change domain to example --- spec/FilesController.spec.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 49c91d98d9..250e367e1f 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -225,11 +225,11 @@ describe('FilesController', () => { adapterWithReturn.createFile = () => { return Promise.resolve({ name: 'newFilename.txt', - url: 'http://new.url/newFilename.txt' + url: 'http://example.com/newFilename.txt' }); }; adapterWithReturn.getFileLocation = () => { - return Promise.resolve('http://default.url/file.txt'); + return Promise.resolve('http://example.com/file.txt'); }; const controllerWithReturn = new FilesController(adapterWithReturn, null, { preserveFileName: true }); @@ -241,7 +241,7 @@ describe('FilesController', () => { ); expect(result.name).toBe('newFilename.txt'); - expect(result.url).toBe('http://new.url/newFilename.txt'); + expect(result.url).toBe('http://example.com/newFilename.txt'); }); it('should use original filename and generate url when adapter returns nothing', async () => { @@ -251,7 +251,7 @@ describe('FilesController', () => { return Promise.resolve(); }; adapterWithoutReturn.getFileLocation = (config, filename) => { - return Promise.resolve(`http://default.url/${filename}`); + return Promise.resolve(`http://example.com/${filename}`); }; const controllerWithoutReturn = new FilesController(adapterWithoutReturn, null, { preserveFileName: true }); @@ -264,7 +264,7 @@ describe('FilesController', () => { ); expect(result.name).toBe('originalFile.txt'); - expect(result.url).toBe('http://default.url/originalFile.txt'); + expect(result.url).toBe('http://example.com/originalFile.txt'); }); it('should use original filename when adapter returns only url', async () => { @@ -272,11 +272,11 @@ describe('FilesController', () => { const adapterWithOnlyURL = { ...mockAdapter }; adapterWithOnlyURL.createFile = () => { return Promise.resolve({ - url: 'http://new.url/partialFile.txt' + url: 'http://example.com/partialFile.txt' }); }; adapterWithOnlyURL.getFileLocation = () => { - return Promise.resolve('http://default.url/file.txt'); + return Promise.resolve('http://example.com/file.txt'); }; const controllerWithPartial = new FilesController(adapterWithOnlyURL, null, { preserveFileName: true }); @@ -289,7 +289,7 @@ describe('FilesController', () => { ); expect(result.name).toBe('originalFile.txt'); - expect(result.url).toBe('http://new.url/partialFile.txt'); + expect(result.url).toBe('http://example.com/partialFile.txt'); }); it('should use adapter filename and generate url when adapter returns only filename', async () => { @@ -301,7 +301,7 @@ describe('FilesController', () => { }); }; adapterWithOnlyFilename.getFileLocation = (config, filename) => { - return Promise.resolve(`http://default.url/${filename}`); + return Promise.resolve(`http://example.com/${filename}`); }; const controllerWithOnlyFilename = new FilesController(adapterWithOnlyFilename, null, { preserveFileName: true }); @@ -314,6 +314,6 @@ describe('FilesController', () => { ); expect(result.name).toBe('newname.txt'); - expect(result.url).toBe('http://default.url/newname.txt'); + expect(result.url).toBe('http://example.com/newname.txt'); }); }); From 696c2119b6ecbcc8b52f608f7cb1e7e76004bc5b Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:37:18 -0400 Subject: [PATCH 15/17] Use config and stripped down config --- spec/CloudCode.spec.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index e3d689a14f..c25e3ac37e 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -4145,11 +4145,14 @@ describe('saveFile hooks', () => { }, }; // Get the actual config values that will be used + const config = Config.get('test'); + const expectedConfig = { - applicationId: 'test', - mount: 'http://localhost:8378/1', - fileKey: 'test' + applicationId: config.applicationId, + mount: config.mount, + fileKey: config.fileKey }; + expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, @@ -4183,12 +4186,14 @@ describe('saveFile hooks', () => { foo: 'bar', }, }; - // Get the actual config values that will be used + const config = Config.get('test'); + const expectedConfig = { - applicationId: 'test', - mount: 'http://localhost:8378/1', - fileKey: 'test' + applicationId: config.applicationId, + mount: config.mount, + fileKey: config.fileKey }; + expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, @@ -4220,12 +4225,14 @@ describe('saveFile hooks', () => { metadata: { foo: 'bar' }, tags: { bar: 'foo' }, }; - // Get the actual config values that will be used + const config = Config.get('test'); + const expectedConfig = { - applicationId: 'test', - mount: 'http://localhost:8378/1', - fileKey: 'test' + applicationId: config.applicationId, + mount: config.mount, + fileKey: config.fileKey }; + expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Buffer), From 6908e004e7cc8fd72c21a842e0e92f08de1915bb Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:20:05 -0400 Subject: [PATCH 16/17] Revert to full config --- spec/CloudCode.spec.js | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index c25e3ac37e..7623277ef4 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -4147,18 +4147,16 @@ describe('saveFile hooks', () => { // Get the actual config values that will be used const config = Config.get('test'); - const expectedConfig = { - applicationId: config.applicationId, - mount: config.mount, - fileKey: config.fileKey - }; + if (!config.mount){ + config.mount = 'http://localhost:8378/1'; // Default mount for tests + } expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, 'text/plain', newOptions, - expectedConfig + config ); }); @@ -4188,18 +4186,16 @@ describe('saveFile hooks', () => { }; const config = Config.get('test'); - const expectedConfig = { - applicationId: config.applicationId, - mount: config.mount, - fileKey: config.fileKey - }; + if (!config.mount){ + config.mount = 'http://localhost:8378/1'; // Default mount for tests + } expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, newContentType, newOptions, - expectedConfig + config ); const expectedFileName = 'donald_duck.pdf'; expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length); @@ -4227,18 +4223,16 @@ describe('saveFile hooks', () => { }; const config = Config.get('test'); - const expectedConfig = { - applicationId: config.applicationId, - mount: config.mount, - fileKey: config.fileKey - }; + if (!config.mount){ + config.mount = 'http://localhost:8378/1'; // Default mount for tests + } expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Buffer), 'text/plain', options, - expectedConfig + config ); }); From 715ea9ba99625d8e9012e2038a38ba1dd38cdd25 Mon Sep 17 00:00:00 2001 From: Adrian Curtin <48138055+AdrianCurtin@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:51:24 -0400 Subject: [PATCH 17/17] Reduce test expectations to only check application Id --- spec/CloudCode.spec.js | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 7623277ef4..e48d9e4134 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -4144,19 +4144,13 @@ describe('saveFile hooks', () => { foo: 'bar', }, }; - // Get the actual config values that will be used - const config = Config.get('test'); - - if (!config.mount){ - config.mount = 'http://localhost:8378/1'; // Default mount for tests - } expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, 'text/plain', newOptions, - config + jasmine.objectContaining({ applicationId: 'test' }) ); }); @@ -4184,18 +4178,15 @@ describe('saveFile hooks', () => { foo: 'bar', }, }; - const config = Config.get('test'); - - if (!config.mount){ - config.mount = 'http://localhost:8378/1'; // Default mount for tests - } expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, newContentType, newOptions, - config + jasmine.objectContaining({ + applicationId: 'test' + }) ); const expectedFileName = 'donald_duck.pdf'; expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length); @@ -4221,18 +4212,14 @@ describe('saveFile hooks', () => { metadata: { foo: 'bar' }, tags: { bar: 'foo' }, }; - const config = Config.get('test'); - if (!config.mount){ - config.mount = 'http://localhost:8378/1'; // Default mount for tests - } expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Buffer), 'text/plain', options, - config + jasmine.objectContaining({ applicationId: 'test' }) ); });