diff --git a/prober/src/main/java/google/registry/monitoring/blackbox/ProbingAction.java b/prober/src/main/java/google/registry/monitoring/blackbox/ProbingAction.java
index 8173ba6ba..bfda98232 100644
--- a/prober/src/main/java/google/registry/monitoring/blackbox/ProbingAction.java
+++ b/prober/src/main/java/google/registry/monitoring/blackbox/ProbingAction.java
@@ -135,12 +135,12 @@ public abstract class ProbingAction implements Callable
Once the connection is successful, we establish which of the handlers in the pipeline is - * the {@link ActionHandler}.From that, we can obtain a future that is marked as a success when - * we receive an expected response from the server.
+ * the {@link ActionHandler}.From that, we can obtain a future that is marked as a success when we + * receive an expected response from the server. * *Next, we set a timer set to a specified delay. After the delay has passed, we send the - * {@code outboundMessage} down the channel pipeline, and when we observe a success or failure, - * we inform the {@link ProbingStep} of this.
+ * {@code outboundMessage} down the channel pipeline, and when we observe a success or failure, we + * inform the {@link ProbingStep} of this. * * @return {@link ChannelFuture} that denotes when the action has been successfully performed. */ diff --git a/prober/src/main/java/google/registry/monitoring/blackbox/ProbingSequence.java b/prober/src/main/java/google/registry/monitoring/blackbox/ProbingSequence.java index 7f97f8bd5..c9757c006 100644 --- a/prober/src/main/java/google/registry/monitoring/blackbox/ProbingSequence.java +++ b/prober/src/main/java/google/registry/monitoring/blackbox/ProbingSequence.java @@ -14,14 +14,25 @@ package google.registry.monitoring.blackbox; +import com.google.common.flogger.FluentLogger; +import google.registry.monitoring.blackbox.exceptions.UnrecoverableStateException; import google.registry.monitoring.blackbox.tokens.Token; +import google.registry.util.CircularList; import io.netty.bootstrap.Bootstrap; import io.netty.channel.AbstractChannel; +import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; /** * Represents Sequence of {@link ProbingStep}s that the Prober performs in order. * + *Inherits from {@link CircularList}, with element type of + * {@link ProbingStep} as the manner in which the sequence is carried out is similar to the {@link + * CircularList}. However, the {@link Builder} of {@link ProbingSequence} override + * {@link CircularList.AbstractBuilder} allowing for more customized flows, that are looped, but + * not necessarily entirely circular. + * Example: first -> second -> third -> fourth -> second -> third -> fourth -> second -> ... + *
* *Created with {@link Builder} where we specify {@link EventLoopGroup}, {@link AbstractChannel} * class type, then sequentially add in the {@link ProbingStep.Builder}s in order and mark which one @@ -31,78 +42,202 @@ import io.netty.channel.EventLoopGroup; * the first one is activated with the requisite {@link Token}, the {@link ProbingStep}s do the rest * of the work.
*/ -public class ProbingSequence { +public class ProbingSequence extends CircularListCalls {@code runNextStep} to have next {@link ProbingSequence} call {@code runStep} + * with next token depending on if the current step is the last one in the sequence. + * + *
If unable to generate the action, or the calling the action results in an immediate error, + * we note an error. Otherwise, if the future marked as finished when the action is completed is + * marked as a success, we note a success. Otherwise, if the cause of failure will either be a + * failure or error.
+ */ + private void runStep(Token token) { + ProbingAction currentAction; + ChannelFuture future; + + try { + // Attempt to generate new action. On error, move on to next step. + currentAction = get().generateAction(token); + + // Call the generated action. + future = currentAction.call(); + + } catch (UnrecoverableStateException e) { + // On an UnrecoverableStateException, terminate the sequence. + logger.atSevere().withCause(e).log( + "Unrecoverable error in generating or calling action."); + return; + + } catch (Exception e) { + // On any other type of error, restart the sequence at the very first step. + logger.atWarning().withCause(e).log("Error in generating or calling action."); + + // Restart the sequence at the very first step. + restartSequence(); + return; + } + + future.addListener(f -> { + if (f.isSuccess()) { + // On a successful result, we log as a successful step, and note a success. + logger.atInfo().log(String.format("Successfully completed Probing Step: %s", this)); + + } else { + // On a failed result, we log the failure and note either a failure or error. + logger.atSevere().withCause(f.cause()).log("Did not result in future success"); + + // If not unrecoverable, we restart the sequence. + if (!(f.cause() instanceof UnrecoverableStateException)) { + restartSequence(); + } + // Otherwise, we just terminate the full sequence. + return; + } + + if (get().protocol().persistentConnection()) { + // If the connection is persistent, we store the channel in the token. + token.setChannel(currentAction.channel()); + } + + //Calls next runStep + runNextStep(token); + + }); + } + + /** + * Helper method to first generate the next token, then call runStep on the next {@link + * ProbingSequence}. + */ + private void runNextStep(Token token) { + token = lastStep ? token.next() : token; + next().runStep(token); + } + + /** + * Helper method to restart the sequence at the very first step, with a channel-less {@link + * Token}. + */ + private void restartSequence() { + // Gets next possible token to insure no replicated domains used. + Token restartToken = startToken.next(); + + // Makes sure channel from original token isn't passed down. + restartToken.setChannel(null); + + // Runs the very first step with starting token. + first.runStep(restartToken); } /** * Turns {@link ProbingStep.Builder}s into fully self-dependent sequence with supplied {@link * Bootstrap}. */ - public static class Builder { + public static class Builder extends CircularList.AbstractBuilderIf unable to generate the action, or the calling the action results in an immediate error, - * we note an error. Otherwise, if the future marked as finished when the action is completed is - * marked as a success, we note a success. Otherwise, if the cause of failure will either be a - * failure or error.
- */ - @Override - public void accept(Token token) { - ProbingAction currentAction; - //attempt to generate new action. On error, move on to next step - try { - currentAction = generateAction(token); - } catch (UndeterminedStateException e) { - logger.atWarning().withCause(e).log("Error in Action Generation"); - nextStep.accept(generateNextToken(token)); - return; - } - - ChannelFuture future; - try { - //call the generated action - future = currentAction.call(); - } catch (Exception e) { - //On error in calling action, log error and note an error - logger.atWarning().withCause(e).log("Error in Action Performed"); - - //Move on to next step in ProbingSequence - nextStep.accept(generateNextToken(token)); - return; - } - - future.addListener(f -> { - if (f.isSuccess()) { - //On a successful result, we log as a successful step, and not a success - logger.atInfo().log(String.format("Successfully completed Probing Step: %s", this)); - - } else { - //On a failed result, we log the failure and note either a failure or error - logger.atSevere().withCause(f.cause()).log("Did not result in future success"); - } - - if (protocol().persistentConnection()) { - //If the connection is persistent, we store the channel in the token - token.setChannel(currentAction.channel()); - } - - //Move on the the next step in the ProbingSequence - nextStep.accept(generateNextToken(token)); - - - }); - } - @Override public final String toString() { return String.format("ProbingStep with Protocol: %s\n" @@ -175,7 +87,7 @@ public abstract class ProbingStep implements ConsumerFirst tests the construction of sequences and ensures the ordering is exactly how + * we expect it to be.
+ * + *Then tests the execution of each step, by ensuring the methods treatment of any kind + * of response from the {@link ProbingStep}s or {@link ProbingAction}s is what is expected.
+ * + *On every test that runs the sequence, in order for the sequence to stop, we throw an + * {@link UnrecoverableStateException}, using mocks of the steps or actions, as the sequences + * are run using the main thread (with {@link EmbeddedChannel}).
+ */ @RunWith(JUnit4.class) public class ProbingSequenceTest { - private ProbingStep firstStep; - private ProbingStep secondStep; - private ProbingStep thirdStep; + /** + * Default mock {@link ProbingAction} returned when generating an action with a mockStep. + */ + private ProbingAction mockAction = Mockito.mock(ProbingAction.class); - private Token testToken; + /** + * Default mock {@link ProbingStep} that will usually return a {@code mockAction} on call to + * generate action. + */ + private ProbingStep mockStep = Mockito.mock(ProbingStep.class); - private ProbingStep setupMockStep() { - ProbingStep mock = Mockito.mock(ProbingStep.class); - doCallRealMethod().when(mock).nextStep(any(ProbingStep.class)); - doCallRealMethod().when(mock).nextStep(); - return mock; - } + + /** + * Default mock {@link Token} that is passed into each {@link ProbingSequence} tested. + */ + private Token mockToken = Mockito.mock(Token.class); + + /** + * Default mock {@link Protocol} returned {@code mockStep} and occasionally, other mock {@link + * ProbingStep}s. + */ + private Protocol mockProtocol = Mockito.mock(Protocol.class); + + /** + * {@link EmbeddedChannel} used to create new {@link ChannelPromise} objects returned by mock + * {@link ProbingAction}s on their {@code call} methods. + */ + private EmbeddedChannel channel = new EmbeddedChannel(); @Before public void setup() { - firstStep = setupMockStep(); - secondStep = setupMockStep(); - thirdStep = setupMockStep(); + // To avoid a NullPointerException, we must have a protocol return persistent connection as + // false. + doReturn(true).when(mockProtocol).persistentConnection(); - testToken = Mockito.mock(Token.class); + // In order to avoid a NullPointerException, we must have the protocol returned that stores + // persistent connection as false. + doReturn(mockProtocol).when(mockStep).protocol(); + + // Allows for test if channel is accurately set. + doCallRealMethod().when(mockToken).setChannel(any(Channel.class)); + doCallRealMethod().when(mockToken).channel(); + + // Allows call to mockAction to retrieve mocked channel. + doReturn(channel).when(mockAction).channel(); } @Test public void testSequenceBasicConstruction_Success() { + ProbingStep firstStep = Mockito.mock(ProbingStep.class); + ProbingStep secondStep = Mockito.mock(ProbingStep.class); + ProbingStep thirdStep = Mockito.mock(ProbingStep.class); - ProbingSequence sequence = new ProbingSequence.Builder(testToken) - .addStep(firstStep) - .addStep(secondStep) - .addStep(thirdStep) + ProbingSequence sequence = new ProbingSequence.Builder(mockToken) + .add(firstStep) + .add(secondStep) + .add(thirdStep) .build(); - assertThat(firstStep.nextStep()).isEqualTo(secondStep); - assertThat(secondStep.nextStep()).isEqualTo(thirdStep); - assertThat(thirdStep.nextStep()).isEqualTo(firstStep); + assertThat(sequence.get()).isEqualTo(firstStep); + sequence = sequence.next(); - sequence.start(); + assertThat(sequence.get()).isEqualTo(secondStep); + sequence = sequence.next(); - verify(firstStep, times(1)).accept(testToken); + assertThat(sequence.get()).isEqualTo(thirdStep); + sequence = sequence.next(); + + assertThat(sequence.get()).isEqualTo(firstStep); } @Test public void testSequenceAdvancedConstruction_Success() { + ProbingStep firstStep = Mockito.mock(ProbingStep.class); + ProbingStep secondStep = Mockito.mock(ProbingStep.class); + ProbingStep thirdStep = Mockito.mock(ProbingStep.class); - ProbingSequence sequence = new ProbingSequence.Builder(testToken) - .addStep(thirdStep) - .addStep(secondStep) + ProbingSequence sequence = new ProbingSequence.Builder(mockToken) + .add(thirdStep) + .add(secondStep) .markFirstRepeated() - .addStep(firstStep) + .add(firstStep) .build(); - assertThat(firstStep.nextStep()).isEqualTo(secondStep); - assertThat(secondStep.nextStep()).isEqualTo(firstStep); - assertThat(thirdStep.nextStep()).isEqualTo(secondStep); + assertThat(sequence.get()).isEqualTo(thirdStep); + sequence = sequence.next(); + + assertThat(sequence.get()).isEqualTo(secondStep); + sequence = sequence.next(); + + assertThat(sequence.get()).isEqualTo(firstStep); + sequence = sequence.next(); + + assertThat(sequence.get()).isEqualTo(secondStep); + + } + + @Test + public void testRunStep_Success() throws UndeterminedStateException { + //Always returns a succeeded future on call to mockAction. + doReturn(channel.newSucceededFuture()).when(mockAction).call(); + + // Has mockStep always return mockAction on call to generateAction + doReturn(mockAction).when(mockStep).generateAction(any(Token.class)); + + //Dummy step that server purpose of placeholder to test ability of ProbingSequence to move on. + ProbingStep secondStep = Mockito.mock(ProbingStep.class); + ProbingAction secondAction = Mockito.mock(ProbingAction.class); + + doReturn(channel.newFailedFuture(new UnrecoverableStateException(""))).when(secondAction) + .call(); + doReturn(secondAction).when(secondStep).generateAction(mockToken); + + //Build testable sequence from mocked components. + ProbingSequence sequence = new ProbingSequence.Builder(mockToken) + .add(mockStep) + .add(secondStep) + .build(); sequence.start(); - verify(thirdStep, times(1)).accept(testToken); + // We expect to have only generated actions from mockStep once, and we expect to have called + // this generated action only once, as when we move on to secondStep, it terminates the + // sequence. + verify(mockStep).generateAction(any(Token.class)); + verify(mockStep).generateAction(mockToken); + verify(mockAction).call(); + + // Similarly, we expect to generate actions and call the action from the secondStep once, as + // after calling it, the sequence should be terminated + verify(secondStep).generateAction(any(Token.class)); + verify(secondStep).generateAction(mockToken); + verify(secondAction).call(); + + //We should have modified the token's channel after the first, succeeded step. + assertThat(mockToken.channel()).isEqualTo(channel); } + @Test + public void testRunLoop_Success() throws UndeterminedStateException { + // Always returns a succeeded future on call to mockAction. + doReturn(channel.newSucceededFuture()).when(mockAction).call(); + + // Has mockStep always return mockAction on call to generateAction + doReturn(mockAction).when(mockStep).generateAction(mockToken); + + // Dummy step that server purpose of placeholder to test ability of ProbingSequence to move on. + ProbingStep secondStep = Mockito.mock(ProbingStep.class); + ProbingAction secondAction = Mockito.mock(ProbingAction.class); + + // Necessary for success of ProbingSequence runStep method as it calls get().protocol() + doReturn(mockProtocol).when(secondStep).protocol(); + + // We ensure that secondStep has necessary attributes to be successful step to pass on to + // mockStep once more. + doReturn(channel.newSucceededFuture()).when(secondAction).call(); + doReturn(secondAction).when(secondStep).generateAction(mockToken); + + // We get a secondToken that is returned when we are on our second loop in the sequence. This + // will inform mockStep on when to generate a different ProbingAction. + Token secondToken = Mockito.mock(Token.class); + doReturn(secondToken).when(mockToken).next(); + + // The thirdAction we use is made so that when it is called, it will halt the ProbingSequence + // by returning an UnrecoverableStateException. + ProbingAction thirdAction = Mockito.mock(ProbingAction.class); + doReturn(channel.newFailedFuture(new UnrecoverableStateException(""))).when(thirdAction).call(); + doReturn(thirdAction).when(mockStep).generateAction(secondToken); + + //Build testable sequence from mocked components. + ProbingSequence sequence = new ProbingSequence.Builder(mockToken) + .add(mockStep) + .add(secondStep) + .build(); + + sequence.start(); + + // We expect to have generated actions from mockStep twice (once for mockToken and once for + // secondToken), and we expectto have called each generated action only once, as when we move + // on to mockStep the second time, it will terminate the sequence after calling thirdAction. + verify(mockStep, times(2)).generateAction(any(Token.class)); + verify(mockStep).generateAction(mockToken); + verify(mockStep).generateAction(secondToken); + verify(mockAction).call(); + verify(thirdAction).call(); + + // Similarly, we expect to generate actions and call the action from the secondStep once, as + // after calling it, we move on to mockStep again, which terminates the sequence. + verify(secondStep).generateAction(any(Token.class)); + verify(secondStep).generateAction(mockToken); + verify(secondAction).call(); + + //We should have modified the token's channel after the first, succeeded step. + assertThat(mockToken.channel()).isEqualTo(channel); + } + + /** + * Test for when we expect Failure within try catch block of generating and calling a + * {@link ProbingAction}. + * + * @throws UndeterminedStateException - necessary for having mock return anything on a call to + * {@code generateAction}. + */ + private void testActionFailure() throws UndeterminedStateException { + //Dummy step that server purpose of placeholder to test ability of ProbingSequence to move on. + ProbingStep secondStep = Mockito.mock(ProbingStep.class); + + // We create a second token that when used to generate an action throws an + // UnrecoverableStateException to terminate the sequence + Token secondToken = Mockito.mock(Token.class); + doReturn(secondToken).when(mockToken).next(); + doThrow(new UnrecoverableStateException("")).when(mockStep).generateAction(secondToken); + + //Build testable sequence from mocked components. + ProbingSequence sequence = new ProbingSequence.Builder(mockToken) + .add(mockStep) + .add(secondStep) + .build(); + + sequence.start(); + + // We expect that we have generated actions twice. First, when we actually test generateAction + // with an actual call using mockToken, and second when we throw an + // UnrecoverableStateException with secondToken. + verify(mockStep, times(2)).generateAction(any(Token.class)); + verify(mockStep).generateAction(mockToken); + verify(mockStep).generateAction(secondToken); + + // We should never reach the step where we modify the channel, as it should have failed by then + assertThat(mockToken.channel()).isNull(); + assertThat(secondToken.channel()).isNull(); + + // We should never reach the second step, since we fail on the first step, then terminate on + // the first step after retrying. + verify(secondStep, times(0)).generateAction(any(Token.class)); + } + + @Test + public void testRunStep_FailureRunning() throws UndeterminedStateException { + // Returns a failed future when calling the generated mock action. + doReturn(channel.newFailedFuture(new FailureException(""))).when(mockAction).call(); + + // Returns mock action on call to generate action for ProbingStep. + doReturn(mockAction).when(mockStep).generateAction(mockToken); + + //Tests generic behavior we expect when we fail in generating or calling an action. + testActionFailure(); + + // We only expect to have called this action once, as we only get it from one generateAction + // call. + verify(mockAction).call(); + + + } + + + @Test + public void testRunStep_FailureGenerating() throws UndeterminedStateException { + // Create a mock first step that returns the dummy action when called to generate an action. + doThrow(UndeterminedStateException.class).when(mockStep).generateAction(mockToken); + + testActionFailure(); + + // We expect to have never called this action, as we fail each time whenever generating actions. + verify(mockAction, times(0)).call(); + } } diff --git a/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java b/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java index db7ec2719..132f19f8b 100644 --- a/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java +++ b/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java @@ -16,13 +16,9 @@ package google.registry.monitoring.blackbox; import static com.google.common.truth.Truth.assertThat; import static google.registry.monitoring.blackbox.ProbingAction.CONNECTION_FUTURE_KEY; -import static java.nio.charset.StandardCharsets.US_ASCII; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import com.google.common.collect.ImmutableList; import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException; @@ -34,8 +30,7 @@ import google.registry.monitoring.blackbox.messages.OutboundMessageType; import google.registry.monitoring.blackbox.messages.TestMessage; import google.registry.monitoring.blackbox.tokens.Token; import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.EventLoopGroup; import io.netty.channel.embedded.EmbeddedChannel; @@ -43,7 +38,6 @@ import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; import io.netty.channel.nio.NioEventLoopGroup; import org.joda.time.Duration; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.mockito.Mockito; @@ -62,14 +56,11 @@ public class ProbingStepTest { private static final int PROTOCOL_PORT = 0; private static final String TEST_MESSAGE = "TEST_MESSAGE"; private static final String SECONDARY_TEST_MESSAGE = "SECONDARY_TEST_MESSAGE"; - private static final LocalAddress ADDRESS = new LocalAddress(ADDRESS_NAME); - + private static final LocalAddress address = new LocalAddress(ADDRESS_NAME); private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1); private final Bootstrap bootstrap = new Bootstrap() .group(eventLoopGroup) .channel(LocalChannel.class); - - /** * Used for testing how well probing step can create connection to blackbox server */ @@ -91,13 +82,53 @@ public class ProbingStepTest { private Token testToken(String host) throws UndeterminedStateException { Token token = Mockito.mock(Token.class); doReturn(host).when(token).host(); - doAnswer(answer -> answer.getArgument(0)).when(token) + doAnswer(answer -> ((OutboundMessageType) answer.getArgument(0)).modifyMessage(host)) + .when(token) .modifyMessage(any(OutboundMessageType.class)); + return token; } @Test - public void testNewChannel() throws Exception { + public void testProbingActionGenerate_embeddedChannel() throws UndeterminedStateException { + // Sets up Protocol to represent existing channel connection. + Protocol testProtocol = Protocol.builder() + .setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler)) + .setName(PROTOCOL_NAME) + .setPort(PROTOCOL_PORT) + .setPersistentConnection(true) + .build(); + + // Sets up an embedded channel to contain the two handlers we created already. + EmbeddedChannel channel = new EmbeddedChannel(conversionHandler, testHandler); + channel.attr(CONNECTION_FUTURE_KEY).set(channel.newSucceededFuture()); + + // Sets up testToken to return arbitrary value, and the embedded channel. Used for when the + // ProbingStep generates an ExistingChannelAction. + Token testToken = testToken(SECONDARY_TEST_MESSAGE); + doReturn(channel).when(testToken).channel(); + + // Sets up generic {@link ProbingStep} that we are testing. + ProbingStep testStep = ProbingStep.builder() + .setMessageTemplate(new TestMessage(TEST_MESSAGE)) + .setBootstrap(bootstrap) + .setDuration(Duration.ZERO) + .setProtocol(testProtocol) + .build(); + + ProbingAction testAction = testStep.generateAction(testToken); + + assertThat(testAction.channel()).isEqualTo(channel); + assertThat(testAction.delay()).isEqualTo(Duration.ZERO); + assertThat(testAction.outboundMessage().toString()).isEqualTo(SECONDARY_TEST_MESSAGE); + assertThat(testAction.host()).isEqualTo(SECONDARY_TEST_MESSAGE); + assertThat(testAction.protocol()).isEqualTo(testProtocol); + + + } + + @Test + public void testProbingActionGenerate_newChannel() throws UndeterminedStateException { // Sets up Protocol for when we create a new channel. Protocol testProtocol = Protocol.builder() .setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler)) @@ -106,97 +137,32 @@ public class ProbingStepTest { .setPersistentConnection(false) .build(); - // Sets up our main step (firstStep) and throwaway step (dummyStep). - ProbingStep firstStep = ProbingStep.builder() + // Sets up generic ProbingStep that we are testing. + ProbingStep testStep = ProbingStep.builder() + .setMessageTemplate(new TestMessage(TEST_MESSAGE)) .setBootstrap(bootstrap) .setDuration(Duration.ZERO) - .setMessageTemplate(new TestMessage(TEST_MESSAGE)) .setProtocol(testProtocol) .build(); - //Sets up mock dummy step that returns succeeded promise when we successfully reach it. - ProbingStep dummyStep = Mockito.mock(ProbingStep.class); - - firstStep.nextStep(dummyStep); - // Sets up testToken to return arbitrary values, and no channel. Used when we create a new // channel. Token testToken = testToken(ADDRESS_NAME); - //Set up blackbox server that receives our messages then echoes them back to us - nettyRule.setUpServer(ADDRESS); + // Sets up server listening at LocalAddress so generated action can have successful connection. + nettyRule.setUpServer(address); - //checks that the ProbingSteps are appropriately pointing to each other - assertThat(firstStep.nextStep()).isEqualTo(dummyStep); + ProbingAction testAction = testStep.generateAction(testToken); - //Call accept on the first step, which should send our message to the server, which will then be - //echoed back to us, causing us to move to the next step - firstStep.accept(testToken); + ChannelFuture connectionFuture = testAction.channel().attr(CONNECTION_FUTURE_KEY).get(); + connectionFuture = connectionFuture.syncUninterruptibly(); - //checks that we have appropriately sent the write message to server - nettyRule.assertReceivedMessage(TEST_MESSAGE); + assertThat(connectionFuture.isSuccess()).isTrue(); + assertThat(testAction.delay()).isEqualTo(Duration.ZERO); + assertThat(testAction.outboundMessage().toString()).isEqualTo(ADDRESS_NAME); + assertThat(testAction.host()).isEqualTo(ADDRESS_NAME); + assertThat(testAction.protocol()).isEqualTo(testProtocol); - //checks that when the future is successful, we pass down the requisite token - verify(dummyStep, times(1)).accept(any(Token.class)); - } - //TODO - Currently, this test fails to receive outbound messages from the embedded channel, which - // we will fix in a later release. - @Ignore - @Test - public void testWithSequence_ExistingChannel() throws Exception { - // Sets up Protocol for when a channel already exists. - Protocol testProtocol = Protocol.builder() - .setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler)) - .setName(PROTOCOL_NAME) - .setPort(PROTOCOL_PORT) - .setPersistentConnection(true) - .build(); - - // Sets up our main step (firstStep) and throwaway step (dummyStep). - ProbingStep firstStep = ProbingStep.builder() - .setBootstrap(bootstrap) - .setDuration(Duration.ZERO) - .setMessageTemplate(new TestMessage(TEST_MESSAGE)) - .setProtocol(testProtocol) - .build(); - - //Sets up mock dummy step that returns succeeded promise when we successfully reach it. - ProbingStep dummyStep = Mockito.mock(ProbingStep.class); - - firstStep.nextStep(dummyStep); - - // Sets up an embedded channel to contain the two handlers we created already. - EmbeddedChannel channel = new EmbeddedChannel(conversionHandler, testHandler); - - //Assures that the channel has a succeeded connectionFuture. - channel.attr(CONNECTION_FUTURE_KEY).set(channel.newSucceededFuture()); - - // Sets up testToken to return arbitrary value, and the embedded channel. Used for when the - // ProbingStep generates an ExistingChannelAction. - Token testToken = testToken(""); - doReturn(channel).when(testToken).channel(); - - //checks that the ProbingSteps are appropriately pointing to each other - assertThat(firstStep.nextStep()).isEqualTo(dummyStep); - - //Call accept on the first step, which should send our message through the EmbeddedChannel - // pipeline - firstStep.accept(testToken); - - Object msg = channel.readOutbound(); - - while (msg == null) { - msg = channel.readOutbound(); - } - //Ensures the accurate message is sent down the pipeline - assertThat(((ByteBuf) channel.readOutbound()).toString(UTF_8)).isEqualTo(TEST_MESSAGE); - - //Write response to our message down EmbeddedChannel pipeline - channel.writeInbound(Unpooled.wrappedBuffer(SECONDARY_TEST_MESSAGE.getBytes(US_ASCII))); - - //At this point, we should have received the message, so the future obtained should be marked - // as a success - verify(dummyStep, times(1)).accept(any(Token.class)); } } diff --git a/prober/src/test/java/google/registry/monitoring/blackbox/tokens/WebWhoisTokenTest.java b/prober/src/test/java/google/registry/monitoring/blackbox/tokens/WebWhoisTokenTest.java index 9a3e75536..148558a2b 100644 --- a/prober/src/test/java/google/registry/monitoring/blackbox/tokens/WebWhoisTokenTest.java +++ b/prober/src/test/java/google/registry/monitoring/blackbox/tokens/WebWhoisTokenTest.java @@ -16,9 +16,9 @@ package google.registry.monitoring.blackbox.tokens; import static com.google.common.truth.Truth.assertThat; -import com.google.common.collect.ImmutableList; import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException; import google.registry.monitoring.blackbox.messages.HttpRequestMessage; +import google.registry.util.CircularList; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -29,15 +29,17 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class WebWhoisTokenTest { - private static String PREFIX = "whois.nic."; - private static String HOST = "starter"; - private static String FIRST_TLD = "first_test"; - private static String SECOND_TLD = "second_test"; - private static String THIRD_TLD = "third_test"; - private static ImmutableListIn its construction, we create a sequence of {@link CircularList} objects, each storing an + * instance of T. They each point to each other in a circular manner, such that we can perform + * circular iteration on the elements. Once finished building, we return this first {@link + * CircularList} object in the sequence.
+ */ +public class CircularListSupports adding in element at a time, adding an {@link Iterable} + * of elements, and adding an variable number of elemetns.
+ * + *Sets first element added to {@code first}, and when built, points last element to the + * {@code first} element.
+ */ + public abstract static class AbstractBuilder