@@ -453,4 +453,243 @@ mod test {
453
453
}
454
454
}
455
455
}
456
+
457
+ #[ test]
458
+ fn test_uplink_config_for_tests ( ) {
459
+ let endpoints = Endpoints :: fallback ( vec ! [
460
+ Url :: from_str( "http://test1.example.com" ) . unwrap( ) ,
461
+ Url :: from_str( "http://test2.example.com" ) . unwrap( ) ,
462
+ ] ) ;
463
+
464
+ let config = UplinkConfig :: for_tests ( endpoints. clone ( ) ) ;
465
+
466
+ assert_eq ! ( config. apollo_key. expose_secret( ) , "key" ) ;
467
+ assert_eq ! ( config. apollo_graph_ref, "graph" ) ;
468
+ assert_eq ! ( config. poll_interval, Duration :: from_secs( 2 ) ) ;
469
+ assert_eq ! ( config. timeout, Duration :: from_secs( 5 ) ) ;
470
+
471
+ // Check endpoints
472
+ if let Some ( Endpoints :: Fallback { urls } ) = config. endpoints {
473
+ assert_eq ! ( urls. len( ) , 2 ) ;
474
+ assert_eq ! ( urls[ 0 ] . as_str( ) , "http://test1.example.com/" ) ;
475
+ assert_eq ! ( urls[ 1 ] . as_str( ) , "http://test2.example.com/" ) ;
476
+ } else {
477
+ panic ! ( "Expected fallback endpoints" ) ;
478
+ }
479
+ }
480
+
481
+ #[ test]
482
+ fn test_endpoints_fallback ( ) {
483
+ let urls = vec ! [
484
+ Url :: from_str( "http://test1.example.com" ) . unwrap( ) ,
485
+ Url :: from_str( "http://test2.example.com" ) . unwrap( ) ,
486
+ ] ;
487
+ let endpoints = Endpoints :: fallback ( urls. clone ( ) ) ;
488
+
489
+ if let Endpoints :: Fallback {
490
+ urls : fallback_urls,
491
+ } = endpoints
492
+ {
493
+ assert_eq ! ( fallback_urls. len( ) , 2 ) ;
494
+ assert_eq ! ( fallback_urls[ 0 ] , urls[ 0 ] ) ;
495
+ assert_eq ! ( fallback_urls[ 1 ] , urls[ 1 ] ) ;
496
+ } else {
497
+ panic ! ( "Expected fallback endpoints" ) ;
498
+ }
499
+ }
500
+
501
+ #[ test]
502
+ fn test_endpoints_round_robin ( ) {
503
+ let urls = vec ! [
504
+ Url :: from_str( "http://test1.example.com" ) . unwrap( ) ,
505
+ Url :: from_str( "http://test2.example.com" ) . unwrap( ) ,
506
+ ] ;
507
+ let endpoints = Endpoints :: round_robin ( urls. clone ( ) ) ;
508
+
509
+ if let Endpoints :: RoundRobin {
510
+ urls : rr_urls,
511
+ current,
512
+ } = endpoints
513
+ {
514
+ assert_eq ! ( rr_urls. len( ) , 2 ) ;
515
+ assert_eq ! ( rr_urls[ 0 ] , urls[ 0 ] ) ;
516
+ assert_eq ! ( rr_urls[ 1 ] , urls[ 1 ] ) ;
517
+ assert_eq ! ( current, 0 ) ;
518
+ } else {
519
+ panic ! ( "Expected round robin endpoints" ) ;
520
+ }
521
+ }
522
+
523
+ #[ test]
524
+ fn test_endpoints_url_count ( ) {
525
+ let urls = vec ! [
526
+ Url :: from_str( "http://test1.example.com" ) . unwrap( ) ,
527
+ Url :: from_str( "http://test2.example.com" ) . unwrap( ) ,
528
+ Url :: from_str( "http://test3.example.com" ) . unwrap( ) ,
529
+ ] ;
530
+
531
+ let fallback = Endpoints :: fallback ( urls. clone ( ) ) ;
532
+ assert_eq ! ( fallback. url_count( ) , 3 ) ;
533
+
534
+ let round_robin = Endpoints :: round_robin ( urls) ;
535
+ assert_eq ! ( round_robin. url_count( ) , 3 ) ;
536
+ }
537
+
538
+ #[ test]
539
+ fn test_endpoints_iter_fallback ( ) {
540
+ let urls = vec ! [
541
+ Url :: from_str( "http://test1.example.com" ) . unwrap( ) ,
542
+ Url :: from_str( "http://test2.example.com" ) . unwrap( ) ,
543
+ ] ;
544
+ let mut endpoints = Endpoints :: fallback ( urls. clone ( ) ) ;
545
+
546
+ {
547
+ let iter_urls: Vec < & Url > = endpoints. iter ( ) . collect ( ) ;
548
+ assert_eq ! ( iter_urls. len( ) , 2 ) ;
549
+ assert_eq ! ( iter_urls[ 0 ] , & urls[ 0 ] ) ;
550
+ assert_eq ! ( iter_urls[ 1 ] , & urls[ 1 ] ) ;
551
+ }
552
+
553
+ // Fallback should always return the same order
554
+ {
555
+ let iter_urls2: Vec < & Url > = endpoints. iter ( ) . collect ( ) ;
556
+ assert_eq ! ( iter_urls2. len( ) , 2 ) ;
557
+ assert_eq ! ( iter_urls2[ 0 ] , & urls[ 0 ] ) ;
558
+ assert_eq ! ( iter_urls2[ 1 ] , & urls[ 1 ] ) ;
559
+ }
560
+ }
561
+
562
+ #[ test]
563
+ fn test_endpoints_iter_round_robin ( ) {
564
+ let urls = vec ! [
565
+ Url :: from_str( "http://test1.example.com" ) . unwrap( ) ,
566
+ Url :: from_str( "http://test2.example.com" ) . unwrap( ) ,
567
+ Url :: from_str( "http://test3.example.com" ) . unwrap( ) ,
568
+ ] ;
569
+ let mut endpoints = Endpoints :: round_robin ( urls. clone ( ) ) ;
570
+
571
+ // First iteration should start at index 0
572
+ {
573
+ let iter_urls1: Vec < & Url > = endpoints. iter ( ) . collect ( ) ;
574
+ assert_eq ! ( iter_urls1. len( ) , 3 ) ;
575
+ assert_eq ! ( iter_urls1[ 0 ] , & urls[ 0 ] ) ;
576
+ assert_eq ! ( iter_urls1[ 1 ] , & urls[ 1 ] ) ;
577
+ assert_eq ! ( iter_urls1[ 2 ] , & urls[ 2 ] ) ;
578
+ }
579
+
580
+ // Second iteration should start at index 3 (current incremented to 3, then mod 3 = 0)
581
+ // But since the inspect closure increments current for each item yielded,
582
+ // the actual behavior is that current advances as the iterator is consumed
583
+ {
584
+ let iter_urls2: Vec < & Url > = endpoints. iter ( ) . collect ( ) ;
585
+ assert_eq ! ( iter_urls2. len( ) , 3 ) ;
586
+ // After the first iteration consumed 3 items, current should be 3, then 3 % 3 = 0
587
+ assert_eq ! ( iter_urls2[ 0 ] , & urls[ 0 ] ) ;
588
+ assert_eq ! ( iter_urls2[ 1 ] , & urls[ 1 ] ) ;
589
+ assert_eq ! ( iter_urls2[ 2 ] , & urls[ 2 ] ) ;
590
+ }
591
+ }
592
+
593
+ #[ test]
594
+ fn test_endpoints_default ( ) {
595
+ let endpoints = Endpoints :: default ( ) ;
596
+ assert_eq ! ( endpoints. url_count( ) , 2 ) ; // GCP_URL and AWS_URL
597
+
598
+ if let Endpoints :: Fallback { urls } = endpoints {
599
+ // URLs parsed with trailing slash
600
+ assert_eq ! ( urls[ 0 ] . as_str( ) , "https://uplink.api.apollographql.com/" ) ;
601
+ assert_eq ! (
602
+ urls[ 1 ] . as_str( ) ,
603
+ "https://aws.uplink.api.apollographql.com/"
604
+ ) ;
605
+ } else {
606
+ panic ! ( "Expected fallback endpoints" ) ;
607
+ }
608
+ }
609
+
610
+ #[ test]
611
+ fn test_uplink_config_default ( ) {
612
+ let config = UplinkConfig :: default ( ) ;
613
+
614
+ assert_eq ! ( config. apollo_key. expose_secret( ) , "" ) ;
615
+ assert_eq ! ( config. apollo_graph_ref, "" ) ;
616
+ assert ! ( config. endpoints. is_none( ) ) ;
617
+ assert_eq ! ( config. poll_interval, Duration :: from_secs( 0 ) ) ;
618
+ assert_eq ! ( config. timeout, Duration :: from_secs( 0 ) ) ;
619
+ }
620
+
621
+ #[ test]
622
+ fn test_error_display ( ) {
623
+ let error1 = Error :: FetchFailedSingle ;
624
+ assert_eq ! (
625
+ error1. to_string( ) ,
626
+ "fetch failed from uplink endpoint, and there are no fallback endpoints configured"
627
+ ) ;
628
+
629
+ let error2 = Error :: FetchFailedMultiple { url_count : 3 } ;
630
+ assert_eq ! (
631
+ error2. to_string( ) ,
632
+ "fetch failed from all 3 uplink endpoints"
633
+ ) ;
634
+
635
+ let error3 = Error :: UplinkError {
636
+ code : "AUTH_FAILED" . to_string ( ) ,
637
+ message : "Invalid API key" . to_string ( ) ,
638
+ } ;
639
+ assert_eq ! (
640
+ error3. to_string( ) ,
641
+ "uplink error: code=AUTH_FAILED message=Invalid API key"
642
+ ) ;
643
+
644
+ let error4 = Error :: UplinkErrorNoRetry {
645
+ code : "UNKNOWN_REF" . to_string ( ) ,
646
+ message : "Graph not found" . to_string ( ) ,
647
+ } ;
648
+ assert_eq ! (
649
+ error4. to_string( ) ,
650
+ "uplink error, the request will not be retried: code=UNKNOWN_REF message=Graph not found"
651
+ ) ;
652
+ }
653
+
654
+ #[ test]
655
+ fn test_uplink_request_debug ( ) {
656
+ let request = UplinkRequest {
657
+ api_key : "test_api_key" . to_string ( ) ,
658
+ graph_ref : "test@main" . to_string ( ) ,
659
+ id : Some ( "test_id" . to_string ( ) ) ,
660
+ } ;
661
+
662
+ let debug_output = format ! ( "{:?}" , request) ;
663
+ assert ! ( debug_output. contains( "test_api_key" ) ) ;
664
+ assert ! ( debug_output. contains( "test@main" ) ) ;
665
+ assert ! ( debug_output. contains( "test_id" ) ) ;
666
+ }
667
+
668
+ #[ test]
669
+ fn test_uplink_response_debug ( ) {
670
+ let response_new = UplinkResponse :: New {
671
+ response : "test_response" . to_string ( ) ,
672
+ id : "test_id" . to_string ( ) ,
673
+ delay : 30 ,
674
+ } ;
675
+ let debug_new = format ! ( "{:?}" , response_new) ;
676
+ assert ! ( debug_new. contains( "New" ) ) ;
677
+ assert ! ( debug_new. contains( "test_response" ) ) ;
678
+
679
+ let response_unchanged = UplinkResponse :: < String > :: Unchanged {
680
+ id : Some ( "test_id" . to_string ( ) ) ,
681
+ delay : Some ( 30 ) ,
682
+ } ;
683
+ let debug_unchanged = format ! ( "{:?}" , response_unchanged) ;
684
+ assert ! ( debug_unchanged. contains( "Unchanged" ) ) ;
685
+
686
+ let response_error = UplinkResponse :: < String > :: Error {
687
+ retry_later : true ,
688
+ code : "RETRY_LATER" . to_string ( ) ,
689
+ message : "Try again" . to_string ( ) ,
690
+ } ;
691
+ let debug_error = format ! ( "{:?}" , response_error) ;
692
+ assert ! ( debug_error. contains( "Error" ) ) ;
693
+ assert ! ( debug_error. contains( "retry_later: true" ) ) ;
694
+ }
456
695
}
0 commit comments