2222import static org .junit .Assert .assertSame ;
2323import static org .junit .Assert .assertTrue ;
2424import static org .mockito .ArgumentMatchers .isA ;
25+ import static org .mockito .ArgumentMatchers .isNull ;
2526import static org .mockito .Mockito .any ;
27+ import static org .mockito .Mockito .doNothing ;
2628import static org .mockito .Mockito .doReturn ;
2729import static org .mockito .Mockito .doThrow ;
2830import static org .mockito .Mockito .mock ;
2931import static org .mockito .Mockito .eq ;
32+ import static org .mockito .Mockito .never ;
3033import static org .mockito .Mockito .times ;
3134import static org .mockito .Mockito .verify ;
3235import static org .mockito .Mockito .when ;
3336
3437import java .io .IOException ;
3538import java .lang .reflect .Field ;
3639import java .net .InetSocketAddress ;
40+ import java .util .concurrent .ExecutorService ;
3741
3842import javax .naming .ConfigurationException ;
3943
44+ import com .cloud .agent .api .StartupAnswer ;
45+ import com .cloud .agent .api .StartupRoutingCommand ;
46+ import com .cloud .utils .backoff .BackoffAlgorithm ;
47+ import com .cloud .utils .nio .NioClient ;
4048import org .apache .logging .log4j .Logger ;
4149import org .junit .Before ;
4250import org .junit .Test ;
4351import org .junit .runner .RunWith ;
52+ import org .mockito .ArgumentCaptor ;
53+ import org .mockito .Mockito ;
4454import org .mockito .junit .MockitoJUnitRunner ;
4555import org .springframework .test .util .ReflectionTestUtils ;
4656
4757import com .cloud .resource .ServerResource ;
4858import com .cloud .utils .backoff .impl .ConstantTimeBackoff ;
4959import com .cloud .utils .nio .Link ;
50- import com .cloud .utils .nio .NioConnection ;
5160
5261@ RunWith (MockitoJUnitRunner .class )
5362public class AgentTest {
@@ -222,7 +231,7 @@ public void testStopAndCleanupConnectionConnectionIsNullDoesNothing() {
222231
223232 @ Test
224233 public void testStopAndCleanupConnectionValidConnectionNoWaitStopsAndCleansUp () throws IOException {
225- NioConnection mockConnection = mock (NioConnection .class );
234+ NioClient mockConnection = mock (NioClient .class );
226235 agent .connection = mockConnection ;
227236 agent .stopAndCleanupConnection ();
228237 verify (mockConnection ).stop ();
@@ -231,7 +240,7 @@ public void testStopAndCleanupConnectionValidConnectionNoWaitStopsAndCleansUp()
231240
232241 @ Test
233242 public void testStopAndCleanupConnectionCleanupThrowsIOExceptionLogsWarning () throws IOException {
234- NioConnection mockConnection = mock (NioConnection .class );
243+ NioClient mockConnection = mock (NioClient .class );
235244 agent .connection = mockConnection ;
236245 doThrow (new IOException ("Cleanup failed" )).when (mockConnection ).cleanUp ();
237246 agent .stopAndCleanupConnection ();
@@ -241,7 +250,7 @@ public void testStopAndCleanupConnectionCleanupThrowsIOExceptionLogsWarning() th
241250
242251 @ Test
243252 public void testStopAndCleanupConnectionValidConnectionWaitForStopWaitsForStartupToStop () throws IOException {
244- NioConnection mockConnection = mock (NioConnection .class );
253+ NioClient mockConnection = mock (NioClient .class );
245254 ConstantTimeBackoff mockBackoff = mock (ConstantTimeBackoff .class );
246255 mockBackoff .setTimeToWait (0 );
247256 agent .connection = mockConnection ;
@@ -290,4 +299,77 @@ public void testSelectReconnectionHostWithNullSocketAddressUsesShellNextHost() {
290299 assertEquals ("fallback.host.com" , result );
291300 verify (shell , times (1 )).getNextHost ();
292301 }
302+
303+ private Agent setupAgentSpyForStartupAnswerTests (String currentHost ) throws ConfigurationException {
304+ Agent spyAgent = Mockito .spy (agent );
305+ doNothing ().when (spyAgent ).reconnect (any (), any (), eq (false ));
306+
307+ NioClient mockConnection = mock (NioClient .class );
308+ when (mockConnection .getHost ()).thenReturn (currentHost );
309+ spyAgent .connection = mockConnection ;
310+
311+ spyAgent .requestHandler = mock (ExecutorService .class );
312+ when (serverResource .isExitOnFailures ()).thenReturn (false );
313+ when (shell .getBackoffAlgorithm ()).thenReturn (mock (BackoffAlgorithm .class ));
314+ return spyAgent ;
315+ }
316+
317+ @ Test
318+ public void testProcessStartupAnswerRetryCurrentMsReconnectsToCurrentHost () throws Exception {
319+ String currentHost = "ms-host-0.example.com" ;
320+ Agent spyAgent = setupAgentSpyForStartupAnswerTests (currentHost );
321+ Link link = mock (Link .class );
322+
323+ StartupAnswer answer = new StartupAnswer (new StartupRoutingCommand (), "duplicate" );
324+ answer .setRetryCurrentMs (true );
325+
326+ spyAgent .processStartupAnswer (answer , null , link );
327+
328+ ArgumentCaptor <Runnable > captor = ArgumentCaptor .forClass (Runnable .class );
329+ verify (spyAgent .requestHandler ).submit (captor .capture ());
330+ captor .getValue ().run ();
331+
332+ verify (spyAgent ).reconnect (eq (link ), eq (currentHost ), eq (false ));
333+ }
334+
335+ @ Test
336+ public void testProcessStartupAnswerRetryCurrentMsAppliesBackoffBeforeReconnect () throws Exception {
337+ Agent spyAgent = setupAgentSpyForStartupAnswerTests ("ms-host-0.example.com" );
338+ Link link = mock (Link .class );
339+
340+ BackoffAlgorithm mockBackoff = mock (BackoffAlgorithm .class );
341+ when (shell .getBackoffAlgorithm ()).thenReturn (mockBackoff );
342+
343+ StartupAnswer answer = new StartupAnswer (new StartupRoutingCommand (), "duplicate" );
344+ answer .setRetryCurrentMs (true );
345+
346+ spyAgent .processStartupAnswer (answer , null , link );
347+
348+ ArgumentCaptor <Runnable > captor = ArgumentCaptor .forClass (Runnable .class );
349+ verify (spyAgent .requestHandler ).submit (captor .capture ());
350+ captor .getValue ().run ();
351+
352+ verify (mockBackoff ).waitBeforeRetry ();
353+ }
354+
355+ @ Test
356+ public void testProcessStartupAnswerNoRetryCurrentMsReconnectsWithNullAndNoBackoff () throws Exception {
357+ Agent spyAgent = setupAgentSpyForStartupAnswerTests ("ms-host-0.example.com" );
358+ Link link = mock (Link .class );
359+
360+ BackoffAlgorithm mockBackoff = mock (BackoffAlgorithm .class );
361+ when (shell .getBackoffAlgorithm ()).thenReturn (mockBackoff );
362+
363+ StartupAnswer answer = new StartupAnswer (new StartupRoutingCommand (), "not a duplicate" );
364+ // retryCurrentMs defaults to false
365+
366+ spyAgent .processStartupAnswer (answer , null , link );
367+
368+ ArgumentCaptor <Runnable > captor = ArgumentCaptor .forClass (Runnable .class );
369+ verify (spyAgent .requestHandler ).submit (captor .capture ());
370+ captor .getValue ().run ();
371+
372+ verify (spyAgent ).reconnect (eq (link ), isNull (), eq (false ));
373+ verify (mockBackoff , never ()).waitBeforeRetry ();
374+ }
293375}
0 commit comments