@Override public void setupTracingCompletionWhenAsyncRequestCompletes( HttpServletRequest asyncRequest, HttpServletResponse asyncResponse, TracingState originalRequestTracingState, HttpTagAndSpanNamingStrategy<HttpServletRequest,HttpServletResponse> tagAndNamingStrategy, HttpTagAndSpanNamingAdapter<HttpServletRequest,HttpServletResponse> tagAndNamingAdapter ) { // Async processing was started, so we have to complete it with a listener. asyncRequest.getAsyncContext().addListener( new WingtipsRequestSpanCompletionAsyncListener( originalRequestTracingState, tagAndNamingStrategy, tagAndNamingAdapter ), asyncRequest, asyncResponse ); }
@Test public void onComplete_calls_completeRequestSpan_and_does_nothing_else() { // when implSpy.onComplete(asyncEventMock); // then verify(implSpy).onComplete(asyncEventMock); verify(implSpy).completeRequestSpan(asyncEventMock); verifyNoMoreInteractions(implSpy); }
@Override public void onComplete(AsyncEvent event) { completeRequestSpan(event); }
@Test public void onStartAsync_does_nothing_if_asyncEvent_has_null_AsyncContext() { // given // This should never happen in reality, but we protect against null pointer exceptions anyway. doReturn(null).when(asyncEventMock).getAsyncContext(); // when implSpy.onStartAsync(asyncEventMock); // then verify(implSpy).onStartAsync(asyncEventMock); verifyNoMoreInteractions(implSpy); }
@Test public void completeRequestSpan_does_nothing_if_listener_is_already_marked_completed() { // given implSpy.alreadyCompleted.set(true); assertThat(tracingStateSpan.isCompleted()).isFalse(); // when implSpy.completeRequestSpan(asyncEventMock); // then assertThat(tracingStateSpan.isCompleted()).isFalse(); assertThat(implSpy.alreadyCompleted.get()).isTrue(); }
@Test public void onStartAsync_propagates_the_listener_to_the_new_AsyncContext() { // given AsyncContext asyncContextMock = mock(AsyncContext.class); doReturn(asyncContextMock).when(asyncEventMock).getAsyncContext(); // when implSpy.onStartAsync(asyncEventMock); // then verify(asyncContextMock).addListener(implSpy, requestMock, responseMock); }
@Test public void constructor_sets_fields_as_expected() { // given TracingState tracingStateMock = mock(TracingState.class); // when WingtipsRequestSpanCompletionAsyncListener impl = new WingtipsRequestSpanCompletionAsyncListener(tracingStateMock, tagAndNamingStrategy, tagAndNamingAdapterMock); // then assertThat(impl.originalRequestTracingState).isSameAs(tracingStateMock); assertThat(impl.tagAndNamingStrategy).isSameAs(tagAndNamingStrategy); assertThat(impl.tagAndNamingAdapter).isSameAs(tagAndNamingAdapterMock); assertThat(impl.alreadyCompleted.get()).isFalse(); }
@Test public void completeRequestSpan_marks_listener_as_completed_even_if_unexpected_exception_occurs() { // given Tracer.getInstance().startRequestWithRootSpan("someOtherUnrelatedSpan"); TracingState unrelatedThreadTracingState = TracingState.getCurrentThreadTracingState(); final RuntimeException expectedExplosion = new RuntimeException("kaboom"); SpanRecorder explodingSpanRecorder = new SpanRecorder() { @Override public void spanCompleted(Span span) { throw expectedExplosion; } }; Tracer.getInstance().addSpanLifecycleListener(explodingSpanRecorder); assertThat(implSpy.alreadyCompleted.get()).isFalse(); // when Throwable actualEx = catchThrowable(() -> implSpy.completeRequestSpan(asyncEventMock)); // then assertThat(actualEx).isSameAs(expectedExplosion); assertThat(explodingSpanRecorder.completedSpans).hasSize(0); assertThat(implSpy.alreadyCompleted.get()).isTrue(); // Tracing state got reset back to original from when the method was called. assertThat(TracingState.getCurrentThreadTracingState()).isEqualTo(unrelatedThreadTracingState); }
@Test public void constructor_uses_default_NoOpHttpTagAdapter_if_passed_null_tag_adapter() { // when WingtipsRequestSpanCompletionAsyncListener impl = new WingtipsRequestSpanCompletionAsyncListener(tracingState, tagAndNamingStrategy, null); // then assertThat(impl.tagAndNamingAdapter).isSameAs(NoOpHttpTagAdapter.getDefaultInstance()); assertThat(impl.originalRequestTracingState).isSameAs(tracingState); assertThat(impl.tagAndNamingStrategy).isSameAs(tagAndNamingStrategy); }
implSpy.completeRequestSpan(asyncEventMock);
@Test public void constructor_uses_default_NoOpHttpTagStrategy_if_passed_null_tag_strategy() { // when WingtipsRequestSpanCompletionAsyncListener impl = new WingtipsRequestSpanCompletionAsyncListener(tracingState, null, tagAndNamingAdapterMock); // then assertThat(impl.tagAndNamingStrategy).isSameAs(NoOpHttpTagStrategy.getDefaultInstance()); assertThat(impl.originalRequestTracingState).isSameAs(tracingState); assertThat(impl.tagAndNamingAdapter).isSameAs(tagAndNamingAdapterMock); }
new WingtipsRequestSpanCompletionAsyncListener(tracingState, tagAndNamingStrategy, tagAndNamingAdapterMock) ); asyncEventMock = mock(AsyncEvent.class);