@@ -60,6 +60,10 @@ pub struct IpInfoConfig {
6060
6161 // Default mapping of country codes to their respective continent code and name
6262 pub default_continents : Option < HashMap < String , Continent > > ,
63+
64+ /// Custom base URL for API requests (used for testing). If None, uses the default URL.
65+ #[ doc( hidden) ]
66+ pub base_url : Option < String > ,
6367}
6468
6569impl Default for IpInfoConfig {
@@ -73,6 +77,7 @@ impl Default for IpInfoConfig {
7377 default_flags : None ,
7478 default_currencies : None ,
7579 default_continents : None ,
80+ base_url : None ,
7681 }
7782 }
7883}
@@ -87,6 +92,7 @@ pub struct IpInfo {
8792 country_flags : HashMap < String , CountryFlag > ,
8893 country_currencies : HashMap < String , CountryCurrency > ,
8994 continents : HashMap < String , Continent > ,
95+ base_url : String ,
9096}
9197
9298pub struct BatchReqOpts {
@@ -130,6 +136,7 @@ impl IpInfo {
130136 country_flags : HashMap :: new ( ) ,
131137 country_currencies : HashMap :: new ( ) ,
132138 continents : HashMap :: new ( ) ,
139+ base_url : config. base_url . unwrap_or_else ( || BASE_URL . to_string ( ) ) ,
133140 } ;
134141
135142 if config. defaut_countries . is_none ( ) {
@@ -447,7 +454,7 @@ impl IpInfo {
447454 ) -> Result < ResproxyDetails , IpError > {
448455 let response = self
449456 . client
450- . get ( format ! ( "{BASE_URL }/resproxy/{ip}" ) )
457+ . get ( format ! ( "{}/resproxy/{ip}" , self . base_url ) )
451458 . headers ( Self :: construct_headers ( ) )
452459 . bearer_auth ( self . token . as_deref ( ) . unwrap_or_default ( ) )
453460 . send ( )
@@ -517,6 +524,8 @@ mod tests {
517524 use super :: * ;
518525 use crate :: IpErrorKind ;
519526 use std:: env;
527+ use wiremock:: matchers:: { method, path} ;
528+ use wiremock:: { Mock , MockServer , ResponseTemplate } ;
520529
521530 fn get_ipinfo_client ( ) -> IpInfo {
522531 IpInfo :: new ( IpInfoConfig {
@@ -666,22 +675,58 @@ mod tests {
666675
667676 #[ tokio:: test]
668677 async fn request_resproxy ( ) {
669- let ipinfo = get_ipinfo_client ( ) ;
678+ let mock_server = MockServer :: start ( ) . await ;
679+
680+ Mock :: given ( method ( "GET" ) )
681+ . and ( path ( "/resproxy/175.107.211.204" ) )
682+ . respond_with ( ResponseTemplate :: new ( 200 ) . set_body_json (
683+ serde_json:: json!( {
684+ "ip" : "175.107.211.204" ,
685+ "last_seen" : "2025-01-20" ,
686+ "percent_days_seen" : 0.85 ,
687+ "service" : "example_service"
688+ } ) ,
689+ ) )
690+ . mount ( & mock_server)
691+ . await ;
692+
693+ let ipinfo = IpInfo :: new ( IpInfoConfig {
694+ token : Some ( "test_token" . to_string ( ) ) ,
695+ base_url : Some ( mock_server. uri ( ) ) ,
696+ ..Default :: default ( )
697+ } )
698+ . expect ( "should construct" ) ;
670699
671700 let details = ipinfo
672701 . lookup_resproxy ( "175.107.211.204" )
673702 . await
674703 . expect ( "should lookup resproxy" ) ;
675704
676705 assert_eq ! ( details. ip, "175.107.211.204" ) ;
677- assert ! ( details. last_seen. is_some ( ) ) ;
678- assert ! ( details. percent_days_seen. is_some ( ) ) ;
679- assert ! ( details. service. is_some ( ) ) ;
706+ assert_eq ! ( details. last_seen, Some ( "2025-01-20" . to_string ( ) ) ) ;
707+ assert_eq ! ( details. percent_days_seen, Some ( 0.85 ) ) ;
708+ assert_eq ! ( details. service, Some ( "example_service" . to_string ( ) ) ) ;
680709 }
681710
682711 #[ tokio:: test]
683712 async fn request_resproxy_empty ( ) {
684- let ipinfo = get_ipinfo_client ( ) ;
713+ let mock_server = MockServer :: start ( ) . await ;
714+
715+ Mock :: given ( method ( "GET" ) )
716+ . and ( path ( "/resproxy/8.8.8.8" ) )
717+ . respond_with (
718+ ResponseTemplate :: new ( 200 )
719+ . set_body_json ( serde_json:: json!( { } ) ) ,
720+ )
721+ . mount ( & mock_server)
722+ . await ;
723+
724+ let ipinfo = IpInfo :: new ( IpInfoConfig {
725+ token : Some ( "test_token" . to_string ( ) ) ,
726+ base_url : Some ( mock_server. uri ( ) ) ,
727+ ..Default :: default ( )
728+ } )
729+ . expect ( "should construct" ) ;
685730
686731 let details = ipinfo
687732 . lookup_resproxy ( "8.8.8.8" )
0 commit comments