@@ -547,5 +547,217 @@ describe('ObjectQLPlugin - Metadata Service Integration', () => {
547547 expect ( syncedNames ) . not . toContain ( 'sys__user' ) ;
548548 expect ( syncedNames ) . not . toContain ( 'sys__session' ) ;
549549 } ) ;
550+
551+ it ( 'should use syncSchemasBatch when driver supports batchSchemaSync' , async ( ) => {
552+ // Arrange - driver that supports batch schema sync
553+ const batchCalls : Array < { object : string ; schema : any } [ ] > = [ ] ;
554+ const singleCalls : Array < { object : string ; schema : any } > = [ ] ;
555+ const mockDriver = {
556+ name : 'batch-driver' ,
557+ version : '1.0.0' ,
558+ supports : { batchSchemaSync : true } ,
559+ connect : async ( ) => { } ,
560+ disconnect : async ( ) => { } ,
561+ find : async ( ) => [ ] ,
562+ findOne : async ( ) => null ,
563+ create : async ( _o : string , d : any ) => d ,
564+ update : async ( _o : string , _i : any , d : any ) => d ,
565+ delete : async ( ) => true ,
566+ syncSchema : async ( object : string , schema : any ) => {
567+ singleCalls . push ( { object, schema } ) ;
568+ } ,
569+ syncSchemasBatch : async ( schemas : Array < { object : string ; schema : any } > ) => {
570+ batchCalls . push ( schemas ) ;
571+ } ,
572+ } ;
573+
574+ await kernel . use ( {
575+ name : 'mock-batch-driver-plugin' ,
576+ type : 'driver' ,
577+ version : '1.0.0' ,
578+ init : async ( ctx ) => {
579+ ctx . registerService ( 'driver.batch' , mockDriver ) ;
580+ } ,
581+ } ) ;
582+
583+ const appManifest = {
584+ id : 'com.test.batchapp' ,
585+ name : 'batchapp' ,
586+ namespace : 'bat' ,
587+ version : '1.0.0' ,
588+ objects : [
589+ {
590+ name : 'alpha' ,
591+ label : 'Alpha' ,
592+ fields : { a : { name : 'a' , label : 'A' , type : 'text' } } ,
593+ } ,
594+ {
595+ name : 'beta' ,
596+ label : 'Beta' ,
597+ fields : { b : { name : 'b' , label : 'B' , type : 'text' } } ,
598+ } ,
599+ {
600+ name : 'gamma' ,
601+ label : 'Gamma' ,
602+ fields : { c : { name : 'c' , label : 'C' , type : 'text' } } ,
603+ } ,
604+ ] ,
605+ } ;
606+
607+ await kernel . use ( {
608+ name : 'mock-batch-app-plugin' ,
609+ type : 'app' ,
610+ version : '1.0.0' ,
611+ init : async ( ctx ) => {
612+ ctx . registerService ( 'app.batchapp' , appManifest ) ;
613+ } ,
614+ } ) ;
615+
616+ const plugin = new ObjectQLPlugin ( ) ;
617+ await kernel . use ( plugin ) ;
618+
619+ // Act
620+ await kernel . bootstrap ( ) ;
621+
622+ // Assert - syncSchemasBatch should have been called once with all objects
623+ expect ( batchCalls . length ) . toBe ( 1 ) ;
624+ const batchedObjects = batchCalls [ 0 ] . map ( ( s ) => s . object ) . sort ( ) ;
625+ expect ( batchedObjects ) . toContain ( 'bat__alpha' ) ;
626+ expect ( batchedObjects ) . toContain ( 'bat__beta' ) ;
627+ expect ( batchedObjects ) . toContain ( 'bat__gamma' ) ;
628+ // syncSchema should NOT have been called individually
629+ expect ( singleCalls . length ) . toBe ( 0 ) ;
630+ } ) ;
631+
632+ it ( 'should fall back to sequential syncSchema when batch fails' , async ( ) => {
633+ // Arrange - driver where batch fails
634+ const singleCalls : Array < { object : string ; schema : any } > = [ ] ;
635+ const mockDriver = {
636+ name : 'fallback-driver' ,
637+ version : '1.0.0' ,
638+ supports : { batchSchemaSync : true } ,
639+ connect : async ( ) => { } ,
640+ disconnect : async ( ) => { } ,
641+ find : async ( ) => [ ] ,
642+ findOne : async ( ) => null ,
643+ create : async ( _o : string , d : any ) => d ,
644+ update : async ( _o : string , _i : any , d : any ) => d ,
645+ delete : async ( ) => true ,
646+ syncSchema : async ( object : string , schema : any ) => {
647+ singleCalls . push ( { object, schema } ) ;
648+ } ,
649+ syncSchemasBatch : async ( ) => {
650+ throw new Error ( 'batch not supported at runtime' ) ;
651+ } ,
652+ } ;
653+
654+ await kernel . use ( {
655+ name : 'mock-fallback-driver-plugin' ,
656+ type : 'driver' ,
657+ version : '1.0.0' ,
658+ init : async ( ctx ) => {
659+ ctx . registerService ( 'driver.fallback' , mockDriver ) ;
660+ } ,
661+ } ) ;
662+
663+ const appManifest = {
664+ id : 'com.test.fallback' ,
665+ name : 'fallback' ,
666+ namespace : 'fb' ,
667+ version : '1.0.0' ,
668+ objects : [
669+ {
670+ name : 'one' ,
671+ label : 'One' ,
672+ fields : { x : { name : 'x' , label : 'X' , type : 'text' } } ,
673+ } ,
674+ {
675+ name : 'two' ,
676+ label : 'Two' ,
677+ fields : { y : { name : 'y' , label : 'Y' , type : 'text' } } ,
678+ } ,
679+ ] ,
680+ } ;
681+
682+ await kernel . use ( {
683+ name : 'mock-fallback-app-plugin' ,
684+ type : 'app' ,
685+ version : '1.0.0' ,
686+ init : async ( ctx ) => {
687+ ctx . registerService ( 'app.fallback' , appManifest ) ;
688+ } ,
689+ } ) ;
690+
691+ const plugin = new ObjectQLPlugin ( ) ;
692+ await kernel . use ( plugin ) ;
693+
694+ // Act - should not throw
695+ await expect ( kernel . bootstrap ( ) ) . resolves . not . toThrow ( ) ;
696+
697+ // Assert - sequential fallback should have been used
698+ const syncedObjects = singleCalls . map ( ( s ) => s . object ) . sort ( ) ;
699+ expect ( syncedObjects ) . toContain ( 'fb__one' ) ;
700+ expect ( syncedObjects ) . toContain ( 'fb__two' ) ;
701+ } ) ;
702+
703+ it ( 'should not use batch when driver does not support batchSchemaSync' , async ( ) => {
704+ // Arrange - driver without batch support (but with syncSchema)
705+ const singleCalls : string [ ] = [ ] ;
706+ const mockDriver = {
707+ name : 'nobatch-driver' ,
708+ version : '1.0.0' ,
709+ connect : async ( ) => { } ,
710+ disconnect : async ( ) => { } ,
711+ find : async ( ) => [ ] ,
712+ findOne : async ( ) => null ,
713+ create : async ( _o : string , d : any ) => d ,
714+ update : async ( _o : string , _i : any , d : any ) => d ,
715+ delete : async ( ) => true ,
716+ syncSchema : async ( object : string ) => {
717+ singleCalls . push ( object ) ;
718+ } ,
719+ } ;
720+
721+ await kernel . use ( {
722+ name : 'mock-nobatch-driver-plugin' ,
723+ type : 'driver' ,
724+ version : '1.0.0' ,
725+ init : async ( ctx ) => {
726+ ctx . registerService ( 'driver.nobatch' , mockDriver ) ;
727+ } ,
728+ } ) ;
729+
730+ const appManifest = {
731+ id : 'com.test.nobatch' ,
732+ name : 'nobatch' ,
733+ namespace : 'nb' ,
734+ version : '1.0.0' ,
735+ objects : [
736+ {
737+ name : 'item' ,
738+ label : 'Item' ,
739+ fields : { z : { name : 'z' , label : 'Z' , type : 'text' } } ,
740+ } ,
741+ ] ,
742+ } ;
743+
744+ await kernel . use ( {
745+ name : 'mock-nobatch-app-plugin' ,
746+ type : 'app' ,
747+ version : '1.0.0' ,
748+ init : async ( ctx ) => {
749+ ctx . registerService ( 'app.nobatch' , appManifest ) ;
750+ } ,
751+ } ) ;
752+
753+ const plugin = new ObjectQLPlugin ( ) ;
754+ await kernel . use ( plugin ) ;
755+
756+ // Act
757+ await kernel . bootstrap ( ) ;
758+
759+ // Assert - sequential syncSchema should have been used
760+ expect ( singleCalls ) . toContain ( 'nb__item' ) ;
761+ } ) ;
550762 } ) ;
551763} ) ;
0 commit comments