package com.github.kristofa.brave;
import com.github.kristofa.brave.internal.DefaultSpanCodec;
import com.twitter.zipkin.gen.Endpoint;
import com.twitter.zipkin.gen.Span;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import zipkin.Constants;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class ClientTracerTest {
private static final long START_TIME_MICROSECONDS = System.currentTimeMillis() * 1000;
private static final String REQUEST_NAME = "requestname";
private static final long TRACE_ID = 105;
private static final SpanId PARENT_CONTEXT =
SpanId.builder().traceId(TRACE_ID).spanId(103).build();
private static final Endpoint ENDPOINT = Endpoint.create("service", 80);
static final zipkin.Endpoint ZIPKIN_ENDPOINT = zipkin.Endpoint.create("service", 80);
private static final zipkin.Span BASE_SPAN =
DefaultSpanCodec.toZipkin(Brave.toSpan(SpanId.builder().spanId(TRACE_ID).build()));
long timestamp = START_TIME_MICROSECONDS;
AnnotationSubmitter.Clock clock = () -> timestamp;
private Span span = Brave.toSpan(SpanId.builder().spanId(TRACE_ID).sampled(true).build());
List<zipkin.Span> spans = new ArrayList<>();
Brave brave = newBrave();
Recorder recorder = brave.clientTracer().recorder();
@Before
public void setup() {
ThreadLocalServerClientAndLocalSpanState.clear();
}
Brave newBrave() {
return new Brave.Builder(ENDPOINT).clock(clock).reporter(spans::add).build();
}
@Test
public void setClientSent_noopWhenNoCurrentSpan() {
brave.clientTracer().setClientSent();
assertThat(spans).isEmpty();
recorder.flush(span);
assertThat(spans).matches(s -> s.isEmpty() || s.contains(BASE_SPAN));
}
@Test
public void setClientSent_doesntFlush() {
brave.clientSpanThreadBinder().setCurrentSpan(span);
brave.clientTracer().setClientSent();
assertThat(spans).matches(s -> s.isEmpty() || s.contains(BASE_SPAN));
}
@Test
public void setClientSent() {
brave.clientSpanThreadBinder().setCurrentSpan(span);
brave.clientTracer().setClientSent();
recorder.flush(brave.clientSpanThreadBinder().get());
assertThat(spans.get(0).annotations).containsExactly(
zipkin.Annotation.create(START_TIME_MICROSECONDS,
Constants.CLIENT_SEND,
ZIPKIN_ENDPOINT
)
);
}
@Test
public void setClientSent_serverAddress() {
brave.clientSpanThreadBinder().setCurrentSpan(span);
brave.clientTracer().setClientSent(Endpoint.builder()
.ipv4(127 << 24 | 1).port(9).serviceName("foobar").build());
recorder.flush(span);
assertThat(spans.get(0).binaryAnnotations).containsExactly(
zipkin.BinaryAnnotation.address(
Constants.SERVER_ADDR,
zipkin.Endpoint.builder().serviceName("foobar").ipv4(127 << 24 | 1).port(9).build()
)
);
}
@Test
public void setClientSent_serverAddress_nullName() {
brave.clientSpanThreadBinder().setCurrentSpan(span);
brave.clientTracer()
.setClientSent(1 << 24 | 2 << 16 | 3 << 8 | 4, 9999, null);
recorder.flush(span);
assertThat(spans.get(0).binaryAnnotations.get(0).endpoint.serviceName)
.isEqualTo("unknown");
}
@Test
public void startNewSpan_unsampledServerSpan() {
brave.serverSpanThreadBinder().setCurrentSpan(ServerSpan.NOT_SAMPLED);
assertNull(brave.clientTracer().startNewSpan(REQUEST_NAME));
assertNull(brave.clientSpanThreadBinder().get());
}
@Test
public void startNewSpan_unsampledBrave() {
Brave brave = new Brave.Builder(ENDPOINT).traceSampler(Sampler.NEVER_SAMPLE).build();
assertNull(brave.clientTracer().startNewSpan(REQUEST_NAME));
assertNull(brave.clientSpanThreadBinder().get());
}
@Test
public void startNewSpan_createsNewTraceAndAttachesCurrentSpan() {
brave.serverSpanThreadBinder().setCurrentSpan(ServerSpan.EMPTY);
SpanId newContext = brave.clientTracer().startNewSpan(REQUEST_NAME);
assertNotNull(newContext);
assertNull(newContext.nullableParentId());
assertThat(Brave.context(brave.clientSpanThreadBinder().get()))
.isEqualTo(newContext);
}
@Test
public void startNewSpan_createsChild() {
final ServerSpan parentSpan = ServerSpan.create(Brave.toSpan(PARENT_CONTEXT));
brave.serverSpanThreadBinder().setCurrentSpan(parentSpan);
SpanId newContext = brave.clientTracer().startNewSpan(REQUEST_NAME);
assertNotNull(newContext);
assertEquals(TRACE_ID, newContext.traceId);
assertEquals(PARENT_CONTEXT.spanId, newContext.parentId);
assertThat(Brave.context(brave.clientSpanThreadBinder().get()))
.isEqualTo(newContext);
}
@Test
public void startNewSpan_setsRequestName() {
brave.clientTracer().startNewSpan(REQUEST_NAME);
recorder.flush(brave.clientSpanThreadBinder().get());
assertThat(spans.get(0).name).isEqualTo(REQUEST_NAME);
}
@Test
public void setClientReceived_noopWhenNoCurrentSpan() {
brave.clientTracer().setClientReceived();
assertThat(spans).isEmpty();
assertThat(brave.clientSpanThreadBinder().get()).isNull();
}
@Test
public void setClientReceived() {
recorder.start(span, START_TIME_MICROSECONDS);
brave.clientSpanThreadBinder().setCurrentSpan(span);
timestamp = START_TIME_MICROSECONDS + 100;
brave.clientTracer().setClientReceived();
assertThat(spans.get(0).duration).isEqualTo(100L);
assertThat(spans.get(0).annotations).contains(
zipkin.Annotation.create(START_TIME_MICROSECONDS + 100,
Constants.CLIENT_RECV,
ZIPKIN_ENDPOINT
)
);
}
@Test
public void setClientReceived_preciseDuration() {
recorder.start(span, START_TIME_MICROSECONDS);
brave.clientSpanThreadBinder().setCurrentSpan(span);
timestamp = START_TIME_MICROSECONDS + 500;
brave.clientTracer().setClientReceived();
assertThat(spans.get(0).duration).isEqualTo(500L);
}
/** Duration of less than one microsecond is confusing to plot and could coerce to null. */
@Test
public void setClientReceived_lessThanMicrosRoundUp() {
recorder.start(span, START_TIME_MICROSECONDS);
brave.clientSpanThreadBinder().setCurrentSpan(span);
timestamp = START_TIME_MICROSECONDS; // no time passed!
brave.clientTracer().setClientReceived();
assertThat(spans.get(0).duration).isEqualTo(1L);
}
}