@@ -130,9 +130,10 @@ impl XMLPlanBuilder {
130130 }
131131
132132 let mut for_each_node = ExecutionPlanNode :: action ( "for-each" ) ;
133- for_each_node. add ( ExecutionPlanNode :: value_node ( format ! ( "{}*" , child_name) ) ) ;
133+ let marker = format ! ( "{}*" , child_name) ;
134+ for_each_node. add ( ExecutionPlanNode :: value_node ( marker. as_str ( ) ) ) ;
134135 for_each_node. add ( ExecutionPlanNode :: resolve_current_value ( & p) ) ;
135- let item_path = p . join ( "[*]" ) ;
136+ let item_path = path . join ( marker . as_str ( ) ) ;
136137
137138 self . process_element ( context, elements[ 0 ] , Some ( 0 ) , & item_path, & mut for_each_node) ;
138139
@@ -151,7 +152,7 @@ impl XMLPlanBuilder {
151152 let text_nodes = text_nodes ( element) ;
152153 let p = path. join ( "#text" ) ;
153154 let no_markers = remove_marker ( & p) ;
154- let no_indices = drop_indices ( & p ) ;
155+ let no_indices = drop_indices ( & no_markers ) ;
155156 let matchers = context. select_best_matcher_from ( & no_markers, & no_indices)
156157 . filter ( |matcher| !matcher. is_type_matcher ( ) )
157158 . remove_duplicates ( ) ;
@@ -288,12 +289,12 @@ impl PlanBodyBuilder for XMLPlanBuilder {
288289mod tests {
289290 use bytes:: Bytes ;
290291 use pretty_assertions:: assert_eq;
291-
292+ use pact_models:: matchingrules;
293+ use pact_models:: matchingrules:: MatchingRule ;
292294 use crate :: engine:: bodies:: { PlanBodyBuilder , XMLPlanBuilder } ;
293295 use crate :: engine:: context:: { MatchingConfiguration , PlanMatchingContext } ;
294296
295- #[ test]
296- #[ cfg( feature = "xml" ) ]
297+ #[ test_log:: test]
297298 fn xml_plan_builder_with_very_simple_xml ( ) {
298299 let builder = XMLPlanBuilder :: new ( ) ;
299300 let context = PlanMatchingContext :: default ( ) ;
@@ -331,8 +332,7 @@ mod tests {
331332)"# , buffer) ;
332333 }
333334
334- #[ test]
335- #[ cfg( feature = "xml" ) ]
335+ #[ test_log:: test]
336336 fn xml_plan_builder_with_allowed_unexpected_values ( ) {
337337 let builder = XMLPlanBuilder :: new ( ) ;
338338 let context = PlanMatchingContext {
@@ -373,8 +373,7 @@ mod tests {
373373)"# , buffer) ;
374374 }
375375
376- #[ test]
377- #[ cfg( feature = "xml" ) ]
376+ #[ test_log:: test]
378377 fn xml_plan_builder_with_simple_xml ( ) {
379378 let builder = XMLPlanBuilder :: new ( ) ;
380379 let context = PlanMatchingContext :: default ( ) ;
@@ -637,6 +636,246 @@ mod tests {
637636 )
638637 )
639638 )
639+ )"# , buffer) ;
640+ }
641+
642+ #[ test_log:: test]
643+ fn matching_rule_on_element_text ( ) {
644+ let builder = XMLPlanBuilder :: new ( ) ;
645+ let matching_rules = matchingrules ! {
646+ "body" => { "$.values.value" => [ MatchingRule :: Regex ( "\\ d+" . to_string( ) ) ] }
647+ } ;
648+ let context = PlanMatchingContext {
649+ matching_rules : matching_rules. rules_for_category ( "body" ) . unwrap_or_default ( ) ,
650+ .. PlanMatchingContext :: default ( )
651+ } ;
652+ let xml = r#"<?xml version="1.0" encoding="UTF-8"?> <values><value>100</value></values>"# ;
653+ let content = Bytes :: copy_from_slice ( xml. as_bytes ( ) ) ;
654+ let node = builder. build_plan ( & content, & context) . unwrap ( ) ;
655+ let mut buffer = String :: new ( ) ;
656+ node. pretty_form ( & mut buffer, 0 ) ;
657+
658+ assert_eq ! ( r#"%tee (
659+ %xml:parse (
660+ $.body
661+ ),
662+ :$ (
663+ %if (
664+ %check:exists (
665+ ~>$.values
666+ ),
667+ :$.values (
668+ :#text (
669+ %expect:empty (
670+ %to-string (
671+ ~>$.values['#text']
672+ )
673+ )
674+ ),
675+ %expect:only-entries (
676+ ['value'],
677+ ~>$.values
678+ ),
679+ %expect:count (
680+ UINT(1),
681+ ~>$.values.value,
682+ %join (
683+ 'Expected 1 <value> child element but there were ',
684+ %length (
685+ ~>$.values.value
686+ )
687+ )
688+ ),
689+ %if (
690+ %check:exists (
691+ ~>$.values.value[0]
692+ ),
693+ :$.values.value[0] (
694+ :#text (
695+ #{'#text must match the regular expression /\\d+/'},
696+ %match:regex (
697+ '100',
698+ %to-string (
699+ ~>$.values.value[0]['#text']
700+ ),
701+ json:{"regex":"\\d+"}
702+ )
703+ ),
704+ %expect:empty (
705+ ~>$.values.value[0]
706+ )
707+ ),
708+ %error (
709+ 'Was expecting an XML element /values/value/0 but it was missing'
710+ )
711+ )
712+ ),
713+ %error (
714+ 'Was expecting an XML element /values but it was missing'
715+ )
716+ )
717+ )
718+ )"# , buffer) ;
719+ }
720+
721+ #[ test_log:: test]
722+ fn matching_rule_on_attribute ( ) {
723+ let builder = XMLPlanBuilder :: new ( ) ;
724+ let matching_rules = matchingrules ! {
725+ "body" => { "$.value.@id" => [ MatchingRule :: Regex ( "\\ d+" . to_string( ) ) ] }
726+ } ;
727+ let context = PlanMatchingContext {
728+ matching_rules : matching_rules. rules_for_category ( "body" ) . unwrap_or_default ( ) ,
729+ .. PlanMatchingContext :: default ( )
730+ } ;
731+ let xml = r#"<?xml version="1.0" encoding="UTF-8"?> <value id="100"/>"# ;
732+ let content = Bytes :: copy_from_slice ( xml. as_bytes ( ) ) ;
733+ let node = builder. build_plan ( & content, & context) . unwrap ( ) ;
734+ let mut buffer = String :: new ( ) ;
735+ node. pretty_form ( & mut buffer, 0 ) ;
736+
737+ assert_eq ! ( r#"%tee (
738+ %xml:parse (
739+ $.body
740+ ),
741+ :$ (
742+ %if (
743+ %check:exists (
744+ ~>$.value
745+ ),
746+ :$.value (
747+ :attributes (
748+ :$.value['@id'] (
749+ #{'@id must match the regular expression /\\d+/'},
750+ %if (
751+ %check:exists (
752+ ~>$.value['@id']
753+ ),
754+ %match:regex (
755+ '100',
756+ %xml:value (
757+ ~>$.value['@id']
758+ ),
759+ json:{"regex":"\\d+"}
760+ )
761+ )
762+ ),
763+ %expect:entries (
764+ ['id'],
765+ %xml:attributes (
766+ ~>$.value
767+ ),
768+ %join (
769+ 'The following expected attributes were missing: ',
770+ %join-with (
771+ ', ',
772+ ** (
773+ %apply ()
774+ )
775+ )
776+ )
777+ ),
778+ %expect:only-entries (
779+ ['id'],
780+ %xml:attributes (
781+ ~>$.value
782+ )
783+ )
784+ ),
785+ :#text (
786+ %expect:empty (
787+ %to-string (
788+ ~>$.value['#text']
789+ )
790+ )
791+ ),
792+ %expect:empty (
793+ ~>$.value
794+ )
795+ ),
796+ %error (
797+ 'Was expecting an XML element /value but it was missing'
798+ )
799+ )
800+ )
801+ )"# , buffer) ;
802+ }
803+
804+ #[ test_log:: test]
805+ fn type_matching_rule_on_element ( ) {
806+ let builder = XMLPlanBuilder :: new ( ) ;
807+ let matching_rules = matchingrules ! {
808+ "body" => { "$.values" => [ MatchingRule :: MinType ( 2 ) ] }
809+ } ;
810+ let context = PlanMatchingContext {
811+ matching_rules : matching_rules. rules_for_category ( "body" ) . unwrap_or_default ( ) ,
812+ .. PlanMatchingContext :: default ( )
813+ } ;
814+ let xml = r#"<?xml version="1.0" encoding="UTF-8"?> <values><value>100</value><value>300</value></values>"# ;
815+ let content = Bytes :: copy_from_slice ( xml. as_bytes ( ) ) ;
816+ let node = builder. build_plan ( & content, & context) . unwrap ( ) ;
817+ let mut buffer = String :: new ( ) ;
818+ node. pretty_form ( & mut buffer, 0 ) ;
819+
820+ assert_eq ! ( r#"%tee (
821+ %xml:parse (
822+ $.body
823+ ),
824+ :$ (
825+ %if (
826+ %check:exists (
827+ ~>$.values
828+ ),
829+ :$.values (
830+ :#text (
831+ %expect:empty (
832+ %to-string (
833+ ~>$.values['#text']
834+ )
835+ )
836+ ),
837+ %expect:only-entries (
838+ ['value'],
839+ ~>$.values
840+ ),
841+ #{'value must match by type and have at least 2 items'},
842+ %match:min-type (
843+ xml:'<value>100</value>',
844+ ~>$.values.value,
845+ json:{"min":2}
846+ ),
847+ %for-each (
848+ 'value*',
849+ ~>$.values.value,
850+ %if (
851+ %check:exists (
852+ ~>$.values['value*']
853+ ),
854+ :$.values['value*'] (
855+ :#text (
856+ %match:equality (
857+ '100',
858+ %to-string (
859+ ~>$.values['value*']['#text']
860+ ),
861+ NULL
862+ )
863+ ),
864+ %expect:empty (
865+ ~>$.values['value*']
866+ )
867+ ),
868+ %error (
869+ 'Was expecting an XML element /values/value* but it was missing'
870+ )
871+ )
872+ )
873+ ),
874+ %error (
875+ 'Was expecting an XML element /values but it was missing'
876+ )
877+ )
878+ )
640879)"# , buffer) ;
641880 }
642881}
0 commit comments