@@ -594,6 +594,176 @@ describe('batch', () => {
594594 } ) ;
595595 }
596596
597+ describe ( 'batch request size limit' , ( ) => {
598+ it ( 'should reject batch request when sub-requests exceed batchRequestLimit' , async ( ) => {
599+ await reconfigureServer ( {
600+ requestComplexity : { batchRequestLimit : 2 } ,
601+ } ) ;
602+ await expectAsync (
603+ request ( {
604+ method : 'POST' ,
605+ url : 'http://localhost:8378/1/batch' ,
606+ headers,
607+ body : JSON . stringify ( {
608+ requests : [
609+ { method : 'GET' , path : '/1/classes/TestClass' } ,
610+ { method : 'GET' , path : '/1/classes/TestClass' } ,
611+ { method : 'GET' , path : '/1/classes/TestClass' } ,
612+ ] ,
613+ } ) ,
614+ } )
615+ ) . toBeRejectedWith (
616+ jasmine . objectContaining ( {
617+ status : 400 ,
618+ data : jasmine . objectContaining ( {
619+ error : jasmine . stringContaining ( '3' ) ,
620+ } ) ,
621+ } )
622+ ) ;
623+ } ) ;
624+
625+ it ( 'should allow batch request when sub-requests are within batchRequestLimit' , async ( ) => {
626+ await reconfigureServer ( {
627+ requestComplexity : { batchRequestLimit : 5 } ,
628+ } ) ;
629+ const result = await request ( {
630+ method : 'POST' ,
631+ url : 'http://localhost:8378/1/batch' ,
632+ headers,
633+ body : JSON . stringify ( {
634+ requests : [
635+ { method : 'POST' , path : '/1/classes/TestClass' , body : { key : 'v1' } } ,
636+ { method : 'POST' , path : '/1/classes/TestClass' , body : { key : 'v2' } } ,
637+ ] ,
638+ } ) ,
639+ } ) ;
640+ expect ( result . data . length ) . toEqual ( 2 ) ;
641+ expect ( result . data [ 0 ] . success . objectId ) . toBeDefined ( ) ;
642+ expect ( result . data [ 1 ] . success . objectId ) . toBeDefined ( ) ;
643+ } ) ;
644+
645+ it ( 'should allow batch request at exactly batchRequestLimit' , async ( ) => {
646+ await reconfigureServer ( {
647+ requestComplexity : { batchRequestLimit : 2 } ,
648+ } ) ;
649+ const result = await request ( {
650+ method : 'POST' ,
651+ url : 'http://localhost:8378/1/batch' ,
652+ headers,
653+ body : JSON . stringify ( {
654+ requests : [
655+ { method : 'POST' , path : '/1/classes/TestClass' , body : { key : 'v1' } } ,
656+ { method : 'POST' , path : '/1/classes/TestClass' , body : { key : 'v2' } } ,
657+ ] ,
658+ } ) ,
659+ } ) ;
660+ expect ( result . data . length ) . toEqual ( 2 ) ;
661+ } ) ;
662+
663+ it ( 'should not limit batch request when batchRequestLimit is -1 (disabled)' , async ( ) => {
664+ await reconfigureServer ( {
665+ requestComplexity : { batchRequestLimit : - 1 } ,
666+ } ) ;
667+ const requests = Array . from ( { length : 20 } , ( _ , i ) => ( {
668+ method : 'POST' ,
669+ path : '/1/classes/TestClass' ,
670+ body : { key : `v${ i } ` } ,
671+ } ) ) ;
672+ const result = await request ( {
673+ method : 'POST' ,
674+ url : 'http://localhost:8378/1/batch' ,
675+ headers,
676+ body : JSON . stringify ( { requests } ) ,
677+ } ) ;
678+ expect ( result . data . length ) . toEqual ( 20 ) ;
679+ } ) ;
680+
681+ it ( 'should not limit batch request by default (no requestComplexity configured)' , async ( ) => {
682+ const requests = Array . from ( { length : 20 } , ( _ , i ) => ( {
683+ method : 'POST' ,
684+ path : '/1/classes/TestClass' ,
685+ body : { key : `v${ i } ` } ,
686+ } ) ) ;
687+ const result = await request ( {
688+ method : 'POST' ,
689+ url : 'http://localhost:8378/1/batch' ,
690+ headers,
691+ body : JSON . stringify ( { requests } ) ,
692+ } ) ;
693+ expect ( result . data . length ) . toEqual ( 20 ) ;
694+ } ) ;
695+
696+ it ( 'should bypass batchRequestLimit for master key requests' , async ( ) => {
697+ await reconfigureServer ( {
698+ requestComplexity : { batchRequestLimit : 2 } ,
699+ } ) ;
700+ const result = await request ( {
701+ method : 'POST' ,
702+ url : 'http://localhost:8378/1/batch' ,
703+ headers : {
704+ ...headers ,
705+ 'X-Parse-Master-Key' : 'test' ,
706+ } ,
707+ body : JSON . stringify ( {
708+ requests : [
709+ { method : 'GET' , path : '/1/classes/TestClass' } ,
710+ { method : 'GET' , path : '/1/classes/TestClass' } ,
711+ { method : 'GET' , path : '/1/classes/TestClass' } ,
712+ ] ,
713+ } ) ,
714+ } ) ;
715+ expect ( result . data . length ) . toEqual ( 3 ) ;
716+ } ) ;
717+
718+ it ( 'should bypass batchRequestLimit for maintenance key requests' , async ( ) => {
719+ await reconfigureServer ( {
720+ requestComplexity : { batchRequestLimit : 2 } ,
721+ } ) ;
722+ const result = await request ( {
723+ method : 'POST' ,
724+ url : 'http://localhost:8378/1/batch' ,
725+ headers : {
726+ ...headers ,
727+ 'X-Parse-Maintenance-Key' : 'testing' ,
728+ } ,
729+ body : JSON . stringify ( {
730+ requests : [
731+ { method : 'GET' , path : '/1/classes/TestClass' } ,
732+ { method : 'GET' , path : '/1/classes/TestClass' } ,
733+ { method : 'GET' , path : '/1/classes/TestClass' } ,
734+ ] ,
735+ } ) ,
736+ } ) ;
737+ expect ( result . data . length ) . toEqual ( 3 ) ;
738+ } ) ;
739+
740+ it ( 'should include limit in error message when batch exceeds batchRequestLimit' , async ( ) => {
741+ await reconfigureServer ( {
742+ requestComplexity : { batchRequestLimit : 5 } ,
743+ } ) ;
744+ await expectAsync (
745+ request ( {
746+ method : 'POST' ,
747+ url : 'http://localhost:8378/1/batch' ,
748+ headers,
749+ body : JSON . stringify ( {
750+ requests : Array . from ( { length : 10 } , ( ) => ( {
751+ method : 'GET' ,
752+ path : '/1/classes/TestClass' ,
753+ } ) ) ,
754+ } ) ,
755+ } )
756+ ) . toBeRejectedWith (
757+ jasmine . objectContaining ( {
758+ status : 400 ,
759+ data : jasmine . objectContaining ( {
760+ error : jasmine . stringContaining ( '5' ) ,
761+ } ) ,
762+ } )
763+ ) ;
764+ } ) ;
765+ } ) ;
766+
597767 describe ( 'subrequest path type validation' , ( ) => {
598768 it ( 'rejects object path in batch subrequest with proper error instead of 500' , async ( ) => {
599769 await expectAsync (
0 commit comments