Search in sources :

Example 1 with HandlerMethodToExecute

use of com.nike.riposte.server.handler.base.BaseInboundHandlerWithTracingAndMdcSupport.HandlerMethodToExecute in project riposte by Nike-Inc.

the class BaseInboundHandlerWithTracingAndMdcSupportTest method verifyMethodBehaviorDetails.

private void verifyMethodBehaviorDetails(String methodName, Object[] methodArgs, PipelineContinuationBehavior doMethodReturnValue, boolean shouldPerformDebugLogging, boolean forceEnableDTraceOnAllMethods, boolean isDefaultMethodImpl, boolean argsEligibleForLinkUnlink, boolean shouldExplodeInDoMethod) throws InvocationTargetException, IllegalAccessException {
    // Setup all the things!
    BaseInboundHandlerWithTracingAndMdcSupport handlerSpy = spy(handler);
    Method mainMethod = findMethodWithName(BaseInboundHandlerWithTracingAndMdcSupport.class, methodName);
    String methodNameWithFirstCharCapitalized = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
    String doMethodName = "do" + methodNameWithFirstCharCapitalized;
    Method doMethod = findMethodWithName(BaseInboundHandlerWithTracingAndMdcSupport.class, doMethodName);
    String fireEventMethodName = "fire" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
    Method fireEventMethod = ("handlerAdded".equals(methodName) || "handlerRemoved".equals(methodName)) ? null : findMethodWithName(ChannelHandlerContext.class, fireEventMethodName);
    Whitebox.setInternalState(handlerSpy, "debugHandlerMethodCalls", shouldPerformDebugLogging);
    Whitebox.setInternalState(handlerSpy, "forceEnableDTraceOnAllMethods", forceEnableDTraceOnAllMethods);
    Whitebox.setInternalState(handlerSpy, "isDefaultDo" + methodNameWithFirstCharCapitalized + "Impl", isDefaultMethodImpl);
    HandlerMethodToExecute expectedHandlerMethodToExecute = constructHandlerMethodToExecuteFromMethodName(methodName);
    doReturn(argsEligibleForLinkUnlink).when(handlerSpy).argsAreEligibleForLinkingAndUnlinkingDistributedTracingInfo(eq(expectedHandlerMethodToExecute), eq(ctxMock), anyObject(), any(Throwable.class));
    TestLogger handlerLogger = TestLoggerFactory.getTestLogger(((Logger) Whitebox.getInternalState(handlerSpy, "logger")).getName());
    handlerLogger.clear();
    ObjectHolder<Boolean> calledLinkMethod = new ObjectHolder<>(false);
    ObjectHolder<Boolean> calledDoMethod = new ObjectHolder<>(false);
    ObjectHolder<Boolean> calledUnlinkMethod = new ObjectHolder<>(false);
    ObjectHolder<Boolean> calledSuperMethod = new ObjectHolder<>(false);
    @SuppressWarnings("unchecked") Pair<Deque<Span>, Map<String, String>> linkTracingAndMdcReturnVal = mock(Pair.class);
    boolean shouldCallLinkAndUnlinkMethods = (!isDefaultMethodImpl && argsEligibleForLinkUnlink) || forceEnableDTraceOnAllMethods || shouldPerformDebugLogging;
    // Configure the linkTracingAndMdcToCurrentThread method to:
    //      1. Indicate that the link method was called (for future assertions).
    //      2. Assert that the do* method has not yet been called.
    //      3. Assert that the unlink method has not yet been called.
    //      4. Assert that the super method has not yet been called.
    //      5. Return linkTracingAndMdcReturnVal (for future assertions).
    doAnswer(invocation -> {
        calledLinkMethod.heldObject = true;
        assertThat(calledDoMethod.heldObject, is(false));
        assertThat(calledUnlinkMethod.heldObject, is(false));
        assertThat(calledSuperMethod.heldObject, is(false));
        return linkTracingAndMdcReturnVal;
    }).when(handlerSpy).linkTracingAndMdcToCurrentThread(ctxMock);
    // Configure the do* method depending on whether it's supposed to explode with a IntentionalDoMethodException or not.
    if (shouldExplodeInDoMethod) {
        // The do* method is supposed to explode. Make it so.
        when(doMethod.invoke(handlerSpy, methodArgs)).thenThrow(new IntentionalDoMethodException("intentional exception"));
    } else {
        // The do* method is not supposed to explode. Configure it to:
        //      1. Indicate that the do* method was called (for future assertions).
        //      2. Assert that the link method was called previously if and only if shouldCallLinkAndUnlinkMethods is true.
        //      3. Assert that the unlink method has not yet been called.
        //      4. Assert that the super method has not yet been called.
        //      5. Return doMethodReturnValue (for future assertions).
        when(doMethod.invoke(handlerSpy, methodArgs)).thenAnswer(invocation -> {
            calledDoMethod.heldObject = true;
            assertThat(calledLinkMethod.heldObject, is(shouldCallLinkAndUnlinkMethods));
            assertThat(calledUnlinkMethod.heldObject, is(false));
            assertThat(calledSuperMethod.heldObject, is(false));
            return doMethodReturnValue;
        });
    }
    // Configure the unlinkTracingAndMdcFromCurrentThread method to:
    //      1. Indicate that the unlink method was called (for future assertions).
    //      2. Assert that the link method was called previously if and only if shouldCallLinkAndUnlinkMethods is true.
    //      3. Assert that the do* method was called (if it was setup to *not* explode).
    //      4. Assert that the super method has not yet been called.
    //      5. Assert that the tracing info arg passed in was the same data that was returned by the link method.
    doAnswer(invocation -> {
        calledUnlinkMethod.heldObject = true;
        assertThat(calledLinkMethod.heldObject, is(shouldCallLinkAndUnlinkMethods));
        if (shouldExplodeInDoMethod)
            assertThat(calledDoMethod.heldObject, is(false));
        else
            assertThat(calledDoMethod.heldObject, is(true));
        assertThat(calledSuperMethod.heldObject, is(false));
        assertThat(invocation.getArguments()[1], is(linkTracingAndMdcReturnVal));
        return null;
    }).when(handlerSpy).unlinkTracingAndMdcFromCurrentThread(ctxMock, linkTracingAndMdcReturnVal);
    //      we can use that as a proxy to verify that the super method was called.
    if (fireEventMethod != null) {
        Object[] fireEventArgs = new Object[fireEventMethod.getParameterCount()];
        // Configure the ctxMock's fire* method to:
        //      1. Indicate that the super method was called (for future assertions).
        //      2. Assert that the link method was called previously if and only if shouldCallLinkAndUnlinkMethods is true.
        //      3. Assert that the do* method was called previously.
        //      4. Assert that the unlink method was called previously if and only if shouldCallLinkAndUnlinkMethods is true.
        when(fireEventMethod.invoke(ctxMock, fireEventArgs)).thenAnswer(invocation -> {
            calledSuperMethod.heldObject = true;
            assertThat(calledLinkMethod.heldObject, is(shouldCallLinkAndUnlinkMethods));
            assertThat(calledDoMethod.heldObject, is(true));
            assertThat(calledUnlinkMethod.heldObject, is(shouldCallLinkAndUnlinkMethods));
            return ctxMock;
        });
    }
    try {
        // Execute the method in question.
        mainMethod.invoke(handlerSpy, methodArgs);
    } catch (InvocationTargetException ex) {
        // An exception occurred during method execution.
        if (ex.getCause() instanceof AssertionError) {
            //      on how to interpret what went wrong.
            throw new RuntimeException("An AssertionError was experienced while running the method-to-be-tested. This either means a bug in " + "BaseInboundHandlerWithTracingAndMdcSupport for the method in question, or this unit test is " + "out-of-date and needs to be fixed. See the AssertionError cause for the true cause of the error.", ex.getCause());
        }
        // Not an AssertionError, so this should be the expected IntentionalDoMethodException.
        assertThat(ex.getCause(), instanceOf(IntentionalDoMethodException.class));
        // Make sure we were supposed to throw a IntentionalDoMethodException.
        assertThat(shouldExplodeInDoMethod, is(true));
        // The do* method was called, so mark it that way (for future assertions).
        calledDoMethod.heldObject = true;
    }
    assertThat(calledLinkMethod.heldObject, is(shouldCallLinkAndUnlinkMethods));
    // The do* method should always be called no matter what.
    assertThat(calledDoMethod.heldObject, is(true));
    assertThat(calledUnlinkMethod.heldObject, is(shouldCallLinkAndUnlinkMethods));
    if (fireEventMethod != null) {
        boolean shouldHaveCalledSuperMethod = !shouldExplodeInDoMethod && (doMethodReturnValue == null || PipelineContinuationBehavior.CONTINUE.equals(doMethodReturnValue));
        assertThat(calledSuperMethod.heldObject, is(shouldHaveCalledSuperMethod));
    }
    if (shouldPerformDebugLogging) {
        List<LoggingEvent> loggingEvents = handlerLogger.getLoggingEvents();
        assertThat(loggingEvents.size(), is(1));
        assertThat(loggingEvents.get(0).getMessage(), containsString(" " + methodName + " for "));
    }
    boolean argsEligibleMethodShouldHaveBeenCalled = !isDefaultMethodImpl;
    int numExpectedArgsEligibleMethodCalls = (argsEligibleMethodShouldHaveBeenCalled) ? 1 : 0;
    verify(handlerSpy, times(numExpectedArgsEligibleMethodCalls)).argsAreEligibleForLinkingAndUnlinkingDistributedTracingInfo(eq(expectedHandlerMethodToExecute), eq(ctxMock), anyObject(), any(Throwable.class));
}
Also used : ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) Method(java.lang.reflect.Method) CoreMatchers.containsString(org.hamcrest.CoreMatchers.containsString) TestLogger(uk.org.lidalia.slf4jtest.TestLogger) Deque(java.util.Deque) InvocationTargetException(java.lang.reflect.InvocationTargetException) LoggingEvent(uk.org.lidalia.slf4jtest.LoggingEvent) HandlerMethodToExecute(com.nike.riposte.server.handler.base.BaseInboundHandlerWithTracingAndMdcSupport.HandlerMethodToExecute) Matchers.anyObject(org.mockito.Matchers.anyObject) HashMap(java.util.HashMap) Map(java.util.Map)

Aggregations

HandlerMethodToExecute (com.nike.riposte.server.handler.base.BaseInboundHandlerWithTracingAndMdcSupport.HandlerMethodToExecute)1 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)1 InvocationTargetException (java.lang.reflect.InvocationTargetException)1 Method (java.lang.reflect.Method)1 Deque (java.util.Deque)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 CoreMatchers.containsString (org.hamcrest.CoreMatchers.containsString)1 Matchers.anyObject (org.mockito.Matchers.anyObject)1 LoggingEvent (uk.org.lidalia.slf4jtest.LoggingEvent)1 TestLogger (uk.org.lidalia.slf4jtest.TestLogger)1