@@ -777,6 +777,140 @@ void shouldReduceRedundantSlotChecks() throws URISyntaxException {
777
777
}
778
778
}
779
779
780
+ @ Test
781
+ void shouldNotRejectRequestsWhenNodesHaveCapabilityButNoFreeSlots () throws URISyntaxException {
782
+ // Create a distributor with rejectUnsupportedCaps enabled
783
+ NewSessionQueue queue =
784
+ new LocalNewSessionQueue (
785
+ tracer ,
786
+ new DefaultSlotMatcher (),
787
+ Duration .ofSeconds (2 ),
788
+ Duration .ofSeconds (2 ),
789
+ Duration .ofSeconds (1 ),
790
+ registrationSecret ,
791
+ 5 );
792
+ LocalDistributor distributor =
793
+ new LocalDistributor (
794
+ tracer ,
795
+ bus ,
796
+ new PassthroughHttpClient .Factory (localNode ),
797
+ new LocalSessionMap (tracer , bus ),
798
+ queue ,
799
+ new DefaultSlotSelector (),
800
+ registrationSecret ,
801
+ Duration .ofMinutes (5 ),
802
+ true , // Enable rejectUnsupportedCaps
803
+ Duration .ofSeconds (5 ),
804
+ newSessionThreadPoolSize ,
805
+ new DefaultSlotMatcher (),
806
+ Duration .ofSeconds (30 ));
807
+
808
+ // Create a node that supports Chrome with single slot
809
+ URI nodeUri = new URI ("http://example:1234" );
810
+ Node node =
811
+ LocalNode .builder (tracer , bus , nodeUri , nodeUri , registrationSecret )
812
+ .add (
813
+ new ImmutableCapabilities ("browserName" , "chrome" ),
814
+ new TestSessionFactory (
815
+ (id , c ) ->
816
+ new Session (id , nodeUri , new ImmutableCapabilities (), c , Instant .now ())))
817
+ .maximumConcurrentSessions (1 )
818
+ .build ();
819
+ distributor .add (node );
820
+
821
+ // Occupy the node's only slot
822
+ SessionRequest sessionRequest =
823
+ new SessionRequest (
824
+ new RequestId (UUID .randomUUID ()),
825
+ Instant .now (),
826
+ Set .of (W3C ),
827
+ Set .of (new ImmutableCapabilities ("browserName" , "chrome" )),
828
+ Map .of (),
829
+ Map .of ());
830
+ Either <SessionNotCreatedException , CreateSessionResponse > result =
831
+ distributor .newSession (sessionRequest );
832
+ assertThat (result .isRight ()).isTrue (); // Session created successfully
833
+
834
+ // Verify node has no available capacity but still supports Chrome
835
+ assertThat (distributor .getAvailableNodes ()).isEmpty (); // No available nodes
836
+
837
+ // Test that the distributor status shows the node is still UP and supports Chrome
838
+ // even though it has no available capacity
839
+ DistributorStatus status = distributor .getStatus ();
840
+ Set <NodeStatus > allNodes = status .getNodes ();
841
+ assertThat (allNodes ).hasSize (1 );
842
+
843
+ NodeStatus nodeStatus = allNodes .iterator ().next ();
844
+ assertThat (nodeStatus .getAvailability ()).isEqualTo (UP );
845
+
846
+ // Verify the node still supports Chrome capability even with no free slots
847
+ boolean supportsChrome =
848
+ nodeStatus .hasCapability (
849
+ new ImmutableCapabilities ("browserName" , "chrome" ), new DefaultSlotMatcher ());
850
+ assertThat (supportsChrome ).isTrue ();
851
+
852
+ // Verify the node has no capacity (all slots occupied)
853
+ assertThat (nodeStatus .hasCapacity ()).isFalse ();
854
+ }
855
+
856
+ @ Test
857
+ void shouldRejectRequestsWhenNoNodesHaveCapability () throws URISyntaxException {
858
+ // Create a distributor with rejectUnsupportedCaps enabled
859
+ NewSessionQueue queue =
860
+ new LocalNewSessionQueue (
861
+ tracer ,
862
+ new DefaultSlotMatcher (),
863
+ Duration .ofSeconds (2 ),
864
+ Duration .ofSeconds (2 ),
865
+ Duration .ofSeconds (1 ),
866
+ registrationSecret ,
867
+ 5 );
868
+ LocalDistributor distributor =
869
+ new LocalDistributor (
870
+ tracer ,
871
+ bus ,
872
+ new PassthroughHttpClient .Factory (localNode ),
873
+ new LocalSessionMap (tracer , bus ),
874
+ queue ,
875
+ new DefaultSlotSelector (),
876
+ registrationSecret ,
877
+ Duration .ofMinutes (5 ),
878
+ true , // Enable rejectUnsupportedCaps
879
+ Duration .ofSeconds (5 ),
880
+ newSessionThreadPoolSize ,
881
+ new DefaultSlotMatcher (),
882
+ Duration .ofSeconds (30 ));
883
+
884
+ // Create a node that only supports Chrome
885
+ URI nodeUri = new URI ("http://example:1234" );
886
+ Node node =
887
+ LocalNode .builder (tracer , bus , nodeUri , nodeUri , registrationSecret )
888
+ .add (
889
+ new ImmutableCapabilities ("browserName" , "chrome" ),
890
+ new TestSessionFactory (
891
+ (id , c ) ->
892
+ new Session (id , nodeUri , new ImmutableCapabilities (), c , Instant .now ())))
893
+ .build ();
894
+ distributor .add (node );
895
+
896
+ // Add a Firefox request to the queue (unsupported capability)
897
+ SessionRequest unsupportedRequest =
898
+ new SessionRequest (
899
+ new RequestId (UUID .randomUUID ()),
900
+ Instant .now (),
901
+ Set .of (W3C ),
902
+ Set .of (new ImmutableCapabilities ("browserName" , "firefox" )),
903
+ Map .of (),
904
+ Map .of ());
905
+ queue .addToQueue (unsupportedRequest );
906
+
907
+ // Wait for checkMatchingSlot to run and reject the request
908
+ wait .until (obj -> queue .getQueueContents ().isEmpty ());
909
+
910
+ // The request should be rejected and removed from queue
911
+ assertThat (queue .getQueueContents ()).isEmpty ();
912
+ }
913
+
780
914
@ Test
781
915
void shouldHandleAllNodesFullyOccupied () throws URISyntaxException {
782
916
// Create a distributor
0 commit comments