@@ -528,14 +528,30 @@ impl HALClient {
528528 async fn send_document ( & self , url : & str , body : & str , method : Method ) -> Result < Value , PactBrokerError > {
529529 debug ! ( "Sending JSON to {} using {}: {}" , url, method, body) ;
530530
531- let base_url = & self . url . parse :: < Url > ( ) ?;
532- let url = if url. starts_with ( "/" ) {
533- base_url. join ( url) ?
531+ // Extract path from URL if it's absolute (like fetch_url does), then resolve with context path
532+ let mut path = if let Ok ( link_as_url) = url. parse :: < Url > ( ) {
533+ // URL is absolute, extract path to use broker's original host and context path
534+ link_as_url. path ( ) . to_string ( )
534535 } else {
535- let url = url . parse :: < Url > ( ) ? ;
536- base_url . join ( & url. path ( ) ) ?
536+ // URL is already a path (relative)
537+ url. to_string ( )
537538 } ;
538539
540+ // If we have a context path and the path doesn't already include it, prepend it
541+ let broker_url = self . url . parse :: < Url > ( ) ?;
542+ let context_path = broker_url. path ( ) ;
543+ if !context_path. is_empty ( ) && context_path != "/" && path. starts_with ( "/" ) {
544+ let context_with_slash = format ! ( "{}/" , context_path) ;
545+ let path_matches_context = path == context_path || path. starts_with ( & context_with_slash) ;
546+ if !path_matches_context {
547+ // Path doesn't include context path, prepend it
548+ let full_path = format ! ( "{}{}" , context_path, path) ;
549+ path = full_path;
550+ }
551+ }
552+
553+ let url = self . resolve_path ( path. as_str ( ) ) ?;
554+
539555 let request_builder = match self . auth {
540556 Some ( ref auth) => match auth {
541557 HttpAuth :: User ( username, password) => self . client
@@ -2677,6 +2693,66 @@ mod tests {
26772693 expect ! ( result. path_info) . to ( be_some ( ) . value ( serde_json:: Value :: String ( "Yay! You found your way here" . to_string ( ) ) ) ) ;
26782694 }
26792695
2696+
2697+ #[ test]
2698+ fn resolve_path_handles_absolute_links_correctly ( ) {
2699+ let client = HALClientBuilder :: builder ( )
2700+ . with_url ( "http://127.0.0.1:8080/pact" , None )
2701+ . build ( ) ;
2702+
2703+ let path = "/pact/pacts/provider/Example%20API/for-verification" ;
2704+ let resolved = client. resolve_path ( path) . expect ( "Should resolve path" ) ;
2705+
2706+ expect ! ( resolved. path( ) ) . to ( be_equal_to ( "/pact/pacts/provider/Example%20API/for-verification" ) ) ;
2707+ }
2708+
2709+ #[ test_log:: test( tokio:: test) ]
2710+ async fn subpath_broker_templates_are_substituted ( ) {
2711+ let pact_broker = PactBuilderAsync :: new ( "RustPactVerifier" , "TemplateTest" )
2712+ . interaction ( "fetch root" , "" , |mut i| async move {
2713+ i. request . path ( "/pact" ) ;
2714+ i. response
2715+ . header ( "Content-Type" , "application/hal+json" )
2716+ . json_body ( json_pattern ! ( {
2717+ "_links" : {
2718+ "pb:test" : {
2719+ "href" : "http://localhost/pact/test/{id}" ,
2720+ "templated" : true
2721+ }
2722+ }
2723+ } ) ) ;
2724+ i
2725+ } )
2726+ . await
2727+ . interaction ( "fetch templated" , "" , |mut i| async move {
2728+ i. request . path ( "/pact/test/123" ) ;
2729+ i. response
2730+ . header ( "Content-Type" , "application/json" )
2731+ . json_body ( json_pattern ! ( "success" ) ) ;
2732+ i
2733+ } )
2734+ . await
2735+ . start_mock_server ( None , None ) ;
2736+
2737+ let base_url = pact_broker. url ( ) . to_string ( ) ;
2738+ let broker_url = if base_url. ends_with ( '/' ) {
2739+ format ! ( "{}pact" , base_url)
2740+ } else {
2741+ format ! ( "{}/pact" , base_url)
2742+ } ;
2743+
2744+ let client = HALClientBuilder :: builder ( )
2745+ . with_url ( broker_url, None )
2746+ . build ( ) ;
2747+
2748+ // Navigate to templated link
2749+ let mut template_vals = HashMap :: new ( ) ;
2750+ template_vals. insert ( "id" . to_string ( ) , "123" . to_string ( ) ) ;
2751+
2752+ let client_after = client. navigate ( "pb:test" , & template_vals) . await . unwrap ( ) ;
2753+ expect ! ( client_after. path_info) . to ( be_some ( ) ) ;
2754+ }
2755+
26802756 #[ test_log:: test( tokio:: test) ]
26812757 async fn navigate_takes_context_paths_into_account ( ) {
26822758 let pact_broker = PactBuilderAsync :: new ( "RustPactVerifier" , "PactBrokerStub" )
0 commit comments