/**
* Copyright 2015-2017 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package zipkin.storage;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import zipkin.Annotation;
import zipkin.Constants;
import zipkin.Span;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static zipkin.Constants.SERVER_RECV;
import static zipkin.TestObjects.APP_ENDPOINT;
import static zipkin.TestObjects.TODAY;
import static zipkin.TraceKeys.HTTP_METHOD;
public class QueryRequestTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void serviceNameCanBeNull() {
assertThat(QueryRequest.builder().build().serviceName)
.isNull();
}
@Test
public void serviceNameCantBeEmpty() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("serviceName was empty");
QueryRequest.builder().serviceName("").build();
}
@Test
public void spanNameCantBeEmpty() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("spanName was empty");
QueryRequest.builder().serviceName("foo").spanName("").build();
}
@Test
public void annotationCantBeEmpty() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("annotation was empty");
QueryRequest.builder().serviceName("foo").addAnnotation("").build();
}
/**
* Particularly in the case of cassandra, indexing boundary annotations isn't fruitful work, and
* not helpful to users. Nevertheless we should ensure an unlikely caller gets an exception.
*/
@Test
public void annotationCantBeCore() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("queries cannot be refined by core annotations: sr");
QueryRequest.builder().serviceName("foo").addAnnotation(Constants.SERVER_RECV).build();
}
@Test
public void binaryAnnotationKeyCantBeEmpty() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("binary annotation key was empty");
QueryRequest.builder().serviceName("foo").addBinaryAnnotation("", "bar").build();
}
@Test
public void binaryAnnotationValueCantBeEmpty() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("binary annotation value for foo was empty");
QueryRequest.builder().serviceName("foo").addBinaryAnnotation("foo", "").build();
}
@Test
public void endTsMustBePositive() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("endTs should be positive, in epoch microseconds: was 0");
QueryRequest.builder().serviceName("foo").endTs(0L).build();
}
@Test
public void limitMustBePositive() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("limit should be positive: was 0");
QueryRequest.builder().serviceName("foo").limit(0).build();
}
@Test
public void annotationQuery_roundTrip() {
String annotationQuery = "http.method=GET and error";
QueryRequest request =
QueryRequest.builder().serviceName("security-service").parseAnnotationQuery(annotationQuery).build();
assertThat(request.binaryAnnotations)
.containsEntry(HTTP_METHOD, "GET")
.hasSize(1);
assertThat(request.annotations)
.containsExactly(Constants.ERROR);
assertThat(request.toAnnotationQuery())
.isEqualTo(annotationQuery);
}
@Test
public void annotationQuery_complexValue() {
String annotationQuery = "http.method=GET=1 and error";
QueryRequest request = QueryRequest.builder().serviceName("security-service")
.parseAnnotationQuery(annotationQuery).build();
assertThat(request.binaryAnnotations)
.containsEntry(HTTP_METHOD, "GET=1")
.hasSize(1);
assertThat(request.annotations)
.containsExactly(Constants.ERROR);
assertThat(request.toAnnotationQuery())
.isEqualTo(annotationQuery);
}
@Test
public void annotationQuery_missingValue() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("binary annotation value for http.method was empty");
String annotationQuery = "http.method=";
QueryRequest request =
QueryRequest.builder().serviceName("security-service").parseAnnotationQuery(annotationQuery).build();
assertThat(request.annotations)
.containsExactly(HTTP_METHOD);
}
@Test
public void toAnnotationQueryWhenNoInputIsNull() {
QueryRequest request = QueryRequest.builder().serviceName("security-service").build();
assertThat(request.toAnnotationQuery())
.isNull();
}
@Test
public void minDuration_mustBePositive() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("minDuration must be a positive number of microseconds");
QueryRequest.builder().serviceName("foo").minDuration(0L).build();
}
@Test
public void maxDuration_onlyWithMinDuration() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("maxDuration is only valid with minDuration");
QueryRequest.builder().serviceName("foo").maxDuration(0L).build();
}
@Test
public void maxDuration_greaterThanOrEqualToMinDuration() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("maxDuration should be >= minDuration");
QueryRequest.builder().serviceName("foo").minDuration(1L).maxDuration(0L).build();
}
/** When a span comes in without a timestamp, use the implicit one based on annotations. */
@Test
public void matchesImplicitTimestamp() {
Span asyncReceive = Span.builder().traceId(10L).id(10L).name("receive")
.addAnnotation(Annotation.create((TODAY) * 1000, SERVER_RECV, APP_ENDPOINT))
.build();
QueryRequest request = QueryRequest.builder().endTs(TODAY).build();
assertThat(request.test(asList(asyncReceive)))
.isTrue();
}
}