diff --git a/package.json b/package.json index e5673c33fb..f17926a26d 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} mongodb-runner start", "test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} TESTING=1 jasmine", "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} mongodb-runner stop", - "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} TESTING=1 nyc jasmine", + "coverage": "npm run pretest && cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} TESTING=1 nyc jasmine && npm run posttest", "start": "node ./bin/parse-server", "prepare": "npm run build", "postinstall": "node -p 'require(\"./postinstall.js\")()'" diff --git a/spec/GridStoreAdapter.spec.js b/spec/GridStoreAdapter.spec.js index 2565f7bee0..1b6d61256c 100644 --- a/spec/GridStoreAdapter.spec.js +++ b/spec/GridStoreAdapter.spec.js @@ -13,7 +13,7 @@ describe_only_db('mongo')('GridStoreAdapter', () => { const config = Config.get(Parse.applicationId); const gridStoreAdapter = new GridStoreAdapter(databaseURI); const db = await gridStoreAdapter._connect(); - db.dropDatabase(); + await db.dropDatabase(); const filesController = new FilesController( gridStoreAdapter, Parse.applicationId, diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 7b62ddef4d..68ee0c01a2 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -178,83 +178,102 @@ describe('batch', () => { }); it('should handle a batch request with transaction = true', done => { - spyOn(databaseAdapter, 'createObject').and.callThrough(); + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + myObject + .save() + .then(() => { + return myObject.destroy(); + }) + .then(() => { + spyOn(databaseAdapter, 'createObject').and.callThrough(); - request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/batch', - body: JSON.stringify({ - requests: [ - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value2' }, - }, - ], - transaction: true, - }), - }).then(response => { - expect(response.data.length).toEqual(2); - expect(response.data[0].success.objectId).toBeDefined(); - expect(response.data[0].success.createdAt).toBeDefined(); - expect(response.data[1].success.objectId).toBeDefined(); - expect(response.data[1].success.createdAt).toBeDefined(); - const query = new Parse.Query('MyObject'); - query.find().then(results => { - expect(databaseAdapter.createObject.calls.count()).toBe(2); - expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toBe( - databaseAdapter.createObject.calls.argsFor(1)[3] - ); - expect(results.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); - done(); + request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/batch', + body: JSON.stringify({ + requests: [ + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value2' }, + }, + ], + transaction: true, + }), + }).then(response => { + expect(response.data.length).toEqual(2); + expect(response.data[0].success.objectId).toBeDefined(); + expect(response.data[0].success.createdAt).toBeDefined(); + expect(response.data[1].success.objectId).toBeDefined(); + expect(response.data[1].success.createdAt).toBeDefined(); + const query = new Parse.Query('MyObject'); + query.find().then(results => { + expect(databaseAdapter.createObject.calls.count()).toBe(2); + expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toBe( + databaseAdapter.createObject.calls.argsFor(1)[3] + ); + expect(results.map(result => result.get('key')).sort()).toEqual( + ['value1', 'value2'] + ); + done(); + }); + }); }); - }); }); it('should not save anything when one operation fails in a transaction', done => { - request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/batch', - body: JSON.stringify({ - requests: [ - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - ], - transaction: true, - }), - }).catch(error => { - expect(error.data).toBeDefined(); - const query = new Parse.Query('MyObject'); - query.find().then(results => { - expect(results.length).toBe(0); - done(); + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + myObject + .save() + .then(() => { + return myObject.destroy(); + }) + .then(() => { + request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/batch', + body: JSON.stringify({ + requests: [ + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + ], + transaction: true, + }), + }).catch(error => { + expect(error.data).toBeDefined(); + const query = new Parse.Query('MyObject'); + query.find().then(results => { + expect(results.length).toBe(0); + done(); + }); + }); }); - }); }); it('should generate separate session for each call', async () => { - const myObject = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections await myObject.save(); await myObject.destroy(); + const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections + await myObject2.save(); + await myObject2.destroy(); + spyOn(databaseAdapter, 'createObject').and.callThrough(); let myObjectCalls = 0; @@ -289,32 +308,6 @@ describe('batch', () => { } }); - let myObject2Calls = 0; - Parse.Cloud.beforeSave('MyObject2', async () => { - myObject2Calls++; - if (myObject2Calls === 2) { - await request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/batch', - body: JSON.stringify({ - requests: [ - { - method: 'POST', - path: '/1/classes/MyObject3', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject3', - body: { key: 'value2' }, - }, - ], - }), - }); - } - }); - const response = await request({ method: 'POST', headers: headers, @@ -342,6 +335,26 @@ describe('batch', () => { expect(response.data[1].success.objectId).toBeDefined(); expect(response.data[1].success.createdAt).toBeDefined(); + await request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/batch', + body: JSON.stringify({ + requests: [ + { + method: 'POST', + path: '/1/classes/MyObject3', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject3', + body: { key: 'value2' }, + }, + ], + }), + }); + const query = new Parse.Query('MyObject'); const results = await query.find(); expect(results.map(result => result.get('key')).sort()).toEqual([