@@ -263,27 +263,34 @@ impl<S> Drop for DynamicRouteProvider<S> {
263263
264264#[ cfg( test) ]
265265mod tests {
266+ use candid:: Principal ;
266267 use reqwest:: Client ;
267268 use std:: {
268269 sync:: { Arc , Once } ,
269- time:: Duration ,
270+ time:: { Duration , Instant } ,
270271 } ;
271272 use tracing:: Level ;
272273 use tracing_subscriber:: FmtSubscriber ;
273274
274275 use crate :: {
275276 agent:: http_transport:: {
276277 dynamic_routing:: {
277- dynamic_route_provider:: { DynamicRouteProviderBuilder , IC0_SEED_DOMAIN } ,
278+ dynamic_route_provider:: {
279+ DynamicRouteProviderBuilder , IC0_SEED_DOMAIN , MAINNET_ROOT_SUBNET_ID ,
280+ } ,
278281 node:: Node ,
279- snapshot:: round_robin_routing:: RoundRobinRoutingSnapshot ,
282+ snapshot:: {
283+ latency_based_routing:: LatencyRoutingSnapshot ,
284+ round_robin_routing:: RoundRobinRoutingSnapshot ,
285+ } ,
280286 test_utils:: {
281287 assert_routed_domains, route_n_times, NodeHealthCheckerMock , NodesFetcherMock ,
282288 } ,
283289 } ,
284290 route_provider:: RouteProvider ,
291+ ReqwestTransport ,
285292 } ,
286- AgentError ,
293+ Agent , AgentError ,
287294 } ;
288295
289296 static TRACING_INIT : Once = Once :: new ( ) ;
@@ -294,6 +301,82 @@ mod tests {
294301 } ) ;
295302 }
296303
304+ async fn assert_no_routing_via_domains (
305+ route_provider : Arc < dyn RouteProvider > ,
306+ excluded_domains : Vec < & str > ,
307+ timeout : Duration ,
308+ route_call_interval : Duration ,
309+ ) {
310+ if excluded_domains. is_empty ( ) {
311+ panic ! ( "List of excluded domains can't be empty" ) ;
312+ }
313+
314+ let route_calls = 30 ;
315+ let start = Instant :: now ( ) ;
316+
317+ while start. elapsed ( ) < timeout {
318+ let routed_domains = ( 0 ..route_calls)
319+ . map ( |_| {
320+ route_provider. route ( ) . map ( |url| {
321+ let domain = url. domain ( ) . expect ( "no domain name in url" ) ;
322+ domain. to_string ( )
323+ } )
324+ } )
325+ . collect :: < Result < Vec < String > , _ > > ( )
326+ . unwrap_or_default ( ) ;
327+
328+ // Exit when excluded domains are not used for routing any more.
329+ if !routed_domains. is_empty ( )
330+ && !routed_domains
331+ . iter ( )
332+ . any ( |d| excluded_domains. contains ( & d. as_str ( ) ) )
333+ {
334+ return ;
335+ }
336+
337+ tokio:: time:: sleep ( route_call_interval) . await ;
338+ }
339+ panic ! ( "Expected excluded domains {excluded_domains:?} are still observed in routing over the last {route_calls} calls" ) ;
340+ }
341+
342+ #[ tokio:: test]
343+ async fn test_mainnet ( ) {
344+ // Setup.
345+ setup_tracing ( ) ;
346+ let seed = Node :: new ( IC0_SEED_DOMAIN ) . unwrap ( ) ;
347+ let client = Client :: builder ( ) . build ( ) . unwrap ( ) ;
348+ let route_provider = DynamicRouteProviderBuilder :: new (
349+ LatencyRoutingSnapshot :: new ( ) ,
350+ vec ! [ seed] ,
351+ client. clone ( ) ,
352+ )
353+ . build ( )
354+ . await ;
355+ let route_provider = Arc :: new ( route_provider) as Arc < dyn RouteProvider > ;
356+ let transport =
357+ ReqwestTransport :: create_with_client_route ( Arc :: clone ( & route_provider) , client)
358+ . expect ( "failed to create transport" ) ;
359+ let agent = Agent :: builder ( )
360+ . with_transport ( transport)
361+ . build ( )
362+ . expect ( "failed to create an agent" ) ;
363+ let subnet_id = Principal :: from_text ( MAINNET_ROOT_SUBNET_ID ) . unwrap ( ) ;
364+ // Assert that seed (ic0.app) is not used for routing. Henceforth, only discovered API nodes are used.
365+ assert_no_routing_via_domains (
366+ Arc :: clone ( & route_provider) ,
367+ vec ! [ IC0_SEED_DOMAIN ] ,
368+ Duration :: from_secs ( 40 ) ,
369+ Duration :: from_secs ( 2 ) ,
370+ )
371+ . await ;
372+ // Act: perform /read_state call via dynamically discovered API BNs.
373+ let api_bns = agent
374+ . fetch_api_boundary_nodes_by_subnet_id ( subnet_id)
375+ . await
376+ . expect ( "failed to fetch api boundary nodes" ) ;
377+ assert ! ( !api_bns. is_empty( ) ) ;
378+ }
379+
297380 #[ tokio:: test]
298381 async fn test_routing_with_topology_and_node_health_updates ( ) {
299382 // Setup.
0 commit comments