@@ -670,13 +670,20 @@ describe('FeatureIngestionService', () => {
670670 . resolves ( mockFeatureTypeWithProperties ) ;
671671
672672 const deleteStub = sinon . stub ( SubmissionRepository . prototype , 'deleteSubmissionFeatures' ) . resolves ( ) ;
673+ const deleteRelationshipsStub = sinon
674+ . stub ( SubmissionRepository . prototype , 'deleteSubmissionFeatureRelationships' )
675+ . resolves ( ) ;
673676
674677 const insertStub = sinon . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRecord' ) ;
675678 insertStub . onFirstCall ( ) . resolves ( { submission_feature_id : 100 } ) ;
676679 insertStub . onSecondCall ( ) . resolves ( { submission_feature_id : 101 } ) ;
677680
678681 const updateParentStub = sinon . stub ( SubmissionRepository . prototype , 'updateSubmissionFeatureParent' ) . resolves ( ) ;
679682
683+ const insertRelationshipsStub = sinon
684+ . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRelationships' )
685+ . resolves ( ) ;
686+
680687 const features : IFlattenedBlock [ ] = [
681688 createValidFeature ( { id : 'uuid-1' , content : [ 'uuid-2' ] } ) ,
682689 createValidFeature ( { id : 'uuid-2' , parent : 'uuid-1' , content : [ ] } )
@@ -687,8 +694,10 @@ describe('FeatureIngestionService', () => {
687694 expect ( result . valid ) . to . be . true ;
688695 expect ( result . errors ) . to . have . length ( 0 ) ;
689696 expect ( deleteStub ) . to . have . been . calledOnceWith ( 1 ) ;
697+ expect ( deleteRelationshipsStub ) . to . have . been . calledOnceWith ( 1 ) ;
690698 expect ( insertStub ) . to . have . been . calledTwice ;
691699 expect ( updateParentStub ) . to . have . been . calledOnceWith ( 101 , 100 ) ;
700+ expect ( insertRelationshipsStub ) . to . have . been . calledOnceWith ( [ { source_feature_id : 100 , target_feature_id : 101 } ] ) ;
692701 } ) ;
693702
694703 it ( 'should return all errors when validation fails' , async ( ) => {
@@ -1037,7 +1046,7 @@ describe('FeatureIngestionService', () => {
10371046 expect ( insertStub . secondCall . args [ 4 ] ) . to . deep . equal ( propsMinimal ) ;
10381047 } ) ;
10391048
1040- it ( 'should call deleteSubmissionFeatures before inserting' , async ( ) => {
1049+ it ( 'should call deleteSubmissionFeatures and deleteSubmissionFeatureRelationships before inserting' , async ( ) => {
10411050 const mockDBConnection = getMockDBConnection ( ) ;
10421051 const service = new FeatureIngestionService ( mockDBConnection ) ;
10431052
@@ -1051,6 +1060,12 @@ describe('FeatureIngestionService', () => {
10511060 callOrder . push ( 'delete' ) ;
10521061 } ) ;
10531062
1063+ const deleteRelationshipsStub = sinon
1064+ . stub ( SubmissionRepository . prototype , 'deleteSubmissionFeatureRelationships' )
1065+ . callsFake ( async ( ) => {
1066+ callOrder . push ( 'deleteRelationships' ) ;
1067+ } ) ;
1068+
10541069 const insertStub = sinon
10551070 . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRecord' )
10561071 . callsFake ( async ( ) => {
@@ -1059,14 +1074,89 @@ describe('FeatureIngestionService', () => {
10591074 } ) ;
10601075
10611076 sinon . stub ( SubmissionRepository . prototype , 'updateSubmissionFeatureParent' ) . resolves ( ) ;
1077+ sinon . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRelationships' ) . resolves ( ) ;
10621078
10631079 const features : IFlattenedBlock [ ] = [ createValidFeature ( ) ] ;
10641080
10651081 await service . ingestFeatures ( 1 , features ) ;
10661082
10671083 expect ( callOrder [ 0 ] ) . to . equal ( 'delete' ) ;
1068- expect ( callOrder [ 1 ] ) . to . equal ( 'insert' ) ;
1069- expect ( deleteStub ) . to . have . been . calledBefore ( insertStub ) ;
1084+ expect ( callOrder [ 1 ] ) . to . equal ( 'deleteRelationships' ) ;
1085+ expect ( callOrder [ 2 ] ) . to . equal ( 'insert' ) ;
1086+ expect ( deleteStub ) . to . have . been . calledOnceWith ( 1 ) ;
1087+ expect ( deleteRelationshipsStub ) . to . have . been . calledOnceWith ( 1 ) ;
1088+ expect ( insertStub ) . to . have . been . calledOnce ;
1089+ } ) ;
1090+
1091+ it ( 'should not call insertSubmissionFeatureRelationships when all features have empty content' , async ( ) => {
1092+ const mockDBConnection = getMockDBConnection ( ) ;
1093+ const service = new FeatureIngestionService ( mockDBConnection ) ;
1094+
1095+ sinon
1096+ . stub ( ValidationRepository . prototype , 'getFeatureTypeWithProperties' )
1097+ . resolves ( mockFeatureTypeWithProperties ) ;
1098+
1099+ sinon . stub ( SubmissionRepository . prototype , 'deleteSubmissionFeatures' ) . resolves ( ) ;
1100+ sinon . stub ( SubmissionRepository . prototype , 'deleteSubmissionFeatureRelationships' ) . resolves ( ) ;
1101+
1102+ const insertStub = sinon
1103+ . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRecord' )
1104+ . resolves ( { submission_feature_id : 1 } ) ;
1105+
1106+ sinon . stub ( SubmissionRepository . prototype , 'updateSubmissionFeatureParent' ) . resolves ( ) ;
1107+
1108+ const insertRelationshipsStub = sinon
1109+ . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRelationships' )
1110+ . resolves ( ) ;
1111+
1112+ const features : IFlattenedBlock [ ] = [
1113+ createValidFeature ( { id : 'uuid-1' , content : [ ] , parent : null } ) ,
1114+ createValidFeature ( { id : 'uuid-2' , content : [ ] , parent : 'uuid-1' } )
1115+ ] ;
1116+
1117+ await service . ingestFeatures ( 1 , features ) ;
1118+
1119+ expect ( insertStub ) . to . have . been . calledTwice ;
1120+ expect ( insertRelationshipsStub ) . to . not . have . been . called ;
1121+ } ) ;
1122+
1123+ it ( 'should insert relationship rows for parent with multiple children' , async ( ) => {
1124+ const mockDBConnection = getMockDBConnection ( ) ;
1125+ const service = new FeatureIngestionService ( mockDBConnection ) ;
1126+
1127+ sinon
1128+ . stub ( ValidationRepository . prototype , 'getFeatureTypeWithProperties' )
1129+ . resolves ( mockFeatureTypeWithProperties ) ;
1130+
1131+ sinon . stub ( SubmissionRepository . prototype , 'deleteSubmissionFeatures' ) . resolves ( ) ;
1132+ sinon . stub ( SubmissionRepository . prototype , 'deleteSubmissionFeatureRelationships' ) . resolves ( ) ;
1133+
1134+ const insertStub = sinon . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRecord' ) ;
1135+ insertStub . onFirstCall ( ) . resolves ( { submission_feature_id : 10 } ) ;
1136+ insertStub . onSecondCall ( ) . resolves ( { submission_feature_id : 20 } ) ;
1137+ insertStub . onThirdCall ( ) . resolves ( { submission_feature_id : 30 } ) ;
1138+ insertStub . onCall ( 3 ) . resolves ( { submission_feature_id : 40 } ) ;
1139+
1140+ sinon . stub ( SubmissionRepository . prototype , 'updateSubmissionFeatureParent' ) . resolves ( ) ;
1141+
1142+ const insertRelationshipsStub = sinon
1143+ . stub ( SubmissionRepository . prototype , 'insertSubmissionFeatureRelationships' )
1144+ . resolves ( ) ;
1145+
1146+ const features : IFlattenedBlock [ ] = [
1147+ createValidFeature ( { id : 'parent' , parent : null , content : [ 'child-1' , 'child-2' , 'child-3' ] } ) ,
1148+ createValidFeature ( { id : 'child-1' , parent : 'parent' , content : [ ] } ) ,
1149+ createValidFeature ( { id : 'child-2' , parent : 'parent' , content : [ ] } ) ,
1150+ createValidFeature ( { id : 'child-3' , parent : 'parent' , content : [ ] } )
1151+ ] ;
1152+
1153+ await service . ingestFeatures ( 1 , features ) ;
1154+
1155+ expect ( insertRelationshipsStub ) . to . have . been . calledOnceWith ( [
1156+ { source_feature_id : 10 , target_feature_id : 20 } ,
1157+ { source_feature_id : 10 , target_feature_id : 30 } ,
1158+ { source_feature_id : 10 , target_feature_id : 40 }
1159+ ] ) ;
10701160 } ) ;
10711161
10721162 it ( 'should insert all features before updating any parent references' , async ( ) => {
0 commit comments