All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.tracing.test.simple.SpansAssert Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
/**
 * Copyright 2022 the original author or 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
 *
 * https://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 io.micrometer.tracing.test.simple;

import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import io.micrometer.common.docs.KeyName;
import io.micrometer.tracing.exporter.FinishedSpan;
import org.assertj.core.api.CollectionAssert;

/**
 * Assertion methods for {@code SimpleSpan}s.
 * 

* To create a new instance of this class, invoke * {@link SpansAssert#assertThat(Collection)} or {@link SpansAssert#then(Collection)}. * * @author Marcin Grzejszczak * @author Artem Bilan * @since 1.0.0 */ public class SpansAssert extends CollectionAssert { /** * Creates a new instance of {@link SpansAssert}. * @param actual actual object to assert */ protected SpansAssert(Collection actual) { super(actual); } /** * Creates the assert object for a collection of {@link FinishedSpan}. * @param actual span to assert against * @return span collection assertions */ public static SpansAssert assertThat(Collection actual) { return new SpansAssert(actual); } /** * Creates the assert object for a collection of {@link FinishedSpan}. * @param actual span to assert against * @return span collection assertions */ public static SpansAssert then(Collection actual) { return new SpansAssert(actual); } /** * Verifies that all spans have the same trace id. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpan)).haveSameTraceId();
     *
     * // assertions fail
     * assertThat(Arrays.asList(finishedSpanWithTraceIdX, finishedSpanWithTraceIdY)).haveSameTraceId();
* @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if spans do not have the same trace id. * @since 1.0.0 */ public SpansAssert haveSameTraceId() { isNotEmpty(); List traceIds = this.actual.stream() .map(FinishedSpan::getTraceId) .distinct() .collect(Collectors.toList()); if (traceIds.size() != 1) { failWithMessage("Spans should have same trace ids but found %s trace ids. Found following spans \n%s", traceIds, spansAsString()); } return this; } private String spansAsString() { return this.actual.stream().map(Object::toString).collect(Collectors.joining("\n")); } /** * Verifies that there is a span with a given name. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasASpanWithName("foo");
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithNameBar)).hasASpanWithName("foo");
* @param name searched span name * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name * @since 1.0.0 */ public SpansAssert hasASpanWithName(String name) { isNotEmpty(); extractSpanWithName(name); return this; } /** * Verifies that there is a span with a given name and also given assertion is met. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasASpanWithName("foo", spanAssert -> spanAssert.isStarted());
     *
     * // assertions fail
     * assertThat(Collections.singletonList(notFinishedSpanWithNameFoo)).hasASpanWithName("foo", spanAssert -> spanAssert.isStarted());
* @param name searched span name * @param spanConsumer assertion to be executed for each span * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name * @throws AssertionError if the span assertion is not met * @since 1.0.0 */ @SuppressWarnings("rawtypes") public SpansAssert hasASpanWithName(String name, Consumer spanConsumer) { isNotEmpty(); atLeastOneSpanPassesTheAssertion(name, spanConsumer); return this; } @SuppressWarnings("rawtypes") private void atLeastOneSpanPassesTheAssertion(String name, Consumer spanConsumer) { FinishedSpan finishedSpan = this.actual.stream().filter(f -> name.equals(f.getName())).filter(f -> { try { spanConsumer.accept(SpanAssert.assertThat(f)); return true; } catch (AssertionError e) { return false; } }).findFirst().orElse(null); if (finishedSpan == null) { failWithMessage( "Not a single span with name <%s> was found or has passed the assertion. Found following spans %s", name, spansAsString()); } } /** * Verifies that there is a span with a given name (ignoring case) and also given * assertion is met. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasASpanWithNameIgnoreCase("FoO", spanAssert -> spanAssert.isStarted());
     *
     * // assertions fail
     * assertThat(Collections.singletonList(notFinishedSpanWithNameFoo)).hasASpanWithNameIgnoreCase("FoO", spanAssert -> spanAssert.isStarted());
* @param name searched span name * @param spanConsumer assertion to be executed for each span * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name * @throws AssertionError if the span assertion is not met * @since 1.0.0 */ @SuppressWarnings("rawtypes") public SpansAssert hasASpanWithNameIgnoreCase(String name, Consumer spanConsumer) { isNotEmpty(); atLeastOneSpanPassesTheAssertionIgnoreCase(name, spanConsumer); return this; } @SuppressWarnings("rawtypes") private void atLeastOneSpanPassesTheAssertionIgnoreCase(String name, Consumer spanConsumer) { FinishedSpan finishedSpan = this.actual.stream().filter(f -> name.equalsIgnoreCase(f.getName())).filter(f -> { try { spanConsumer.accept(SpanAssert.assertThat(f)); return true; } catch (AssertionError e) { return false; } }).findFirst().orElse(null); if (finishedSpan == null) { failWithMessage( "Not a single span with name <%s> was found (ignoring case) or has passed the assertion. Found following spans %s", name, spansAsString()); } } private FinishedSpan extractSpanWithName(String name) { return this.actual.stream().filter(f -> name.equals(f.getName())).findFirst().orElseThrow(() -> { failWithMessage( "There should be at least one span with name <%s> but found none. Found following spans \n%s", name, spansAsString()); return new AssertionError(); }); } private FinishedSpan extractSpanWithNameIgnoreCase(String name) { return this.actual.stream().filter(f -> name.equalsIgnoreCase(f.getName())).findFirst().orElseThrow(() -> { failWithMessage( "There should be at least one span with name (ignore case) <%s> but found none. Found following spans \n%s", name, spansAsString()); return new AssertionError(); }); } /** * Verifies that there is a span with a given name (ignoring case). *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasASpanWithNameIgnoreCase("FoO");
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithNameBar)).hasASpanWithNameIgnoreCase("FoO");
* @param name searched span name * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name (ignoring case) * @since 1.0.0 */ public SpansAssert hasASpanWithNameIgnoreCase(String name) { isNotEmpty(); this.actual.stream().filter(f -> name.equalsIgnoreCase(f.getName())).findFirst().orElseThrow(() -> { failWithMessage( "There should be at least one span with name (ignoring case) <%s> but found none. Found following spans \n%s", name, spansAsString()); return new AssertionError(); }); return this; } /** * Provides verification for all spans having the given name. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).forAllSpansWithNameEqualTo("foo", spanAssert -> spanAssert.isStarted());
     *
     * // assertions fail
     * assertThat(Collections.singletonList(nonStartedSpanWithNameFoo)).forAllSpansWithNameEqualTo("foo", spanAssert -> spanAssert.isStarted());
* @param name searched span name * @param spanConsumer assertion to be executed for each span * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name * @throws AssertionError if there is a span with the given name but the additional * assertion is not successful * @since 1.0.0 */ @SuppressWarnings("rawtypes") public SpansAssert forAllSpansWithNameEqualTo(String name, Consumer spanConsumer) { isNotEmpty(); hasASpanWithName(name); this.actual.stream() .filter(f -> name.equals(f.getName())) .forEach(f -> spanConsumer.accept(SpanAssert.then(f))); return this; } /** * Provides verification for all spans having the given name (ignoring case). *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).forAllSpansWithNameEqualTo("foo", spanAssert -> spanAssert.isStarted());
     *
     * // assertions fail
     * assertThat(Collections.singletonList(nonStartedSpanWithNameFoo)).forAllSpansWithNameEqualTo("foo", spanAssert -> spanAssert.isStarted());
* @param name searched span name (ignoring case) * @param spanConsumer assertion to be executed for each span * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name (ignoring case) * @throws AssertionError if there is a span with the given name (ignoring case) but * the additional assertion is not successful * @since 1.0.0 */ @SuppressWarnings("rawtypes") public SpansAssert forAllSpansWithNameEqualToIgnoreCase(String name, Consumer spanConsumer) { isNotEmpty(); hasASpanWithNameIgnoreCase(name); this.actual.stream() .filter(f -> name.equalsIgnoreCase(f.getName())) .forEach(f -> spanConsumer.accept(SpanAssert.then(f))); return this; } /** * Provides verification for the first span having the given name. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).assertThatASpanWithNameEqualTo("foo").isStarted();
     *
     * // assertions fail
     * assertThat(Collections.singletonList(notStartedSpanWithNameFoo)).assertThatASpanWithNameEqualTo("foo").isStarted();
* @param name searched span name * @return {@link SpansAssertReturningAssert} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name * @since 1.0.0 */ public SpansAssertReturningAssert assertThatASpanWithNameEqualTo(String name) { isNotEmpty(); FinishedSpan span = extractSpanWithName(name); return new SpansAssertReturningAssert(this, span); } /** * Provides verification for the first span having the given name. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).thenASpanWithNameEqualTo("foo").isStarted();
     *
     * // assertions fail
     * assertThat(Collections.singletonList(notStartedSpanWithNameFoo)).thenASpanWithNameEqualTo("foo").isStarted();
* @param name searched span name * @return {@link SpansAssertReturningAssert} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name * @since 1.0.0 */ public SpansAssertReturningAssert thenASpanWithNameEqualTo(String name) { return assertThatASpanWithNameEqualTo(name); } /** * Provides verification for the first span having the given name (ignoring case). *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).assertThatASpanWithNameEqualToIgnoreCase("FoO").isStarted();
     *
     * // assertions fail
     * assertThat(Collections.singletonList(notStartedSpanWithNameFoo)).assertThatASpanWithNameEqualToIgnoreCase("FoO").isStarted();
* @param name searched span name (ignoring case) * @return {@link SpansAssertReturningAssert} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name (ignoring case) * @since 1.0.0 */ public SpansAssertReturningAssert assertThatASpanWithNameEqualToIgnoreCase(String name) { isNotEmpty(); FinishedSpan span = extractSpanWithNameIgnoreCase(name); return new SpansAssertReturningAssert(this, span); } /** * Provides verification for the first span having the given name (ignoring case). *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).thenASpanWithNameEqualToIgnoreCase("FoO").isStarted();
     *
     * // assertions fail
     * assertThat(Collections.singletonList(notStartedSpanWithNameFoo)).thenASpanWithNameEqualToIgnoreCase("FoO").isStarted();
* @param name searched span name (ignoring case) * @return {@link SpansAssertReturningAssert} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with the given name (ignoring case) * @since 1.0.0 */ public SpansAssertReturningAssert thenASpanWithNameEqualToIgnoreCase(String name) { return assertThatASpanWithNameEqualToIgnoreCase(name); } /** * Verifies that there is a proper number of spans. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpan)).hasNumberOfSpansEqualTo(1);
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpan)).hasNumberOfSpansEqualTo(2);
* @param expectedNumberOfSpans expected number of spans * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if the number of spans is different from the desired one * @since 1.0.0 */ public SpansAssert hasNumberOfSpansEqualTo(int expectedNumberOfSpans) { isNotEmpty(); if (this.actual.size() != expectedNumberOfSpans) { failWithMessage("There should be <%s> spans but there were <%s>. Found following spans \n%s", expectedNumberOfSpans, this.actual.size(), spansAsString()); } return this; } /** * Verifies that there is a proper number of spans with the given name. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasNumberOfSpansWithNameEqualTo("foo", 1);
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasNumberOfSpansWithNameEqualTo("foo", 2);
* @param spanName span name * @param expectedNumberOfSpans expected number of spans with the given name * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if the number of properly named spans is different from the * desired one * @since 1.0.0 */ public SpansAssert hasNumberOfSpansWithNameEqualTo(String spanName, int expectedNumberOfSpans) { isNotEmpty(); long spansWithNameSize = this.actual.stream().filter(f -> spanName.equals(f.getName())).count(); if (spansWithNameSize != expectedNumberOfSpans) { failWithMessage("There should be <%s> spans with name <%s> but there were <%s>. Found following spans \n%s", expectedNumberOfSpans, spanName, spansWithNameSize, spansAsString()); } return this; } /** * Verifies that there is a proper number of spans with the given name (ignoring * case). *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasNumberOfSpansWithNameEqualToIgnoreCase(1);
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithNameFoo)).hasNumberOfSpansWithNameEqualToIgnoreCase(2);
* @param spanName span name * @param expectedNumberOfSpans expected number of spans with the given name (ignoring * case) * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if the number of properly named spans is different from the * desired one * @since 1.0.0 */ public SpansAssert hasNumberOfSpansWithNameEqualToIgnoreCase(String spanName, int expectedNumberOfSpans) { isNotEmpty(); long spansWithNameSize = this.actual.stream().filter(f -> spanName.equalsIgnoreCase(f.getName())).count(); if (spansWithNameSize != expectedNumberOfSpans) { failWithMessage( "There should be <%s> spans with name (ignoring case) <%s> but there were <%s>. Found following spans \n%s", expectedNumberOfSpans, spanName, spansWithNameSize, spansAsString()); } return this; } /** * Verifies that there is a span with a given remote service name. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithRemoteServiceNameFoo)).hasASpanWithRemoteServiceName("FoO");
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithRemoteServiceNameBar)).hasASpanWithRemoteServiceName("FoO");
* @param remoteServiceName searched remote service name * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with given remote service name * @since 1.0.0 */ public SpansAssert hasASpanWithRemoteServiceName(String remoteServiceName) { isNotEmpty(); this.actual.stream() .filter(f -> remoteServiceName.equals(f.getRemoteServiceName())) .findFirst() .orElseThrow(() -> { failWithMessage( "There should be at least one span with remote service name <%s> but found none. Found following spans \n%s", remoteServiceName, spansAsString()); return new AssertionError(); }); return this; } /** * Verifies that there is a span with a tag. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithTagFooBar)).hasASpanWithATag("foo", "bar");
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithTagBazBar)).hasASpanWithATag("foo", "bar");
* @param key expected tag key * @param value expected tag value * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with given tag key and value * @since 1.0.0 */ public SpansAssert hasASpanWithATag(String key, String value) { isNotEmpty(); this.actual.stream().filter(f -> { String tag = f.getTags().get(key); return value.equals(tag); }).findFirst().orElseThrow(() -> { failWithMessage( "There should be at least one span with tag key <%s> and value <%s> but found none. Found following spans \n%s", key, value, spansAsString()); return new AssertionError(); }); return this; } /** * Verifies that there is a span with a tag key. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithTagFooBar)).hasASpanWithATagKey("foo");
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithTagBazBar)).hasASpanWithATagKey("foo");
* @param key expected tag key * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with given tag key * @since 1.0.0 */ public SpansAssert hasASpanWithATagKey(String key) { isNotEmpty(); this.actual.stream().filter(f -> f.getTags().containsKey(key)).findFirst().orElseThrow(() -> { failWithMessage( "There should be at least one span with tag key <%s> but found none. Found following spans \n%s", key, spansAsString()); return new AssertionError(); }); return this; } /** * Verifies that there is a span with a tag. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithTagFooBar)).hasASpanWithATag(SomeKeyName.FOO, "bar");
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithTagBazBar)).hasASpanWithATag(SomeKeyName.FOO, "baz");
* @param key expected tag key * @param value expected tag value * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with given tag key * @since 1.0.0 */ public SpansAssert hasASpanWithATag(KeyName key, String value) { return hasASpanWithATag(key.asString(), value); } /** * Verifies that there is a span with a tag key. *

* Examples:

 // assertions succeed
     * assertThat(Collections.singletonList(finishedSpanWithTagFooBar)).hasASpanWithATagKey(SomeKeyName.FOO);
     *
     * // assertions fail
     * assertThat(Collections.singletonList(finishedSpanWithTagBazBar)).hasASpanWithATagKey(SomeKeyName.FOO);
* @param key expected tag key * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if there is no span with given tag key * @since 1.0.0 */ public SpansAssert hasASpanWithATagKey(KeyName key) { return hasASpanWithATagKey(key.asString()); } /** * Syntactic sugar that extends {@link SpanAssert} methods with an option to go back * to {@link SpansAssert}. * * @since 1.0.0 */ public static class SpansAssertReturningAssert extends SpanAssert { private final SpansAssert spansAssert; /** * Creates a new instance of {@link SpansAssertReturningAssert}. * @param spansAssert spans assertion * @param span span to assert */ public SpansAssertReturningAssert(SpansAssert spansAssert, FinishedSpan span) { super(span); this.spansAssert = spansAssert; } /** * Goes back to the {@link SpansAssert}. * @return {@link SpansAssert} assertion */ public SpansAssert backToSpans() { return this.spansAssert; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy