From 199607b3b489a8aadf87cd9da68e06d2ebc092da Mon Sep 17 00:00:00 2001 From: dblythy Date: Sun, 6 Nov 2022 15:55:07 +1100 Subject: [PATCH 1/5] fix: remove mongoDB 4.0 --- .github/workflows/ci.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71fe415f7f..3b00c6e8b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,16 +121,6 @@ jobs: strategy: matrix: include: - - name: MongoDB 4.0, Standalone, MMAPv1 - MONGODB_VERSION: 4.0.28 - MONGODB_TOPOLOGY: standalone - MONGODB_STORAGE_ENGINE: mmapv1 - NODE_VERSION: 18.1.0 - - name: MongoDB 4.0, ReplicaSet, WiredTiger - MONGODB_VERSION: 4.0.28 - MONGODB_TOPOLOGY: replicaset - MONGODB_STORAGE_ENGINE: wiredTiger - NODE_VERSION: 18.1.0 - name: MongoDB 4.2, ReplicaSet, WiredTiger MONGODB_VERSION: 4.2.19 MONGODB_TOPOLOGY: replicaset From 173769927b3a5c0f6d69e478863b4d7c5af7360d Mon Sep 17 00:00:00 2001 From: dblythy Date: Sun, 6 Nov 2022 16:48:03 +1100 Subject: [PATCH 2/5] remove semver --- spec/MongoStorageAdapter.spec.js | 2 - spec/ParseServerRESTController.spec.js | 5 +- spec/batch.spec.js | 725 ++++++++++++------------- 3 files changed, 359 insertions(+), 373 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index f78caa2608..3abca2f06a 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -6,7 +6,6 @@ const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDataba const request = require('../lib/request'); const Config = require('../lib/Config'); const TestUtils = require('../lib/TestUtils'); -const semver = require('semver'); const fakeClient = { s: { options: { dbName: null } }, @@ -402,7 +401,6 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); if ( - semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') && process.env.MONGODB_TOPOLOGY === 'replicaset' && process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger' ) { diff --git a/spec/ParseServerRESTController.spec.js b/spec/ParseServerRESTController.spec.js index 0ec52ed4b5..d470ae7628 100644 --- a/spec/ParseServerRESTController.spec.js +++ b/spec/ParseServerRESTController.spec.js @@ -2,7 +2,6 @@ const ParseServerRESTController = require('../lib/ParseServerRESTController') .ParseServerRESTController; const ParseServer = require('../lib/ParseServer').default; const Parse = require('parse/node').Parse; -const semver = require('semver'); const TestUtils = require('../lib/TestUtils'); let RESTController; @@ -130,8 +129,7 @@ describe('ParseServerRESTController', () => { }); if ( - (semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') && - process.env.MONGODB_TOPOLOGY === 'replicaset' && + (process.env.MONGODB_TOPOLOGY === 'replicaset' && process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger') || process.env.PARSE_SERVER_TEST_DB === 'postgres' ) { @@ -139,7 +137,6 @@ describe('ParseServerRESTController', () => { beforeEach(async () => { await TestUtils.destroyAllDataPermanently(true); if ( - semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') && process.env.MONGODB_TOPOLOGY === 'replicaset' && process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger' ) { diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 8c3baebcc4..6ac5d0f5c6 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -1,6 +1,5 @@ const batch = require('../lib/batch'); const request = require('../lib/request'); -const semver = require('semver'); const TestUtils = require('../lib/TestUtils'); const originalURL = '/parse/batch'; @@ -205,36 +204,70 @@ describe('batch', () => { expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); - if ( - (semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') && - process.env.MONGODB_TOPOLOGY === 'replicaset' && - process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger') || - process.env.PARSE_SERVER_TEST_DB === 'postgres' - ) { - describe('transactions', () => { - beforeEach(async () => { - await TestUtils.destroyAllDataPermanently(true); - if ( - semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') && - process.env.MONGODB_TOPOLOGY === 'replicaset' && - process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger' - ) { - await reconfigureServer({ - databaseAdapter: undefined, - databaseURI: - 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset', - }); - } else { - await reconfigureServer(); - } + describe('transactions', () => { + beforeEach(async () => { + await TestUtils.destroyAllDataPermanently(true); + if ( + process.env.MONGODB_TOPOLOGY === 'replicaset' && + process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger' + ) { + await reconfigureServer({ + databaseAdapter: undefined, + databaseURI: + 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset', + }); + } else { + await reconfigureServer(); + } + }); + + it('should handle a batch request with transaction = true', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save(); + await myObject.destroy(); + spyOn(databaseAdapter, 'createObject').and.callThrough(); + const response = await 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, + }), }); + 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'); + const results = await query.find(); + expect(databaseAdapter.createObject.calls.count() % 2).toBe(0); + for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) { + expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe( + databaseAdapter.createObject.calls.argsFor(i + 1)[3] + ); + } + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + }); - it('should handle a batch request with transaction = true', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save(); - await myObject.destroy(); - spyOn(databaseAdapter, 'createObject').and.callThrough(); - const response = await request({ + it('should not save anything when one operation fails in a transaction', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save(); + await myObject.destroy(); + try { + await request({ method: 'POST', headers: headers, url: 'http://localhost:8378/1/batch', @@ -248,272 +281,18 @@ describe('batch', () => { { method: 'POST', path: '/1/classes/MyObject', - body: { key: 'value2' }, + body: { key: 10 }, }, - ], - transaction: true, - }), - }); - 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'); - const results = await query.find(); - expect(databaseAdapter.createObject.calls.count() % 2).toBe(0); - for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) { - expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe( - databaseAdapter.createObject.calls.argsFor(i + 1)[3] - ); - } - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); - }); - - it('should not save anything when one operation fails in a transaction', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save(); - await myObject.destroy(); - try { - await 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 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - ], - transaction: true, - }), - }); - fail(); - } catch (error) { - expect(error).toBeDefined(); - const query = new Parse.Query('MyObject'); - const results = await query.find(); - expect(results.length).toBe(0); - } - }); - - it('should generate separate session for each call', async () => { - await reconfigureServer(); - 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; - Parse.Cloud.beforeSave('MyObject', async () => { - myObjectCalls++; - if (myObjectCalls === 2) { - try { - await request({ + { method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/batch', - body: JSON.stringify({ - requests: [ - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - ], - transaction: true, - }), - }); - fail('should fail'); - } catch (e) { - expect(e).toBeDefined(); - } - } - }); - - const response = await request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/batch', - body: JSON.stringify({ - requests: [ + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, { method: 'POST', path: '/1/classes/MyObject', @@ -522,95 +301,307 @@ describe('batch', () => { { method: 'POST', path: '/1/classes/MyObject', - body: { key: 'value2' }, + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, - ], - transaction: true, - }), - }); - - 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(); - - await request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/batch', - body: JSON.stringify({ - requests: [ { method: 'POST', - path: '/1/classes/MyObject3', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', body: { key: 'value1' }, }, { method: 'POST', - path: '/1/classes/MyObject3', - body: { key: 'value2' }, + path: '/1/classes/MyObject', + body: { key: 10 }, }, ], + transaction: true, }), }); - + fail(); + } catch (error) { + expect(error).toBeDefined(); const query = new Parse.Query('MyObject'); const results = await query.find(); - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); - - const query2 = new Parse.Query('MyObject2'); - const results2 = await query2.find(); - expect(results2.length).toEqual(0); - - const query3 = new Parse.Query('MyObject3'); - const results3 = await query3.find(); - expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); - - expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true); - let transactionalSession; - let transactionalSession2; - let myObjectDBCalls = 0; - let myObject2DBCalls = 0; - let myObject3DBCalls = 0; - for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) { - const args = databaseAdapter.createObject.calls.argsFor(i); - switch (args[0]) { - case 'MyObject': - myObjectDBCalls++; - if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { - transactionalSession = args[3]; - } else { - expect(transactionalSession).toBe(args[3]); - } - if (transactionalSession2) { - expect(transactionalSession2).not.toBe(args[3]); - } - break; - case 'MyObject2': - myObject2DBCalls++; - if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { - transactionalSession2 = args[3]; - } else { - expect(transactionalSession2).toBe(args[3]); - } - if (transactionalSession) { - expect(transactionalSession).not.toBe(args[3]); - } - break; - case 'MyObject3': - myObject3DBCalls++; - expect(args[3]).toEqual(null); - break; + expect(results.length).toBe(0); + } + }); + + it('should generate separate session for each call', async () => { + await reconfigureServer(); + 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; + Parse.Cloud.beforeSave('MyObject', async () => { + myObjectCalls++; + if (myObjectCalls === 2) { + try { + await request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/batch', + body: JSON.stringify({ + requests: [ + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + ], + transaction: true, + }), + }); + fail('should fail'); + } catch (e) { + expect(e).toBeDefined(); } } - expect(myObjectDBCalls % 2).toEqual(0); - expect(myObjectDBCalls > 0).toEqual(true); - expect(myObject2DBCalls % 9).toEqual(0); - expect(myObject2DBCalls > 0).toEqual(true); - expect(myObject3DBCalls % 2).toEqual(0); - expect(myObject3DBCalls > 0).toEqual(true); }); + + const response = await 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, + }), + }); + + 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(); + + 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(['value1', 'value2']); + + const query2 = new Parse.Query('MyObject2'); + const results2 = await query2.find(); + expect(results2.length).toEqual(0); + + const query3 = new Parse.Query('MyObject3'); + const results3 = await query3.find(); + expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + + expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true); + let transactionalSession; + let transactionalSession2; + let myObjectDBCalls = 0; + let myObject2DBCalls = 0; + let myObject3DBCalls = 0; + for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) { + const args = databaseAdapter.createObject.calls.argsFor(i); + switch (args[0]) { + case 'MyObject': + myObjectDBCalls++; + if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { + transactionalSession = args[3]; + } else { + expect(transactionalSession).toBe(args[3]); + } + if (transactionalSession2) { + expect(transactionalSession2).not.toBe(args[3]); + } + break; + case 'MyObject2': + myObject2DBCalls++; + if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { + transactionalSession2 = args[3]; + } else { + expect(transactionalSession2).toBe(args[3]); + } + if (transactionalSession) { + expect(transactionalSession).not.toBe(args[3]); + } + break; + case 'MyObject3': + myObject3DBCalls++; + expect(args[3]).toEqual(null); + break; + } + } + expect(myObjectDBCalls % 2).toEqual(0); + expect(myObjectDBCalls > 0).toEqual(true); + expect(myObject2DBCalls % 9).toEqual(0); + expect(myObject2DBCalls > 0).toEqual(true); + expect(myObject3DBCalls % 2).toEqual(0); + expect(myObject3DBCalls > 0).toEqual(true); }); - } + }); }); From 820824f0f4b87627171fe114a3062c7241e8ea9d Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 8 Nov 2022 12:54:26 +1100 Subject: [PATCH 3/5] remove WT --- .github/workflows/ci.yml | 17 ++++------------- package.json | 12 ++++++------ spec/MongoStorageAdapter.spec.js | 3 +-- spec/ParseServerRESTController.spec.js | 6 ++---- spec/batch.spec.js | 3 +-- 5 files changed, 14 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b00c6e8b4..7b5d0b0ee1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,51 +121,42 @@ jobs: strategy: matrix: include: - - name: MongoDB 4.2, ReplicaSet, WiredTiger + - name: MongoDB 4.2, ReplicaSet MONGODB_VERSION: 4.2.19 MONGODB_TOPOLOGY: replicaset - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 18.1.0 - - name: MongoDB 4.4, ReplicaSet, WiredTiger + - name: MongoDB 4.4, ReplicaSet MONGODB_VERSION: 4.4.13 MONGODB_TOPOLOGY: replicaset - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 18.1.0 - - name: MongoDB 5, ReplicaSet, WiredTiger + - name: MongoDB 5, ReplicaSet MONGODB_VERSION: 5.3.2 MONGODB_TOPOLOGY: replicaset - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 18.1.0 - - name: MongoDB 6, ReplicaSet, WiredTiger + - name: MongoDB 6, ReplicaSet MONGODB_VERSION: 6.0.2 MONGODB_TOPOLOGY: replicaset - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 18.1.0 - name: Redis Cache PARSE_SERVER_TEST_CACHE: redis MONGODB_VERSION: 4.4.13 MONGODB_TOPOLOGY: standalone - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 18.1.0 - name: Node 12 MONGODB_VERSION: 4.4.13 MONGODB_TOPOLOGY: standalone - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 12.22.11 - name: Node 14 MONGODB_VERSION: 4.4.13 MONGODB_TOPOLOGY: standalone - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 14.19.1 - name: Node 16 MONGODB_VERSION: 4.4.13 MONGODB_TOPOLOGY: standalone - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 16.14.2 - name: Node 17 MONGODB_VERSION: 4.4.13 MONGODB_TOPOLOGY: standalone - MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 17.9.0 fail-fast: false name: ${{ matrix.name }} diff --git a/package.json b/package.json index 1810510d87..e179dd8297 100644 --- a/package.json +++ b/package.json @@ -112,8 +112,8 @@ "lint-fix": "eslint --fix --cache ./", "build": "babel src/ -d lib/ --copy-files", "watch": "babel --watch src/ -d lib/ --copy-files", - "test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", - "test:mongodb:testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", + "test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start", + "test:mongodb:testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", "test:mongodb": "npm run test:mongodb:runnerstart --dbversion=$npm_config_dbversion && npm run test:mongodb:testonly --dbversion=$npm_config_dbversion", "test:mongodb:4.0.28": "npm run test:mongodb --dbversion=4.0.28", "test:mongodb:4.2.19": "npm run test:mongodb --dbversion=4.2.19", @@ -121,11 +121,11 @@ "test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2", "test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.2", "posttest:mongodb": "mongodb-runner stop", - "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", - "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", + "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start", + "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", "test": "npm run testonly", - "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", - "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", + "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner stop", + "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 nyc jasmine", "start": "node ./bin/parse-server", "prettier": "prettier --write {src,spec}/{**/*,*}.js", "prepare": "npm run build", diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 3abca2f06a..b6adb7677a 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -401,8 +401,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' && - process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger' + process.env.MONGODB_TOPOLOGY === 'replicaset' ) { describe('transactions', () => { const headers = { diff --git a/spec/ParseServerRESTController.spec.js b/spec/ParseServerRESTController.spec.js index d470ae7628..c78a008dbb 100644 --- a/spec/ParseServerRESTController.spec.js +++ b/spec/ParseServerRESTController.spec.js @@ -129,16 +129,14 @@ describe('ParseServerRESTController', () => { }); if ( - (process.env.MONGODB_TOPOLOGY === 'replicaset' && - process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger') || + process.env.MONGODB_TOPOLOGY === 'replicaset' || process.env.PARSE_SERVER_TEST_DB === 'postgres' ) { describe('transactions', () => { beforeEach(async () => { await TestUtils.destroyAllDataPermanently(true); if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' && - process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger' + process.env.MONGODB_TOPOLOGY === 'replicaset' ) { await reconfigureServer({ databaseAdapter: undefined, diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 6ac5d0f5c6..7d8719b74f 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -208,8 +208,7 @@ describe('batch', () => { beforeEach(async () => { await TestUtils.destroyAllDataPermanently(true); if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' && - process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger' + process.env.MONGODB_TOPOLOGY === 'replicaset' ) { await reconfigureServer({ databaseAdapter: undefined, From 837a60d1d4a4a1e6df19787be1284dfa1bce6792 Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 8 Nov 2022 13:08:33 +1100 Subject: [PATCH 4/5] fix tests --- spec/MongoStorageAdapter.spec.js | 4 +- spec/ParseServerRESTController.spec.js | 4 +- spec/batch.spec.js | 717 +++++++++++++------------ 3 files changed, 362 insertions(+), 363 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index b6adb7677a..b95986140d 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -400,9 +400,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(schemaAfterDeletion.fields.test).toBeUndefined(); }); - if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' - ) { + if (process.env.MONGODB_TOPOLOGY === 'replicaset') { describe('transactions', () => { const headers = { 'Content-Type': 'application/json', diff --git a/spec/ParseServerRESTController.spec.js b/spec/ParseServerRESTController.spec.js index c78a008dbb..5e6a550e25 100644 --- a/spec/ParseServerRESTController.spec.js +++ b/spec/ParseServerRESTController.spec.js @@ -135,9 +135,7 @@ describe('ParseServerRESTController', () => { describe('transactions', () => { beforeEach(async () => { await TestUtils.destroyAllDataPermanently(true); - if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' - ) { + if (process.env.MONGODB_TOPOLOGY === 'replicaset') { await reconfigureServer({ databaseAdapter: undefined, databaseURI: diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 7d8719b74f..8367c70f61 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -204,69 +204,30 @@ describe('batch', () => { expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); - describe('transactions', () => { - beforeEach(async () => { - await TestUtils.destroyAllDataPermanently(true); - if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' - ) { - await reconfigureServer({ - databaseAdapter: undefined, - databaseURI: - 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset', - }); - } else { - await reconfigureServer(); - } - }); - - it('should handle a batch request with transaction = true', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save(); - await myObject.destroy(); - spyOn(databaseAdapter, 'createObject').and.callThrough(); - const response = await 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, - }), + if ( + process.env.MONGODB_TOPOLOGY === 'replicaset' || + process.env.PARSE_SERVER_TEST_DB === 'postgres' + ) { + describe('transactions', () => { + beforeEach(async () => { + await TestUtils.destroyAllDataPermanently(true); + if (process.env.MONGODB_TOPOLOGY === 'replicaset') { + await reconfigureServer({ + databaseAdapter: undefined, + databaseURI: + 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset', + }); + } else { + await reconfigureServer(); + } }); - 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'); - const results = await query.find(); - expect(databaseAdapter.createObject.calls.count() % 2).toBe(0); - for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) { - expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe( - databaseAdapter.createObject.calls.argsFor(i + 1)[3] - ); - } - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); - }); - it('should not save anything when one operation fails in a transaction', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save(); - await myObject.destroy(); - try { - await request({ + it('should handle a batch request with transaction = true', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save(); + await myObject.destroy(); + spyOn(databaseAdapter, 'createObject').and.callThrough(); + const response = await request({ method: 'POST', headers: headers, url: 'http://localhost:8378/1/batch', @@ -280,68 +241,272 @@ describe('batch', () => { { method: 'POST', path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + body: { key: 'value2' }, }, - { + ], + transaction: true, + }), + }); + 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'); + const results = await query.find(); + expect(databaseAdapter.createObject.calls.count() % 2).toBe(0); + for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) { + expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe( + databaseAdapter.createObject.calls.argsFor(i + 1)[3] + ); + } + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + }); + + it('should not save anything when one operation fails in a transaction', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save(); + await myObject.destroy(); + try { + await 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 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 10 }, + }, + ], + transaction: true, + }), + }); + fail(); + } catch (error) { + expect(error).toBeDefined(); + const query = new Parse.Query('MyObject'); + const results = await query.find(); + expect(results.length).toBe(0); + } + }); + + it('should generate separate session for each call', async () => { + await reconfigureServer(); + 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; + Parse.Cloud.beforeSave('MyObject', async () => { + myObjectCalls++; + if (myObjectCalls === 2) { + try { + await request({ method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, - }, + headers: headers, + url: 'http://localhost:8378/1/batch', + body: JSON.stringify({ + requests: [ + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, + }, + { + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 10 }, + }, + ], + transaction: true, + }), + }); + fail('should fail'); + } catch (e) { + expect(e).toBeDefined(); + } + } + }); + + const response = await request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/batch', + body: JSON.stringify({ + requests: [ { method: 'POST', path: '/1/classes/MyObject', @@ -350,257 +515,95 @@ describe('batch', () => { { method: 'POST', path: '/1/classes/MyObject', - body: { key: 10 }, + body: { key: 'value2' }, }, + ], + transaction: true, + }), + }); + + 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(); + + await request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/batch', + body: JSON.stringify({ + requests: [ { method: 'POST', - path: '/1/classes/MyObject', + path: '/1/classes/MyObject3', body: { key: 'value1' }, }, { method: 'POST', - path: '/1/classes/MyObject', - body: { key: 10 }, + path: '/1/classes/MyObject3', + body: { key: 'value2' }, }, ], - transaction: true, }), }); - fail(); - } catch (error) { - expect(error).toBeDefined(); + const query = new Parse.Query('MyObject'); const results = await query.find(); - expect(results.length).toBe(0); - } - }); - - it('should generate separate session for each call', async () => { - await reconfigureServer(); - 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; - Parse.Cloud.beforeSave('MyObject', async () => { - myObjectCalls++; - if (myObjectCalls === 2) { - try { - await request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/batch', - body: JSON.stringify({ - requests: [ - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, - }, - { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 10 }, - }, - ], - transaction: true, - }), - }); - fail('should fail'); - } catch (e) { - expect(e).toBeDefined(); + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + + const query2 = new Parse.Query('MyObject2'); + const results2 = await query2.find(); + expect(results2.length).toEqual(0); + + const query3 = new Parse.Query('MyObject3'); + const results3 = await query3.find(); + expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + + expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true); + let transactionalSession; + let transactionalSession2; + let myObjectDBCalls = 0; + let myObject2DBCalls = 0; + let myObject3DBCalls = 0; + for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) { + const args = databaseAdapter.createObject.calls.argsFor(i); + switch (args[0]) { + case 'MyObject': + myObjectDBCalls++; + if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { + transactionalSession = args[3]; + } else { + expect(transactionalSession).toBe(args[3]); + } + if (transactionalSession2) { + expect(transactionalSession2).not.toBe(args[3]); + } + break; + case 'MyObject2': + myObject2DBCalls++; + if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { + transactionalSession2 = args[3]; + } else { + expect(transactionalSession2).toBe(args[3]); + } + if (transactionalSession) { + expect(transactionalSession).not.toBe(args[3]); + } + break; + case 'MyObject3': + myObject3DBCalls++; + expect(args[3]).toEqual(null); + break; } } + expect(myObjectDBCalls % 2).toEqual(0); + expect(myObjectDBCalls > 0).toEqual(true); + expect(myObject2DBCalls % 9).toEqual(0); + expect(myObject2DBCalls > 0).toEqual(true); + expect(myObject3DBCalls % 2).toEqual(0); + expect(myObject3DBCalls > 0).toEqual(true); }); - - const response = await 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, - }), - }); - - 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(); - - 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(['value1', 'value2']); - - const query2 = new Parse.Query('MyObject2'); - const results2 = await query2.find(); - expect(results2.length).toEqual(0); - - const query3 = new Parse.Query('MyObject3'); - const results3 = await query3.find(); - expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); - - expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true); - let transactionalSession; - let transactionalSession2; - let myObjectDBCalls = 0; - let myObject2DBCalls = 0; - let myObject3DBCalls = 0; - for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) { - const args = databaseAdapter.createObject.calls.argsFor(i); - switch (args[0]) { - case 'MyObject': - myObjectDBCalls++; - if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { - transactionalSession = args[3]; - } else { - expect(transactionalSession).toBe(args[3]); - } - if (transactionalSession2) { - expect(transactionalSession2).not.toBe(args[3]); - } - break; - case 'MyObject2': - myObject2DBCalls++; - if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { - transactionalSession2 = args[3]; - } else { - expect(transactionalSession2).toBe(args[3]); - } - if (transactionalSession) { - expect(transactionalSession).not.toBe(args[3]); - } - break; - case 'MyObject3': - myObject3DBCalls++; - expect(args[3]).toEqual(null); - break; - } - } - expect(myObjectDBCalls % 2).toEqual(0); - expect(myObjectDBCalls > 0).toEqual(true); - expect(myObject2DBCalls % 9).toEqual(0); - expect(myObject2DBCalls > 0).toEqual(true); - expect(myObject3DBCalls % 2).toEqual(0); - expect(myObject3DBCalls > 0).toEqual(true); }); - }); + } }); From 58d676bb4f6d812197b4cc14e121f98557bcd23d Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Thu, 10 Nov 2022 22:12:59 +0100 Subject: [PATCH 5/5] remove mongodb 4 script --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 78b2091f34..269dd140ec 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,6 @@ "test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start", "test:mongodb:testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", "test:mongodb": "npm run test:mongodb:runnerstart --dbversion=$npm_config_dbversion && npm run test:mongodb:testonly --dbversion=$npm_config_dbversion", - "test:mongodb:4.0.28": "npm run test:mongodb --dbversion=4.0.28", "test:mongodb:4.2.19": "npm run test:mongodb --dbversion=4.2.19", "test:mongodb:4.4.13": "npm run test:mongodb --dbversion=4.4.13", "test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2",