@@ -9,18 +9,19 @@ use std::net::{Ipv6Addr, SocketAddr};
99#[ cfg( feature = "plugins" ) ] use std:: net:: ToSocketAddrs ;
1010
1111use anyhow:: anyhow;
12- use itertools:: Either ;
12+ use itertools:: { Either , Itertools } ;
1313#[ cfg( feature = "plugins" ) ] use maplit:: hashmap;
1414use pact_models:: pact:: Pact ;
1515#[ cfg( feature = "plugins" ) ] use pact_models:: prelude:: v4:: V4Pact ;
1616#[ cfg( feature = "plugins" ) ] use pact_plugin_driver:: catalogue_manager:: { CatalogueEntry , CatalogueEntryProviderType } ;
1717#[ cfg( feature = "plugins" ) ] use pact_plugin_driver:: mock_server:: MockServerDetails ;
18+ use pact_plugin_driver:: plugin_manager:: get_mock_server_results;
1819#[ cfg( feature = "tls" ) ] use rustls:: ServerConfig ;
1920#[ cfg( not( feature = "plugins" ) ) ] use serde:: { Deserialize , Serialize } ;
21+ use serde_json:: { json, Value } ;
2022use tracing:: { debug, error, trace} ;
2123#[ cfg( feature = "plugins" ) ] use url:: Url ;
2224use crate :: builder:: MockServerBuilder ;
23-
2425use crate :: mock_server:: { MockServer , MockServerConfig } ;
2526
2627/// Mock server that has been provided by a plugin
@@ -521,6 +522,121 @@ impl ServerManager {
521522 false
522523 }
523524 }
525+
526+ fn mock_server_entry_by_port ( & self , port : u16 ) -> Option < & ServerEntry > {
527+ self . mock_servers
528+ . iter ( )
529+ . find_map ( |( _, entry) | if entry. port == port {
530+ Some ( entry)
531+ } else {
532+ None
533+ } )
534+ }
535+
536+ fn mock_server_entry_matched ( & self , entry : Option < & ServerEntry > ) -> Option < bool > {
537+ match entry {
538+ Some ( entry) => {
539+ #[ cfg( feature = "plugins" ) ]
540+ match & entry. mock_server {
541+ Either :: Left ( mock_server) => Some ( mock_server. all_matched ( ) ) ,
542+ Either :: Right ( plugin_mock_server) => {
543+ match self . exec_async ( get_mock_server_results ( & plugin_mock_server. mock_server_details ) ) {
544+ Ok ( results) => Some ( results. is_empty ( ) ) ,
545+ Err ( err) => {
546+ error ! ( "Request to plugin to get matching results failed - {}" , err) ;
547+ Some ( false )
548+ }
549+ }
550+ }
551+ }
552+
553+ #[ cfg( not( feature = "plugins" ) ) ]
554+ None
555+ }
556+ None => None
557+ }
558+ }
559+
560+ fn mock_server_entry_mismatches ( & self , entry : Option < & ServerEntry > ) -> anyhow:: Result < Option < Vec < Value > > > {
561+ match entry {
562+ Some ( entry) => {
563+ match & entry. mock_server {
564+ Either :: Left ( mock_server) => Ok ( Some ( mock_server. mismatches ( )
565+ . iter ( )
566+ . map ( |mismatches| mismatches. to_json ( ) )
567+ . collect ( ) ) ) ,
568+ Either :: Right ( plugin_mock_server) => {
569+ #[ cfg( feature = "plugins" ) ]
570+ match self . exec_async ( get_mock_server_results ( & plugin_mock_server. mock_server_details ) ) {
571+ Ok ( results) => Ok ( Some ( results
572+ . iter ( )
573+ . map ( |results| {
574+ json ! ( {
575+ "path" : results. path,
576+ "error" : results. error,
577+ "mismatches" : results. mismatches. iter( ) . map( |mismatch| {
578+ json!( {
579+ "expected" : mismatch. expected,
580+ "actual" : mismatch. actual,
581+ "mismatch" : mismatch. mismatch,
582+ "path" : mismatch. path,
583+ "diff" : mismatch. diff. clone( ) . unwrap_or_default( )
584+ } )
585+ } )
586+ . collect_vec( )
587+ } )
588+ } )
589+ . collect ( ) ) ) ,
590+ Err ( err) => {
591+ error ! ( port = entry. port, "Request to plugin to get mock server matching results failed - {}" , err) ;
592+ Err ( anyhow ! ( "Request to plugin to get mock server (port={}) matching results failed - {}" , entry. port, err) )
593+ }
594+ }
595+
596+ #[ cfg( not( feature = "plugins" ) ) ]
597+ Err ( anyhow ! ( "plugins feature is not enabled" ) )
598+ }
599+ }
600+ }
601+ None => Ok ( None )
602+ }
603+ }
604+
605+ /// Determines if the mock server running with the given ID has matched all its requests
606+ /// correctly. If there is no mock server running with that ID, it will return `None`.
607+ /// In the case the mock server has not received any requests, it will return `Some(true)`
608+ /// as this is the default state.
609+ pub fn mock_server_matched ( & self , id : & str ) -> Option < bool > {
610+ let entry = self . mock_servers . get ( id) ;
611+ self . mock_server_entry_matched ( entry)
612+ }
613+
614+ /// Determines if the mock server running on the given port has matched all its requests
615+ /// correctly. If there is no mock server running on that port, it will return `None`.
616+ /// In the case the mock server has not received any requests, it will return `Some(true)`
617+ /// as this is the default state.
618+ pub fn mock_server_matched_by_port ( & self , port : u16 ) -> Option < bool > {
619+ let entry = self . mock_server_entry_by_port ( port) ;
620+ self . mock_server_entry_matched ( entry)
621+ }
622+
623+ /// Returns all the mismatches from the mock server with the given ID. If there is no
624+ /// mock server with that ID, it will return `None`. The mismatch values are returned
625+ /// as JSON. This is to support mock servers provided by plugins, which can have a different
626+ /// format.
627+ pub fn mock_server_mismatches ( & self , id : & str ) -> anyhow:: Result < Option < Vec < Value > > > {
628+ let entry = self . mock_servers . get ( id) ;
629+ self . mock_server_entry_mismatches ( entry)
630+ }
631+
632+ /// Returns all the mismatches from the mock server running on the given port. If there is no
633+ /// mock server running on that port, it will return `None`. The mismatch values are returned
634+ /// as JSON. This is to support mock servers provided by plugins, which can have a different
635+ /// format.
636+ pub fn mock_server_mismatches_by_port ( & self , port : u16 ) -> anyhow:: Result < Option < Vec < Value > > > {
637+ let entry = self . mock_server_entry_by_port ( port) ;
638+ self . mock_server_entry_mismatches ( entry)
639+ }
524640}
525641
526642#[ cfg( test) ]
@@ -529,41 +645,99 @@ mod tests {
529645 use std:: net:: TcpStream ;
530646
531647 use env_logger;
648+ use expectest:: prelude:: * ;
649+ use hyper:: header:: ACCEPT ;
532650 use pact_models:: sync_pact:: RequestResponsePact ;
533651
534652 use super :: * ;
535653
536654 #[ test]
537- #[ cfg( not( target_os = "windows" ) ) ]
538- fn manager_should_start_and_shutdown_mock_server ( ) {
539- let _ = env_logger:: builder ( ) . is_test ( true ) . try_init ( ) ;
540- let mut manager = ServerManager :: new ( ) ;
541- #[ allow( deprecated) ]
542- let start_result = manager. start_mock_server ( "foobar" . into ( ) ,
543- RequestResponsePact :: default ( ) . boxed ( ) ,
544- 0 , MockServerConfig :: default ( ) ) ;
655+ #[ cfg( not( target_os = "windows" ) ) ]
656+ fn manager_should_start_and_shutdown_mock_server ( ) {
657+ let _ = env_logger:: builder ( ) . is_test ( true ) . try_init ( ) ;
658+ let mut manager = ServerManager :: new ( ) ;
659+ #[ allow( deprecated) ]
660+ let start_result = manager. start_mock_server ( "foobar" . into ( ) ,
661+ RequestResponsePact :: default ( ) . boxed ( ) ,
662+ 0 , MockServerConfig :: default ( ) ) ;
545663
546- assert ! ( start_result. is_ok( ) ) ;
547- let server_port = start_result. unwrap ( ) ;
664+ assert ! ( start_result. is_ok( ) ) ;
665+ let server_port = start_result. unwrap ( ) ;
548666
549- // Server should be up
550- assert ! ( TcpStream :: connect( ( "127.0.0.1" , server_port) ) . is_ok( ) ) ;
667+ // Server should be up
668+ assert ! ( TcpStream :: connect( ( "127.0.0.1" , server_port) ) . is_ok( ) ) ;
551669
552- // Should be able to read matches without blocking
553- let matches =
554- manager. find_mock_server_by_port_mut ( server_port, & |mock_server| mock_server. matches ( ) ) ;
555- assert_eq ! ( matches, Some ( vec![ ] ) ) ;
670+ // Should be able to read matches without blocking
671+ let matches =
672+ manager. find_mock_server_by_port_mut ( server_port, & |mock_server| mock_server. matches ( ) ) ;
673+ assert_eq ! ( matches, Some ( vec![ ] ) ) ;
556674
557- let stopped = manager. shutdown_mock_server_by_port ( server_port) ;
558- assert ! ( stopped) ;
675+ let stopped = manager. shutdown_mock_server_by_port ( server_port) ;
676+ assert ! ( stopped) ;
559677
560- // The tokio runtime is now out of tasks
561- drop ( manager) ;
678+ // The tokio runtime is now out of tasks
679+ drop ( manager) ;
562680
563- let millis = time:: Duration :: from_millis ( 100 ) ;
564- thread:: sleep ( millis) ;
681+ let millis = time:: Duration :: from_millis ( 100 ) ;
682+ thread:: sleep ( millis) ;
565683
566- // Server should be down
567- assert ! ( TcpStream :: connect( ( "127.0.0.1" , server_port) ) . is_err( ) ) ;
568- }
684+ // Server should be down
685+ assert ! ( TcpStream :: connect( ( "127.0.0.1" , server_port) ) . is_err( ) ) ;
686+ }
687+
688+ #[ test_log:: test]
689+ fn manager_can_return_mock_server_status ( ) {
690+ let pact = V4Pact :: default ( ) ;
691+ let mut manager = ServerManager :: new ( ) ;
692+ let mock_server = MockServerBuilder :: new ( )
693+ . bind_to ( "127.0.0.1:0" )
694+ . with_v4_pact ( pact)
695+ . attach_to_manager ( & mut manager)
696+ . unwrap ( )
697+ . unwrap_left ( ) ;
698+
699+ expect ! ( manager. mock_server_matched( "some value" ) ) . to ( be_none ( ) ) ;
700+ expect ! ( manager. mock_server_matched( mock_server. id. as_str( ) ) ) . to ( be_some ( ) . value ( true ) ) ;
701+ expect ! ( manager. mock_server_matched_by_port( 666 ) ) . to ( be_none ( ) ) ;
702+ expect ! ( manager. mock_server_matched_by_port( mock_server. port( ) ) ) . to ( be_some ( ) . value ( true ) ) ;
703+ }
704+
705+ #[ test_log:: test]
706+ fn manager_can_return_mock_server_mismatches ( ) {
707+ let pact = V4Pact :: default ( ) ;
708+ let mut manager = ServerManager :: new ( ) ;
709+ let mock_server = MockServerBuilder :: new ( )
710+ . bind_to ( "127.0.0.1:0" )
711+ . with_v4_pact ( pact)
712+ . attach_to_manager ( & mut manager)
713+ . unwrap ( )
714+ . unwrap_left ( ) ;
715+
716+ let port = mock_server. port ( ) ;
717+ let client = reqwest:: blocking:: Client :: new ( ) ;
718+ let _response = client. get ( format ! ( "http://127.0.0.1:{}" , port) . as_str ( ) )
719+ . header ( ACCEPT , "application/hal+json, application/json" )
720+ . send ( ) ;
721+
722+ let expected_result = vec ! [ json!( {
723+ "method" : "GET" ,
724+ "path" : "/" ,
725+ "request" : {
726+ "body" : "" ,
727+ "headers" : {
728+ "accept" : "application/hal+json, application/json" ,
729+ "host" : format!( "127.0.0.1:{}" , port)
730+ } ,
731+ "method" : "GET" ,
732+ "path" : "/"
733+ } ,
734+ "type" : "request-not-found"
735+ } ) ] ;
736+ expect ! ( manager. mock_server_mismatches( "some value" ) ) . to ( be_ok ( ) . value ( None ) ) ;
737+ expect ! ( manager. mock_server_mismatches( mock_server. id. as_str( ) ) ) . to ( be_ok ( )
738+ . value ( Some ( expected_result. clone ( ) ) ) ) ;
739+ expect ! ( manager. mock_server_mismatches_by_port( 666 ) ) . to ( be_ok ( ) . value ( None ) ) ;
740+ expect ! ( manager. mock_server_mismatches_by_port( mock_server. port( ) ) ) . to ( be_ok ( )
741+ . value ( Some ( expected_result) ) ) ;
742+ }
569743}
0 commit comments