@@ -1069,6 +1069,18 @@ fn from_integration_json_v2(
10691069 }
10701070
10711071 result_value
1072+ } else if map. contains_key ( "expression" ) {
1073+ debug ! ( "detected 'expression' without 'pact:matcher:type', configuring ProviderStateGenerator" ) ;
1074+ if let Some ( generator) = Generator :: from_map ( "ProviderState" , map) {
1075+ let category = generator_category ( matching_rules) ;
1076+ let gen_path = if path_or_status {
1077+ path. parent ( ) . unwrap_or ( DocPath :: root ( ) )
1078+ } else {
1079+ path. clone ( )
1080+ } ;
1081+ generators. add_generator_with_subcategory ( category, gen_path, generator) ;
1082+ }
1083+ map. get ( "value" ) . cloned ( ) . unwrap_or_default ( )
10721084 } else {
10731085 debug ! ( "Configuring a normal value using the 'value' attribute" ) ;
10741086 map. get ( "value" ) . cloned ( ) . unwrap_or_default ( )
@@ -3488,6 +3500,82 @@ mod tests {
34883500 . to ( be_equal_to ( Either :: Right ( vec ! [ "100" . to_string( ) , "200" . to_string( ) ] ) ) ) ;
34893501 }
34903502
3503+ // Issue #460 - ProviderStateGenerator shorthand with expression key (no pact:matcher:type)
3504+ #[ test_log:: test]
3505+ fn from_integration_json_expression_provider_state_generator ( ) {
3506+ let mut rules = MatchingRules :: default ( ) ;
3507+ let mut generators = Generators :: default ( ) ;
3508+ let mut path = DocPath :: root ( ) ;
3509+ path. push_field ( "accountNumber" ) ;
3510+
3511+ let result = from_integration_json_v2 (
3512+ & mut rules, & mut generators,
3513+ r#"{"expression":"${accountNumber}","value":"100"}"# ,
3514+ path. clone ( ) , "query" , 0
3515+ ) ;
3516+
3517+ expect ! ( result) . to ( be_equal_to ( Either :: Left ( "100" . to_string ( ) ) ) ) ;
3518+ let gen_cat = generators. categories . get ( & pact_models:: generators:: GeneratorCategory :: QUERY ) ;
3519+ assert ! ( gen_cat. is_some( ) , "Expected QUERY generator category to be present" ) ;
3520+ let gen_path = DocPath :: root ( ) . join ( "accountNumber" ) ;
3521+ let gen = gen_cat. unwrap ( ) . get ( & gen_path) ;
3522+ assert_eq ! ( gen , Some ( & pact_models:: generators:: Generator :: ProviderStateGenerator ( "${accountNumber}" . to_string( ) , None ) ) ) ;
3523+ }
3524+
3525+ // Issue #460 - query parameter with ProviderStateGenerator shorthand (no pact:matcher:type)
3526+ #[ test_log:: test]
3527+ fn query_with_provider_state_generator_shorthand ( ) {
3528+ let pact_handle = PactHandle :: new ( "TestQPSG1" , "TestQPSGP1" ) ;
3529+ let description = CString :: new ( "query_with_provider_state_generator_shorthand" ) . unwrap ( ) ;
3530+ let handle = pactffi_new_interaction ( pact_handle, description. as_ptr ( ) ) ;
3531+
3532+ let name = CString :: new ( "accountNumber" ) . unwrap ( ) ;
3533+ let value = CString :: new ( r#"{"expression":"${accountNumber}","value":"100"}"# ) . unwrap ( ) ;
3534+ pactffi_with_query_parameter_v2 ( handle, name. as_ptr ( ) , 0 , value. as_ptr ( ) ) ;
3535+
3536+ let interaction = handle. with_interaction ( & |_, _, inner| {
3537+ inner. as_v4_http ( ) . unwrap ( )
3538+ } ) . unwrap ( ) ;
3539+
3540+ pactffi_free_pact_handle ( pact_handle) ;
3541+
3542+ expect ! ( interaction. request. query. clone( ) ) . to ( be_some ( ) . value ( hashmap ! {
3543+ "accountNumber" . to_string( ) => vec![ Some ( "100" . to_string( ) ) ]
3544+ } ) ) ;
3545+ expect ! ( & interaction. request. generators) . to ( be_equal_to ( & generators ! {
3546+ "query" => {
3547+ "$.accountNumber" => pact_models:: generators:: Generator :: ProviderStateGenerator ( "${accountNumber}" . to_string( ) , None )
3548+ }
3549+ } ) ) ;
3550+ }
3551+
3552+ // Issue #460 - header with ProviderStateGenerator shorthand (no pact:matcher:type)
3553+ #[ test_log:: test]
3554+ fn header_with_provider_state_generator_shorthand ( ) {
3555+ let pact_handle = PactHandle :: new ( "TestHPSG1" , "TestHPSGP1" ) ;
3556+ let description = CString :: new ( "header_with_provider_state_generator_shorthand" ) . unwrap ( ) ;
3557+ let handle = pactffi_new_interaction ( pact_handle, description. as_ptr ( ) ) ;
3558+
3559+ let name = CString :: new ( "x-account-id" ) . unwrap ( ) ;
3560+ let value = CString :: new ( r#"{"expression":"${accountId}","value":"ABC123"}"# ) . unwrap ( ) ;
3561+ pactffi_with_header_v2 ( handle, InteractionPart :: Request , name. as_ptr ( ) , 0 , value. as_ptr ( ) ) ;
3562+
3563+ let interaction = handle. with_interaction ( & |_, _, inner| {
3564+ inner. as_v4_http ( ) . unwrap ( )
3565+ } ) . unwrap ( ) ;
3566+
3567+ pactffi_free_pact_handle ( pact_handle) ;
3568+
3569+ expect ! ( interaction. request. headers. clone( ) ) . to ( be_some ( ) . value ( hashmap ! {
3570+ "x-account-id" . to_string( ) => vec![ "ABC123" . to_string( ) ]
3571+ } ) ) ;
3572+ expect ! ( & interaction. request. generators) . to ( be_equal_to ( & generators ! {
3573+ "header" => {
3574+ "$['x-account-id']" => pact_models:: generators:: Generator :: ProviderStateGenerator ( "${accountId}" . to_string( ) , None )
3575+ }
3576+ } ) ) ;
3577+ }
3578+
34913579 #[ test]
34923580 fn pactffi_with_metadata_async ( ) {
34933581 let pact_handle = PactHandle :: new ( "metadata-consumer" , "metadata-provider" ) ;
@@ -4604,4 +4692,37 @@ mod tests {
46044692 expect ! ( headers. get( "Content-Type" ) . unwrap( ) . first( ) . unwrap( ) )
46054693 . to ( be_equal_to ( & JSON . to_string ( ) ) ) ;
46064694 }
4695+
4696+ // Issue #460 - body with ProviderStateGenerator shorthand (expression key, no pact:matcher:type)
4697+ #[ test_log:: test]
4698+ fn body_with_provider_state_generator_shorthand ( ) {
4699+ let pact_handle = PactHandle :: new ( "TestBPSG1" , "TestBPSGP1" ) ;
4700+ let description = CString :: new ( "body_with_provider_state_generator_shorthand" ) . unwrap ( ) ;
4701+ let i_handle = pactffi_new_interaction ( pact_handle, description. as_ptr ( ) ) ;
4702+
4703+ let json_ct = CString :: new ( JSON . to_string ( ) ) . unwrap ( ) ;
4704+ // Body where accountNumber field uses the expression shorthand for ProviderStateGenerator
4705+ let body = CString :: new ( r#"{"accountNumber":{"expression":"${accountNumber}","value":100}}"# ) . unwrap ( ) ;
4706+ let result = pactffi_with_body ( i_handle, InteractionPart :: Request , json_ct. as_ptr ( ) , body. as_ptr ( ) ) ;
4707+
4708+ let interaction = i_handle
4709+ . with_interaction ( & |_, _, inner| inner. as_v4_http ( ) . unwrap ( ) )
4710+ . unwrap ( ) ;
4711+
4712+ pactffi_free_pact_handle ( pact_handle) ;
4713+
4714+ expect ! ( result) . to ( be_true ( ) ) ;
4715+
4716+ // The body should have the value extracted (100), not the full expression object
4717+ let body_bytes = interaction. request . body . value ( ) . unwrap ( ) ;
4718+ let body_json: serde_json:: Value = serde_json:: from_slice ( & body_bytes) . unwrap ( ) ;
4719+ assert_eq ! ( body_json, json!( { "accountNumber" : 100 } ) ) ;
4720+
4721+ // A ProviderStateGenerator should be configured for the accountNumber field
4722+ expect ! ( & interaction. request. generators) . to ( be_equal_to ( & generators ! {
4723+ "body" => {
4724+ "$.accountNumber" => pact_models:: generators:: Generator :: ProviderStateGenerator ( "${accountNumber}" . to_string( ) , None )
4725+ }
4726+ } ) ) ;
4727+ }
46074728}
0 commit comments