@@ -5,6 +5,9 @@ const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageA
55const { MongoClient } = require ( 'mongodb' ) ;
66const 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
912const 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