Batch transaction (#5849)
* Batch transaction boilerplate * Refactoring transaction boilerplate * Independent sessions test * Transactions - partial * Missing only one test * All tests passing for mongo db * Tests on Travis * Transactions on postgres * Fix travis to restart mongodb * Remove mongodb service and keep only mongodb runner * MongoDB service back * Initialize replicaset * Remove mongodb runner again * Again only with mongodb-runner and removing cache * Trying with pretest and posttest * WiredTiger * Pretest and posttest again * Removing inexistent scripts * wiredTiger * One more attempt * Trying another way to run mongodb-runner * Fixing tests * Include batch transaction on direct access * Add tests to direct access
This commit is contained in:
committed by
GitHub
parent
fe18fe0f61
commit
8b97c1380b
@@ -25,7 +25,8 @@
|
||||
"jequal": true,
|
||||
"create": true,
|
||||
"arrayContains": true,
|
||||
"expectAsync": true
|
||||
"expectAsync": true,
|
||||
"databaseAdapter": true
|
||||
},
|
||||
"rules": {
|
||||
"no-console": [0],
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('GridFSBucket and GridStore interop', () => {
|
||||
beforeEach(async () => {
|
||||
const gsAdapter = new GridStoreAdapter(databaseURI);
|
||||
const db = await gsAdapter._connect();
|
||||
db.dropDatabase();
|
||||
await db.dropDatabase();
|
||||
});
|
||||
|
||||
it('a file created in GridStore should be available in GridFS', async () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,6 +2,7 @@ const ParseServerRESTController = require('../lib/ParseServerRESTController')
|
||||
.ParseServerRESTController;
|
||||
const ParseServer = require('../lib/ParseServer').default;
|
||||
const Parse = require('parse/node').Parse;
|
||||
const TestUtils = require('../lib/TestUtils');
|
||||
|
||||
let RESTController;
|
||||
|
||||
@@ -40,7 +41,7 @@ describe('ParseServerRESTController', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle a POST batch', done => {
|
||||
it('should handle a POST batch without transaction', done => {
|
||||
RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
@@ -69,6 +70,272 @@ describe('ParseServerRESTController', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle a POST batch with transaction=false', done => {
|
||||
RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/classes/MyObject',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/classes/MyObject',
|
||||
body: { key: 'value' },
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/classes/MyObject',
|
||||
},
|
||||
],
|
||||
transaction: false,
|
||||
}).then(
|
||||
res => {
|
||||
expect(res.length).toBe(3);
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
jfail(err);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (
|
||||
(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', () => {
|
||||
beforeAll(async () => {
|
||||
if (
|
||||
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',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestUtils.destroyAllDataPermanently(true);
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = true', 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(() => {
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
|
||||
RESTController.request('POST', 'batch', {
|
||||
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.length).toEqual(2);
|
||||
expect(response[0].success.objectId).toBeDefined();
|
||||
expect(response[0].success.createdAt).toBeDefined();
|
||||
expect(response[1].success.objectId).toBeDefined();
|
||||
expect(response[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 => {
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
myObject
|
||||
.save()
|
||||
.then(() => {
|
||||
return myObject.destroy();
|
||||
})
|
||||
.then(() => {
|
||||
RESTController.request('POST', 'batch', {
|
||||
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.message).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('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 RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
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 RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value1' },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value2' },
|
||||
},
|
||||
],
|
||||
transaction: true,
|
||||
});
|
||||
|
||||
expect(response.length).toEqual(2);
|
||||
expect(response[0].success.objectId).toBeDefined();
|
||||
expect(response[0].success.createdAt).toBeDefined();
|
||||
expect(response[1].success.objectId).toBeDefined();
|
||||
expect(response[1].success.createdAt).toBeDefined();
|
||||
|
||||
await RESTController.request('POST', 'batch', {
|
||||
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()).toBe(5);
|
||||
let transactionalSession;
|
||||
let transactionalSession2;
|
||||
let myObjectDBCalls = 0;
|
||||
let myObject2DBCalls = 0;
|
||||
let myObject3DBCalls = 0;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const args = databaseAdapter.createObject.calls.argsFor(i);
|
||||
switch (args[0]) {
|
||||
case 'MyObject':
|
||||
myObjectDBCalls++;
|
||||
if (!transactionalSession) {
|
||||
transactionalSession = args[3];
|
||||
} else {
|
||||
expect(transactionalSession).toBe(args[3]);
|
||||
}
|
||||
if (transactionalSession2) {
|
||||
expect(transactionalSession2).not.toBe(args[3]);
|
||||
}
|
||||
break;
|
||||
case 'MyObject2':
|
||||
myObject2DBCalls++;
|
||||
transactionalSession2 = args[3];
|
||||
if (transactionalSession) {
|
||||
expect(transactionalSession).not.toBe(args[3]);
|
||||
}
|
||||
break;
|
||||
case 'MyObject3':
|
||||
myObject3DBCalls++;
|
||||
expect(args[3]).toEqual(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(myObjectDBCalls).toEqual(2);
|
||||
expect(myObject2DBCalls).toEqual(1);
|
||||
expect(myObject3DBCalls).toEqual(2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('should handle a POST request', done => {
|
||||
RESTController.request('POST', '/classes/MyObject', { key: 'value' })
|
||||
.then(() => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const batch = require('../lib/batch');
|
||||
const request = require('../lib/request');
|
||||
const TestUtils = require('../lib/TestUtils');
|
||||
|
||||
const originalURL = '/parse/batch';
|
||||
const serverURL = 'http://localhost:1234/parse';
|
||||
@@ -7,6 +9,13 @@ const serverURLNaked = 'http://localhost:1234/';
|
||||
const publicServerURL = 'http://domain.com/parse';
|
||||
const publicServerURLNaked = 'http://domain.com/';
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'X-Parse-Installation-Id': 'yolo',
|
||||
};
|
||||
|
||||
describe('batch', () => {
|
||||
it('should return the proper url', () => {
|
||||
const internalURL = batch.makeBatchRoutingPathFunction(originalURL)(
|
||||
@@ -59,4 +68,348 @@ describe('batch', () => {
|
||||
|
||||
expect(internalURL).toEqual('/classes/Object');
|
||||
});
|
||||
|
||||
it('should handle a batch request without transaction', done => {
|
||||
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' },
|
||||
},
|
||||
],
|
||||
}),
|
||||
}).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]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual([
|
||||
'value1',
|
||||
'value2',
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = false', done => {
|
||||
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: false,
|
||||
}),
|
||||
}).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]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual([
|
||||
'value1',
|
||||
'value2',
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (
|
||||
(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', () => {
|
||||
beforeAll(async () => {
|
||||
if (
|
||||
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',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestUtils.destroyAllDataPermanently(true);
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = true', 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(() => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not save anything when one operation fails in a transaction', 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('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 },
|
||||
},
|
||||
],
|
||||
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',
|
||||
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()).toBe(5);
|
||||
let transactionalSession;
|
||||
let transactionalSession2;
|
||||
let myObjectDBCalls = 0;
|
||||
let myObject2DBCalls = 0;
|
||||
let myObject3DBCalls = 0;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const args = databaseAdapter.createObject.calls.argsFor(i);
|
||||
switch (args[0]) {
|
||||
case 'MyObject':
|
||||
myObjectDBCalls++;
|
||||
if (!transactionalSession) {
|
||||
transactionalSession = args[3];
|
||||
} else {
|
||||
expect(transactionalSession).toBe(args[3]);
|
||||
}
|
||||
if (transactionalSession2) {
|
||||
expect(transactionalSession2).not.toBe(args[3]);
|
||||
}
|
||||
break;
|
||||
case 'MyObject2':
|
||||
myObject2DBCalls++;
|
||||
transactionalSession2 = args[3];
|
||||
if (transactionalSession) {
|
||||
expect(transactionalSession).not.toBe(args[3]);
|
||||
}
|
||||
break;
|
||||
case 'MyObject3':
|
||||
myObject3DBCalls++;
|
||||
expect(args[3]).toEqual(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(myObjectDBCalls).toEqual(2);
|
||||
expect(myObject2DBCalls).toEqual(1);
|
||||
expect(myObject3DBCalls).toEqual(2);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -40,20 +40,12 @@ const postgresURI =
|
||||
let databaseAdapter;
|
||||
// need to bind for mocking mocha
|
||||
|
||||
let startDB = () => {};
|
||||
let stopDB = () => {};
|
||||
|
||||
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
|
||||
databaseAdapter = new PostgresStorageAdapter({
|
||||
uri: process.env.PARSE_SERVER_TEST_DATABASE_URI || postgresURI,
|
||||
collectionPrefix: 'test_',
|
||||
});
|
||||
} else {
|
||||
startDB = require('mongodb-runner/mocha/before').bind({
|
||||
timeout: () => {},
|
||||
slow: () => {},
|
||||
});
|
||||
stopDB = require('mongodb-runner/mocha/after');
|
||||
databaseAdapter = new MongoStorageAdapter({
|
||||
uri: mongoURI,
|
||||
collectionPrefix: 'test_',
|
||||
@@ -177,11 +169,6 @@ const reconfigureServer = changedConfiguration => {
|
||||
const Parse = require('parse/node');
|
||||
Parse.serverURL = 'http://localhost:' + port + '/1';
|
||||
|
||||
// 10 minutes timeout
|
||||
beforeAll(startDB, 10 * 60 * 1000);
|
||||
|
||||
afterAll(stopDB);
|
||||
|
||||
beforeEach(done => {
|
||||
try {
|
||||
Parse.User.enableUnsafeCurrentUser();
|
||||
@@ -417,6 +404,7 @@ global.reconfigureServer = reconfigureServer;
|
||||
global.defaultConfiguration = defaultConfiguration;
|
||||
global.mockCustomAuthenticator = mockCustomAuthenticator;
|
||||
global.mockFacebookAuthenticator = mockFacebookAuthenticator;
|
||||
global.databaseAdapter = databaseAdapter;
|
||||
global.jfail = function(err) {
|
||||
fail(JSON.stringify(err));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user