Skip to content

Commit ad40386

Browse files
authored
Test transactions (#6022)
* Test transactions * test: Separate transaction tests for SDK
1 parent f9b77c1 commit ad40386

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed

spec/MongoStorageAdapter.spec.js

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageA
55
const { MongoClient } = require('mongodb');
66
const databaseURI =
77
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
8+
const request = require('../lib/request');
9+
const Config = require('../lib/Config');
10+
const TestUtils = require('../lib/TestUtils');
811

912
const fakeClient = {
1013
s: { options: { dbName: null } },
@@ -316,4 +319,229 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
316319
undefined
317320
);
318321
});
322+
323+
if (
324+
process.env.MONGODB_VERSION === '4.0.4' &&
325+
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
326+
process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger'
327+
) {
328+
describe('transactions', () => {
329+
const headers = {
330+
'Content-Type': 'application/json',
331+
'X-Parse-Application-Id': 'test',
332+
'X-Parse-REST-API-Key': 'rest',
333+
};
334+
335+
beforeAll(async () => {
336+
await reconfigureServer({
337+
databaseAdapter: undefined,
338+
databaseURI:
339+
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
340+
});
341+
});
342+
343+
beforeEach(async () => {
344+
await TestUtils.destroyAllDataPermanently(true);
345+
});
346+
347+
it('should use transaction in a batch with transaction = true', async () => {
348+
const myObject = new Parse.Object('MyObject');
349+
await myObject.save();
350+
351+
const databaseAdapter = Config.get(Parse.applicationId).database
352+
.adapter;
353+
spyOn(
354+
databaseAdapter.database.serverConfig,
355+
'command'
356+
).and.callThrough();
357+
358+
await request({
359+
method: 'POST',
360+
headers: headers,
361+
url: 'http://localhost:8378/1/batch',
362+
body: JSON.stringify({
363+
requests: [
364+
{
365+
method: 'PUT',
366+
path: '/1/classes/MyObject/' + myObject.id,
367+
body: { myAttribute: 'myValue' },
368+
},
369+
],
370+
transaction: true,
371+
}),
372+
});
373+
374+
let found = false;
375+
databaseAdapter.database.serverConfig.command.calls
376+
.all()
377+
.forEach(call => {
378+
found = true;
379+
expect(call.args[2].session.transaction.state).not.toBe(
380+
'NO_TRANSACTION'
381+
);
382+
});
383+
expect(found).toBe(true);
384+
});
385+
386+
it('should not use transaction in a batch with transaction = false', async () => {
387+
const myObject = new Parse.Object('MyObject');
388+
await myObject.save();
389+
390+
const databaseAdapter = Config.get(Parse.applicationId).database
391+
.adapter;
392+
spyOn(
393+
databaseAdapter.database.serverConfig,
394+
'command'
395+
).and.callThrough();
396+
397+
await request({
398+
method: 'POST',
399+
headers: headers,
400+
url: 'http://localhost:8378/1/batch',
401+
body: JSON.stringify({
402+
requests: [
403+
{
404+
method: 'PUT',
405+
path: '/1/classes/MyObject/' + myObject.id,
406+
body: { myAttribute: 'myValue' },
407+
},
408+
],
409+
transaction: false,
410+
}),
411+
});
412+
413+
let found = false;
414+
databaseAdapter.database.serverConfig.command.calls
415+
.all()
416+
.forEach(call => {
417+
found = true;
418+
expect(call.args[2].session).toBe(undefined);
419+
});
420+
expect(found).toBe(true);
421+
});
422+
423+
it('should not use transaction in a batch with no transaction option sent', async () => {
424+
const myObject = new Parse.Object('MyObject');
425+
await myObject.save();
426+
427+
const databaseAdapter = Config.get(Parse.applicationId).database
428+
.adapter;
429+
spyOn(
430+
databaseAdapter.database.serverConfig,
431+
'command'
432+
).and.callThrough();
433+
434+
await request({
435+
method: 'POST',
436+
headers: headers,
437+
url: 'http://localhost:8378/1/batch',
438+
body: JSON.stringify({
439+
requests: [
440+
{
441+
method: 'PUT',
442+
path: '/1/classes/MyObject/' + myObject.id,
443+
body: { myAttribute: 'myValue' },
444+
},
445+
],
446+
}),
447+
});
448+
449+
let found = false;
450+
databaseAdapter.database.serverConfig.command.calls
451+
.all()
452+
.forEach(call => {
453+
found = true;
454+
expect(call.args[2].session).toBe(undefined);
455+
});
456+
expect(found).toBe(true);
457+
});
458+
459+
it('should not use transaction in a put request', async () => {
460+
const myObject = new Parse.Object('MyObject');
461+
await myObject.save();
462+
463+
const databaseAdapter = Config.get(Parse.applicationId).database
464+
.adapter;
465+
spyOn(
466+
databaseAdapter.database.serverConfig,
467+
'command'
468+
).and.callThrough();
469+
470+
await request({
471+
method: 'PUT',
472+
headers: headers,
473+
url: 'http://localhost:8378/1/classes/MyObject/' + myObject.id,
474+
body: { myAttribute: 'myValue' },
475+
});
476+
477+
let found = false;
478+
databaseAdapter.database.serverConfig.command.calls
479+
.all()
480+
.forEach(call => {
481+
found = true;
482+
expect(call.args[2].session).toBe(undefined);
483+
});
484+
expect(found).toBe(true);
485+
});
486+
487+
it('should not use transactions when using SDK insert', async () => {
488+
const databaseAdapter = Config.get(Parse.applicationId).database
489+
.adapter;
490+
spyOn(
491+
databaseAdapter.database.serverConfig,
492+
'insert'
493+
).and.callThrough();
494+
495+
const myObject = new Parse.Object('MyObject');
496+
await myObject.save();
497+
498+
const calls = databaseAdapter.database.serverConfig.insert.calls.all();
499+
expect(calls.length).toBeGreaterThan(0);
500+
calls.forEach(call => {
501+
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
502+
});
503+
});
504+
505+
it('should not use transactions when using SDK update', async () => {
506+
const databaseAdapter = Config.get(Parse.applicationId).database
507+
.adapter;
508+
spyOn(
509+
databaseAdapter.database.serverConfig,
510+
'update'
511+
).and.callThrough();
512+
513+
const myObject = new Parse.Object('MyObject');
514+
await myObject.save();
515+
516+
myObject.set('myAttribute', 'myValue');
517+
await myObject.save();
518+
519+
const calls = databaseAdapter.database.serverConfig.update.calls.all();
520+
expect(calls.length).toBeGreaterThan(0);
521+
calls.forEach(call => {
522+
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
523+
});
524+
});
525+
526+
it('should not use transactions when using SDK delete', async () => {
527+
const databaseAdapter = Config.get(Parse.applicationId).database
528+
.adapter;
529+
spyOn(
530+
databaseAdapter.database.serverConfig,
531+
'remove'
532+
).and.callThrough();
533+
534+
const myObject = new Parse.Object('MyObject');
535+
await myObject.save();
536+
537+
await myObject.destroy();
538+
539+
const calls = databaseAdapter.database.serverConfig.remove.calls.all();
540+
expect(calls.length).toBeGreaterThan(0);
541+
calls.forEach(call => {
542+
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
543+
});
544+
});
545+
});
546+
}
319547
});

0 commit comments

Comments
 (0)