3232import java .io .IOException ;
3333import java .nio .ByteBuffer ;
3434import java .nio .ByteOrder ;
35- import java .util .concurrent .TimeUnit ;
3635import java .util .concurrent .atomic .AtomicLong ;
3736
3837/**
@@ -45,22 +44,30 @@ public class InitialConnectRetryTest {
4544 private static final int TEST_PORT = 19_700 + (int ) (System .nanoTime () % 100 );
4645
4746 @ Test
48- public void testWithoutRetryFailsImmediately () {
49- // No server on this port. With initial_connect_retry off (default),
50- // fromConfig must throw without sitting around for the cap.
51- int port = TEST_PORT + 1 ;
52- long t0 = System .nanoTime ();
53- try (Sender ignored = Sender .fromConfig ("ws::addr=localhost:" + port + ";" )) {
54- Assert .fail ("expected immediate connect failure" );
47+ public void testWithRetryGivesUpAfterCap () {
48+ // No server. With retry on, fromConfig must run the retry loop and
49+ // ultimately throw with the connectWithRetry-shaped message that
50+ // names the elapsed budget and attempt count. The actual budget
51+ // honoring is observable through that message — we don't need a
52+ // wall-clock check.
53+ int port = TEST_PORT + 3 ;
54+ String cfg = "ws::addr=127.0.0.1:" + port
55+ + ";initial_connect_retry=true"
56+ + ";reconnect_max_duration_millis=400"
57+ + ";reconnect_initial_backoff_millis=10"
58+ + ";reconnect_max_backoff_millis=50;" ;
59+ try (Sender ignored = Sender .fromConfig (cfg )) {
60+ Assert .fail ("expected give-up after cap" );
5561 } catch (Exception expected ) {
56- long elapsedMs = (System .nanoTime () - t0 ) / 1_000_000L ;
57- Assert .assertTrue ("must fail fast (took " + elapsedMs + " ms)" ,
58- elapsedMs < 2_000L );
62+ String msg = expected .getMessage ();
63+ Assert .assertNotNull ("error must have a message" , msg );
64+ Assert .assertTrue ("error must come from the retry loop: " + msg ,
65+ msg .contains ("initial connect" ) && msg .contains ("attempts" ));
5966 }
6067 }
6168
6269 @ Test
63- public void testWithRetrySucceedsWhenServerComesUpInTime () throws Exception {
70+ public void testWithRetrySucceedsWhenServerComesUpInTime () {
6471 // initial_connect_retry=true; we open the sender BEFORE starting
6572 // the server, then start the server in a background thread after
6673 // a short delay. The retry loop should see the server come up and
@@ -79,7 +86,7 @@ public void testWithRetrySucceedsWhenServerComesUpInTime() throws Exception {
7986 starter .setDaemon (true );
8087 starter .start ();
8188 try {
82- String cfg = "ws::addr=localhost :" + port
89+ String cfg = "ws::addr=127.0.0.1 :" + port
8390 + ";initial_connect_retry=true"
8491 + ";reconnect_max_duration_millis=5000"
8592 + ";reconnect_initial_backoff_millis=50"
@@ -99,30 +106,30 @@ public void testWithRetrySucceedsWhenServerComesUpInTime() throws Exception {
99106 }
100107
101108 @ Test
102- public void testWithRetryGivesUpAfterCap () {
103- // No server. With retry on but a tight cap, fromConfig should
104- // throw within the cap window (with some slack).
105- int port = TEST_PORT + 3 ;
106- long t0 = System .nanoTime ();
107- String cfg = "ws::addr=localhost:" + port
108- + ";initial_connect_retry=true"
109- + ";reconnect_max_duration_millis=400"
110- + ";reconnect_initial_backoff_millis=10"
111- + ";reconnect_max_backoff_millis=50;" ;
112- try (Sender ignored = Sender .fromConfig (cfg )) {
113- Assert .fail ("expected give-up after cap" );
109+ public void testWithoutRetryFailsImmediately () {
110+ // No server on this port. With initial_connect_retry off (default),
111+ // fromConfig must throw on the first connect failure rather than enter
112+ // the retry loop. We assert the structural shape of the error: the
113+ // raw "Failed to connect" message from buildAndConnect, NOT the
114+ // "initial connect ... attempts" message connectWithRetry produces.
115+ int port = TEST_PORT + 1 ;
116+ // Use the IPv4 literal so the test doesn't pay first-call
117+ // getaddrinfo("localhost") cost on Windows (1-2 s cold lookup).
118+ try (Sender ignored = Sender .fromConfig ("ws::addr=127.0.0.1:" + port + ";" )) {
119+ Assert .fail ("expected immediate connect failure" );
114120 } catch (Exception expected ) {
115- long elapsedMs = (System .nanoTime () - t0 ) / 1_000_000L ;
116- Assert .assertTrue ("must give up around the cap (took " + elapsedMs + " ms)" ,
117- elapsedMs >= 300L && elapsedMs < 3_000L );
118121 String msg = expected .getMessage ();
119- Assert .assertTrue ("error must mention startup retry: " + msg ,
120- msg != null && (msg .contains ("initial connect" )
121- || msg .contains ("Failed to connect" )));
122+ Assert .assertNotNull ("error must have a message" , msg );
123+ Assert .assertTrue ("error must be the raw connect-refused: " + msg ,
124+ msg .contains ("Failed to connect" ));
125+ Assert .assertFalse ("error must NOT mention the retry loop: " + msg ,
126+ msg .contains ("attempts" ));
122127 }
123128 }
124129
125- /** Acks every binary frame so the sender's flush completes. */
130+ /**
131+ * Acks every binary frame so the sender's flush completes.
132+ */
126133 private static class AckHandler implements TestWebSocketServer .WebSocketServerHandler {
127134 private final AtomicLong nextSeq = new AtomicLong (0 );
128135
0 commit comments