@@ -111,8 +111,12 @@ impl Intercept for MockResponseInterceptor {
111
111
while i < rules. len ( ) && matching_response. is_none ( ) {
112
112
let rule = & rules[ i] ;
113
113
114
- // Check if the rule is already exhausted
115
- if rule. is_exhausted ( ) {
114
+ // Check if the rule is already exhausted or if it's a simple rule used once
115
+ //
116
+ // In `aws-smithy-mocks-experimental` all rules were infinite sequences
117
+ // but were only usable once in sequential mode. We retain that here for
118
+ // backwards compatibility.
119
+ if rule. is_exhausted ( ) || ( rule. is_simple ( ) && rule. num_calls ( ) > 0 ) {
116
120
// Rule is exhausted, remove it and try the next one
117
121
rules. remove ( i) ;
118
122
continue ; // Don't increment i since we removed an element
@@ -444,7 +448,7 @@ mod tests {
444
448
expected = "must_match was enabled but no rules matched or all rules were exhausted for"
445
449
) ]
446
450
#[ tokio:: test]
447
- async fn test_exhausted_rules ( ) {
451
+ async fn test_exhausted_rules_sequential ( ) {
448
452
// Create a rule with a single response
449
453
let rule = create_rule_builder ( ) . then_output ( || TestOutput :: new ( "only response" ) ) ;
450
454
@@ -503,6 +507,14 @@ mod tests {
503
507
// Verify the rules were used the expected number of times
504
508
assert_eq ! ( rule1. num_calls( ) , 1 ) ;
505
509
assert_eq ! ( rule2. num_calls( ) , 1 ) ;
510
+
511
+ // Calling with bucket1 again should match rule1 a second time
512
+ let result1 = operation
513
+ . invoke ( TestInput :: new ( "bucket1" , "test-key" ) )
514
+ . await ;
515
+ assert ! ( result1. is_ok( ) ) ;
516
+ assert_eq ! ( result1. unwrap( ) , TestOutput :: new( "response1" ) ) ;
517
+ assert_eq ! ( rule1. num_calls( ) , 2 ) ;
506
518
}
507
519
508
520
#[ tokio:: test]
@@ -553,9 +565,63 @@ mod tests {
553
565
// Verify the rule was used the expected number of times
554
566
assert_eq ! ( rule. num_calls( ) , 3 ) ;
555
567
}
568
+ #[ tokio:: test]
569
+ async fn test_exhausted_sequence_match_any ( ) {
570
+ // Create a rule with a sequence that will be exhausted
571
+ let rule = create_rule_builder ( )
572
+ . match_requests ( |input| input. bucket == "bucket-1" )
573
+ . sequence ( )
574
+ . output ( || TestOutput :: new ( "response 1" ) )
575
+ . output ( || TestOutput :: new ( "response 2" ) )
576
+ . build ( ) ;
577
+
578
+ // Create another rule to use after the first one is exhausted
579
+ let fallback_rule =
580
+ create_rule_builder ( ) . then_output ( || TestOutput :: new ( "fallback response" ) ) ;
581
+
582
+ // Create an interceptor with both rules
583
+ let interceptor = MockResponseInterceptor :: new ( )
584
+ . rule_mode ( RuleMode :: MatchAny )
585
+ . with_rule ( & rule)
586
+ . with_rule ( & fallback_rule) ;
587
+
588
+ let operation = create_test_operation ( interceptor, false ) ;
589
+
590
+ // First two calls should use the first rule
591
+ let result1 = operation
592
+ . invoke ( TestInput :: new ( "bucket-1" , "test-key" ) )
593
+ . await ;
594
+ assert ! ( result1. is_ok( ) ) ;
595
+ assert_eq ! ( result1. unwrap( ) , TestOutput :: new( "response 1" ) ) ;
596
+
597
+ // second should use our fallback rule
598
+ let result2 = operation
599
+ . invoke ( TestInput :: new ( "other-bucket" , "test-key" ) )
600
+ . await ;
601
+ assert ! ( result2. is_ok( ) ) ;
602
+ assert_eq ! ( result2. unwrap( ) , TestOutput :: new( "fallback response" ) ) ;
603
+
604
+ // Third call should use the first rule again and exhaust it
605
+ let result3 = operation
606
+ . invoke ( TestInput :: new ( "bucket-1" , "test-key" ) )
607
+ . await ;
608
+ assert ! ( result3. is_ok( ) ) ;
609
+ assert_eq ! ( result3. unwrap( ) , TestOutput :: new( "response 2" ) ) ;
610
+
611
+ // first rule is exhausted so the matcher shouldn't matter and we should hit our fallback rule
612
+ let result4 = operation
613
+ . invoke ( TestInput :: new ( "bucket-1" , "test-key" ) )
614
+ . await ;
615
+ assert ! ( result4. is_ok( ) ) ;
616
+ assert_eq ! ( result4. unwrap( ) , TestOutput :: new( "fallback response" ) ) ;
617
+
618
+ // Verify the rules were used the expected number of times
619
+ assert_eq ! ( rule. num_calls( ) , 2 ) ;
620
+ assert_eq ! ( fallback_rule. num_calls( ) , 2 ) ;
621
+ }
556
622
557
623
#[ tokio:: test]
558
- async fn test_exhausted_sequence ( ) {
624
+ async fn test_exhausted_sequence_sequential ( ) {
559
625
// Create a rule with a sequence that will be exhausted
560
626
let rule = create_rule_builder ( )
561
627
. sequence ( )
@@ -695,4 +761,118 @@ mod tests {
695
761
assert_eq ! ( result2. unwrap( ) , TestOutput :: new( "success" ) ) ;
696
762
assert_eq ! ( rule2. num_calls( ) , 1 ) ;
697
763
}
764
+
765
+ #[ tokio:: test]
766
+ async fn test_simple_rule_in_match_any_mode ( ) {
767
+ let rule = create_rule_builder ( ) . then_output ( || TestOutput :: new ( "simple response" ) ) ;
768
+
769
+ let interceptor = MockResponseInterceptor :: new ( )
770
+ . rule_mode ( RuleMode :: MatchAny )
771
+ . with_rule ( & rule) ;
772
+
773
+ let operation = create_test_operation ( interceptor, false ) ;
774
+
775
+ for i in 0 ..5 {
776
+ let result = operation
777
+ . invoke ( TestInput :: new ( "test-bucket" , "test-key" ) )
778
+ . await ;
779
+ assert ! ( result. is_ok( ) , "Call {} should succeed" , i) ;
780
+ assert_eq ! ( result. unwrap( ) , TestOutput :: new( "simple response" ) ) ;
781
+ }
782
+ assert_eq ! ( rule. num_calls( ) , 5 ) ;
783
+ assert ! ( !rule. is_exhausted( ) ) ;
784
+ }
785
+
786
+ #[ tokio:: test]
787
+ async fn test_simple_rule_in_sequential_mode ( ) {
788
+ let rule1 = create_rule_builder ( ) . then_output ( || TestOutput :: new ( "first response" ) ) ;
789
+ let rule2 = create_rule_builder ( ) . then_output ( || TestOutput :: new ( "second response" ) ) ;
790
+
791
+ let interceptor = MockResponseInterceptor :: new ( )
792
+ . rule_mode ( RuleMode :: Sequential )
793
+ . with_rule ( & rule1)
794
+ . with_rule ( & rule2) ;
795
+
796
+ let operation = create_test_operation ( interceptor, false ) ;
797
+
798
+ let result1 = operation
799
+ . invoke ( TestInput :: new ( "test-bucket" , "test-key" ) )
800
+ . await ;
801
+ assert ! ( result1. is_ok( ) ) ;
802
+ assert_eq ! ( result1. unwrap( ) , TestOutput :: new( "first response" ) ) ;
803
+
804
+ // Second call should use rule2 (rule1 should be removed after one use in Sequential mode)
805
+ let result2 = operation
806
+ . invoke ( TestInput :: new ( "test-bucket" , "test-key" ) )
807
+ . await ;
808
+ assert ! ( result2. is_ok( ) ) ;
809
+ assert_eq ! ( result2. unwrap( ) , TestOutput :: new( "second response" ) ) ;
810
+
811
+ assert_eq ! ( rule1. num_calls( ) , 1 ) ;
812
+ assert_eq ! ( rule2. num_calls( ) , 1 ) ;
813
+ }
814
+
815
+ #[ tokio:: test]
816
+ async fn test_repeatedly_method ( ) {
817
+ let rule = create_rule_builder ( )
818
+ . sequence ( )
819
+ . output ( || TestOutput :: new ( "first response" ) )
820
+ . output ( || TestOutput :: new ( "repeated response" ) )
821
+ . repeatedly ( )
822
+ . build ( ) ;
823
+
824
+ let interceptor = MockResponseInterceptor :: new ( )
825
+ . rule_mode ( RuleMode :: Sequential )
826
+ . with_rule ( & rule) ;
827
+
828
+ let operation = create_test_operation ( interceptor, false ) ;
829
+
830
+ let result1 = operation
831
+ . invoke ( TestInput :: new ( "test-bucket" , "test-key" ) )
832
+ . await ;
833
+ assert ! ( result1. is_ok( ) ) ;
834
+ assert_eq ! ( result1. unwrap( ) , TestOutput :: new( "first response" ) ) ;
835
+
836
+ // all subsequent calls should return "repeated response"
837
+ for i in 0 ..10 {
838
+ let result = operation
839
+ . invoke ( TestInput :: new ( "test-bucket" , "test-key" ) )
840
+ . await ;
841
+ assert ! ( result. is_ok( ) , "Call {} should succeed" , i) ;
842
+ assert_eq ! ( result. unwrap( ) , TestOutput :: new( "repeated response" ) ) ;
843
+ }
844
+ assert_eq ! ( rule. num_calls( ) , 11 ) ;
845
+ assert ! ( !rule. is_exhausted( ) ) ;
846
+ }
847
+
848
+ #[ should_panic( expected = "times(n) called before adding a response to the sequence" ) ]
849
+ #[ test]
850
+ fn test_times_validation ( ) {
851
+ // This should panic because times() is called before adding any responses
852
+ let _rule = create_rule_builder ( )
853
+ . sequence ( )
854
+ . times ( 3 )
855
+ . output ( || TestOutput :: new ( "response" ) )
856
+ . build ( ) ;
857
+ }
858
+
859
+ #[ should_panic( expected = "repeatedly() called before adding a response to the sequence" ) ]
860
+ #[ test]
861
+ fn test_repeatedly_validation ( ) {
862
+ // This should panic because repeatedly() is called before adding any responses
863
+ let _rule = create_rule_builder ( ) . sequence ( ) . repeatedly ( ) . build ( ) ;
864
+ }
865
+
866
+ #[ test]
867
+ fn test_total_responses_overflow ( ) {
868
+ // Create a rule with a large number of repetitions to test overflow handling
869
+ let rule = create_rule_builder ( )
870
+ . sequence ( )
871
+ . output ( || TestOutput :: new ( "response" ) )
872
+ . times ( usize:: MAX / 2 )
873
+ . output ( || TestOutput :: new ( "another response" ) )
874
+ . repeatedly ( )
875
+ . build ( ) ;
876
+ assert_eq ! ( rule. max_responses, usize :: MAX ) ;
877
+ }
698
878
}
0 commit comments