@@ -4,6 +4,7 @@ import { OmniScriptMigrationTool, OmniScriptExportType } from '../../src/migrati
44import { NameMappingRegistry } from '../../src/migration/NameMappingRegistry' ;
55import { initializeDataModelService } from '../../src/utils/dataModelService' ;
66import { OmnistudioOrgDetails } from '../../src/utils/orgUtils' ;
7+ import { NetUtils } from '../../src/utils/net' ;
78
89describe ( 'OmniScript Standard Data Model (Metadata API Disabled) - Assessment and Migration' , ( ) => {
910 let omniScriptTool : OmniScriptMigrationTool ;
@@ -565,6 +566,246 @@ describe('OmniScript Standard Data Model (Metadata API Disabled) - Assessment an
565566 } ) ;
566567 } ) ;
567568
569+ describe ( 'Standard Data Model - Multi-Level Element Hierarchy Processing' , ( ) => {
570+ it ( 'should properly handle hierarchical elements with parent-child relationships in standard data model' , async ( ) => {
571+ // This test specifically catches the Map[property] vs Map.set() bug in uploadAllElements
572+ const mockElements = [
573+ // Level 0: Root Step element
574+ {
575+ Id : 'step001' ,
576+ Name : 'CustomerInfoStep' ,
577+ Type : 'Step' ,
578+ PropertySetConfig : JSON . stringify ( {
579+ label : 'Customer Information' ,
580+ } ) ,
581+ Level : 0 ,
582+ ParentElementId : null ,
583+ OmniProcessId : 'op123' ,
584+ } ,
585+ // Level 1: Integration Procedure child of Step
586+ {
587+ Id : 'ip001' ,
588+ Name : 'GetCustomerData' ,
589+ Type : 'Integration Procedure Action' ,
590+ PropertySetConfig : JSON . stringify ( {
591+ integrationProcedureKey : 'API-Gateway_Customer@Info!' ,
592+ timeout : 30 ,
593+ } ) ,
594+ Level : 1 ,
595+ ParentElementId : 'step001' ,
596+ OmniProcessId : 'op123' ,
597+ } ,
598+ // Level 1: DataRaptor child of Step
599+ {
600+ Id : 'dr001' ,
601+ Name : 'TransformCustomerData' ,
602+ Type : 'DataRaptor Transform Action' ,
603+ PropertySetConfig : JSON . stringify ( {
604+ bundle : 'Customer-Data@Loader!' ,
605+ inputType : 'JSON' ,
606+ } ) ,
607+ Level : 1 ,
608+ ParentElementId : 'step001' ,
609+ OmniProcessId : 'op123' ,
610+ } ,
611+ // Level 0: Another root element (Text)
612+ {
613+ Id : 'text001' ,
614+ Name : 'CustomerNameInput' ,
615+ Type : 'Text' ,
616+ PropertySetConfig : JSON . stringify ( {
617+ label : 'Customer Name' ,
618+ required : true ,
619+ } ) ,
620+ Level : 0 ,
621+ ParentElementId : null ,
622+ OmniProcessId : 'op123' ,
623+ } ,
624+ // Level 1: Child of Text element
625+ {
626+ Id : 'validate001' ,
627+ Name : 'ValidateCustomerName' ,
628+ Type : 'Formula' ,
629+ PropertySetConfig : JSON . stringify ( {
630+ formula : 'LENGTH({CustomerName}) > 2' ,
631+ } ) ,
632+ Level : 1 ,
633+ ParentElementId : 'text001' ,
634+ OmniProcessId : 'op123' ,
635+ } ,
636+ // Level 2: Grandchild element (child of Integration Procedure)
637+ {
638+ Id : 'nested001' ,
639+ Name : 'NestedValidation' ,
640+ Type : 'Messaging Framework' ,
641+ PropertySetConfig : JSON . stringify ( {
642+ message : 'Validating customer data...' ,
643+ } ) ,
644+ Level : 2 ,
645+ ParentElementId : 'ip001' ,
646+ OmniProcessId : 'op123' ,
647+ } ,
648+ ] ;
649+
650+ const mockUploadResult = {
651+ id : 'op123' ,
652+ success : true ,
653+ referenceId : 'originalId' ,
654+ hasErrors : false ,
655+ errors : [ ] ,
656+ warnings : [ ] ,
657+ newName : 'TestOmniProcess' ,
658+ skipped : false ,
659+ } ;
660+
661+ // Mock NetUtils.updateOne to simulate successful updates for standard data model
662+ const mockUpdateResponses = new Map ( [
663+ [
664+ 'step001' ,
665+ { id : 'step001' , success : true , referenceId : 'step001' , hasErrors : false , errors : [ ] , warnings : [ ] } ,
666+ ] ,
667+ [ 'ip001' , { id : 'ip001' , success : true , referenceId : 'ip001' , hasErrors : false , errors : [ ] , warnings : [ ] } ] ,
668+ [ 'dr001' , { id : 'dr001' , success : true , referenceId : 'dr001' , hasErrors : false , errors : [ ] , warnings : [ ] } ] ,
669+ [
670+ 'text001' ,
671+ { id : 'text001' , success : true , referenceId : 'text001' , hasErrors : false , errors : [ ] , warnings : [ ] } ,
672+ ] ,
673+ [
674+ 'validate001' ,
675+ { id : 'validate001' , success : true , referenceId : 'validate001' , hasErrors : false , errors : [ ] , warnings : [ ] } ,
676+ ] ,
677+ [
678+ 'nested001' ,
679+ { id : 'nested001' , success : true , referenceId : 'nested001' , hasErrors : false , errors : [ ] , warnings : [ ] } ,
680+ ] ,
681+ ] ) ;
682+
683+ // Mock NetUtils.updateOne method
684+ const originalUpdateOne = NetUtils . updateOne . bind ( NetUtils ) ;
685+ NetUtils . updateOne = async ( connection , objectName , referenceId , recordId ) => {
686+ return (
687+ mockUpdateResponses . get ( recordId ) || {
688+ success : false ,
689+ errors : [ 'Mock update failed' ] ,
690+ referenceId,
691+ hasErrors : true ,
692+ warnings : [ ] ,
693+ }
694+ ) ;
695+ } ;
696+
697+ try {
698+ // Call uploadAllElements directly to test the hierarchical processing
699+ const result = await ( omniScriptTool as any ) . uploadAllElements ( mockUploadResult , mockElements ) ;
700+
701+ // Verify that ALL elements were processed and stored correctly
702+ expect ( result ) . to . be . instanceOf ( Map ) ;
703+ expect ( result . size ) . to . equal ( 6 , 'Should have processed all 6 elements' ) ;
704+
705+ // Verify each element was processed at the correct level
706+ // Level 0 elements should be processed first
707+ expect ( result . has ( 'step001' ) ) . to . be . true ;
708+ expect ( result . has ( 'text001' ) ) . to . be . true ;
709+
710+ // Level 1 elements should be processed after their parents
711+ expect ( result . has ( 'ip001' ) ) . to . be . true ;
712+ expect ( result . has ( 'dr001' ) ) . to . be . true ;
713+ expect ( result . has ( 'validate001' ) ) . to . be . true ;
714+
715+ // Level 2 elements should be processed last
716+ expect ( result . has ( 'nested001' ) ) . to . be . true ;
717+
718+ // Verify the responses are correct UploadRecordResult objects
719+ const stepResult = result . get ( 'step001' ) ;
720+ expect ( stepResult . success ) . to . be . true ;
721+ expect ( stepResult . id ) . to . equal ( 'step001' ) ;
722+
723+ const ipResult = result . get ( 'ip001' ) ;
724+ expect ( ipResult . success ) . to . be . true ;
725+ expect ( ipResult . id ) . to . equal ( 'ip001' ) ;
726+
727+ const nestedResult = result . get ( 'nested001' ) ;
728+ expect ( nestedResult . success ) . to . be . true ;
729+ expect ( nestedResult . id ) . to . equal ( 'nested001' ) ;
730+
731+ // This test would FAIL with the original bug because:
732+ // elementsUploadResponse[standardElementId] = response would add properties to the Map object
733+ // but Array.from(elementsUploadResponse.entries()) would return empty array
734+ // so elementsUploadInfo would only contain elements from previous levels, not current level
735+ } finally {
736+ // Restore original method
737+ NetUtils . updateOne = originalUpdateOne ;
738+ }
739+ } ) ;
740+
741+ it ( 'should handle dependency mapping in hierarchical elements for standard data model' , ( ) => {
742+ const mockElements = [
743+ // Parent Step with nested dependencies
744+ {
745+ Id : 'step002' ,
746+ Name : 'ProcessingStep' ,
747+ Type : 'Step' ,
748+ PropertySetConfig : JSON . stringify ( {
749+ label : 'Data Processing Step' ,
750+ } ) ,
751+ Level : 0 ,
752+ ParentElementId : null ,
753+ OmniProcessId : 'op124' ,
754+ } ,
755+ // Child IP Action with special character dependencies
756+ {
757+ Id : 'ip002' ,
758+ Name : 'CustomerProcessing' ,
759+ Type : 'Integration Procedure Action' ,
760+ PropertySetConfig : JSON . stringify ( {
761+ integrationProcedureKey : 'API-Gateway_Customer@Info!' ,
762+ preTransformBundle : 'Customer-Data@Loader!' ,
763+ postTransformBundle : 'Product#Info$Extractor' ,
764+ remoteOptions : {
765+ preTransformBundle : 'Customer-Data@Loader!' ,
766+ postTransformBundle : 'Product#Info$Extractor' ,
767+ } ,
768+ } ) ,
769+ Level : 1 ,
770+ ParentElementId : 'step002' ,
771+ OmniProcessId : 'op124' ,
772+ } ,
773+ // Child DR Action
774+ {
775+ Id : 'dr002' ,
776+ Name : 'DataExtraction' ,
777+ Type : 'DataRaptor Extract Action' ,
778+ PropertySetConfig : JSON . stringify ( {
779+ bundle : 'Customer-Data@Loader!' ,
780+ } ) ,
781+ Level : 1 ,
782+ ParentElementId : 'step002' ,
783+ OmniProcessId : 'op124' ,
784+ } ,
785+ ] ;
786+
787+ // Test element mapping with dependencies
788+ const ipResult = ( omniScriptTool as any ) . mapElementData ( mockElements [ 1 ] , 'op124' , new Map ( ) , new Map ( ) ) ;
789+ const drResult = ( omniScriptTool as any ) . mapElementData ( mockElements [ 2 ] , 'op124' , new Map ( ) , new Map ( ) ) ;
790+
791+ // Verify IP Action dependency mapping
792+ const ipPropertySet = JSON . parse ( ipResult . PropertySetConfig ) ;
793+ expect ( ipPropertySet . integrationProcedureKey ) . to . equal ( 'APIGateway_CustomerInfo' ) ;
794+ expect ( ipPropertySet . preTransformBundle ) . to . equal ( 'CustomerDataLoader' ) ;
795+ expect ( ipPropertySet . postTransformBundle ) . to . equal ( 'ProductInfoExtractor' ) ;
796+ expect ( ipPropertySet . remoteOptions . preTransformBundle ) . to . equal ( 'CustomerDataLoader' ) ;
797+ expect ( ipPropertySet . remoteOptions . postTransformBundle ) . to . equal ( 'ProductInfoExtractor' ) ;
798+
799+ // Verify DR Action dependency mapping
800+ const drPropertySet = JSON . parse ( drResult . PropertySetConfig ) ;
801+ expect ( drPropertySet . bundle ) . to . equal ( 'CustomerDataLoader' ) ;
802+
803+ // Verify OmniProcessId is set correctly for standard data model
804+ expect ( ipResult . OmniProcessId ) . to . equal ( 'op124' ) ;
805+ expect ( drResult . OmniProcessId ) . to . equal ( 'op124' ) ;
806+ } ) ;
807+ } ) ;
808+
568809 describe ( 'Standard Data Model - Error Scenarios and Edge Cases' , ( ) => {
569810 it ( 'should handle empty or null dependency references' , ( ) => {
570811 const mockElementRecord = {
@@ -585,5 +826,55 @@ describe('OmniScript Standard Data Model (Metadata API Disabled) - Assessment an
585826 expect ( propertySet . integrationProcedureKey ) . to . equal ( '' ) ;
586827 expect ( propertySet . preTransformBundle ) . to . equal ( null ) ;
587828 } ) ;
829+
830+ it ( 'should handle element processing failure in hierarchical structure' , async ( ) => {
831+ const mockElements = [
832+ {
833+ Id : 'step003' ,
834+ Name : 'FailingStep' ,
835+ Type : 'Step' ,
836+ PropertySetConfig : JSON . stringify ( { } ) ,
837+ Level : 0 ,
838+ ParentElementId : null ,
839+ OmniProcessId : 'op125' ,
840+ } ,
841+ ] ;
842+
843+ const mockUploadResult = {
844+ id : 'op125' ,
845+ success : true ,
846+ referenceId : 'originalId' ,
847+ hasErrors : false ,
848+ errors : [ ] ,
849+ warnings : [ ] ,
850+ newName : 'TestFailingProcess' ,
851+ skipped : false ,
852+ } ;
853+
854+ // Mock NetUtils.updateOne to simulate failure
855+ const originalUpdateOne = NetUtils . updateOne . bind ( NetUtils ) ;
856+ NetUtils . updateOne = async ( connection , objectName , referenceId ) => ( {
857+ success : false ,
858+ errors : [ 'Simulated update failure' ] ,
859+ hasErrors : true ,
860+ referenceId,
861+ warnings : [ ] ,
862+ } ) ;
863+
864+ try {
865+ const result = await ( omniScriptTool as any ) . uploadAllElements ( mockUploadResult , mockElements ) ;
866+
867+ // Should still return a Map even if updates fail
868+ expect ( result ) . to . be . instanceOf ( Map ) ;
869+ expect ( result . size ) . to . equal ( 1 ) ;
870+
871+ const failedResult = result . get ( 'step003' ) ;
872+ expect ( failedResult . success ) . to . be . false ;
873+ expect ( failedResult . errors ) . to . include ( 'Simulated update failure' ) ;
874+ } finally {
875+ // Restore original method
876+ NetUtils . updateOne = originalUpdateOne ;
877+ }
878+ } ) ;
588879 } ) ;
589880} ) ;
0 commit comments