content: /*
   * Copyright The OpenTelemetry Authors
   * SPDX-License-Identifier: Apache-2.0
   */
  
  package io.opentelemetry.javaagent.instrumentation.spring.jms.v6_0;
  
  import static io.opentelemetry.api.common.AttributeKey.stringArrayKey;
  import static io.opentelemetry.api.trace.SpanKind.CONSUMER;
  import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
  import static io.opentelemetry.api.trace.SpanKind.PRODUCER;
  import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanKind;
  import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
  import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
  import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME;
  import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID;
  import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION;
  import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM;
  import static java.util.Collections.singletonList;
  import static org.assertj.core.api.Assertions.assertThat;
  
  import io.opentelemetry.sdk.trace.data.LinkData;
  import io.opentelemetry.sdk.trace.data.SpanData;
  import jakarta.jms.ConnectionFactory;
  import java.util.concurrent.CompletableFuture;
  import java.util.concurrent.ExecutionException;
  import java.util.concurrent.TimeUnit;
  import java.util.concurrent.TimeoutException;
  import java.util.concurrent.atomic.AtomicReference;
  import org.assertj.core.api.AbstractStringAssert;
  import org.junit.jupiter.params.ParameterizedTest;
  import org.junit.jupiter.params.provider.ValueSource;
  import org.springframework.boot.SpringApplication;
  import org.springframework.context.ConfigurableApplicationContext;
  import org.springframework.jms.core.JmsTemplate;
  
  @SuppressWarnings("deprecation") // using deprecated semconv
  class SpringJmsListenerTest extends AbstractSpringJmsListenerTest {
  
    @Override
    void assertSpringJmsListener() {
      AtomicReference<SpanData> producerSpan = new AtomicReference<>();
      testing.waitAndAssertSortedTraces(
          orderByRootSpanKind(INTERNAL, CONSUMER),
          trace -> {
            trace.hasSpansSatisfyingExactly(
                span -> span.hasName("parent").hasNoParent(),
                span ->
                    span.hasName("spring-jms-listener publish")
                        .hasKind(PRODUCER)
                        .hasParent(trace.getSpan(0))
                        .hasAttributesSatisfyingExactly(
                            equalTo(MESSAGING_SYSTEM, "jms"),
                            equalTo(MESSAGING_DESTINATION_NAME, "spring-jms-listener"),
                            equalTo(MESSAGING_OPERATION, "publish"),
                            satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank)));
  
            producerSpan.set(trace.getSpan(1));
          },
          trace ->
              trace.hasSpansSatisfyingExactly(
                  span ->
                      span.hasName("spring-jms-listener receive")
                          .hasKind(CONSUMER)
                          .hasNoParent()
                          .hasLinks(LinkData.create(producerSpan.get().getSpanContext()))
                          .hasAttributesSatisfyingExactly(
                              equalTo(MESSAGING_SYSTEM, "jms"),
                              equalTo(MESSAGING_DESTINATION_NAME, "spring-jms-listener"),
                              equalTo(MESSAGING_OPERATION, "receive"),
                              satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank)),
                  span ->
                      span.hasName("spring-jms-listener process")
                          .hasKind(CONSUMER)
                          .hasParent(trace.getSpan(0))
                          .hasLinks(LinkData.create(producerSpan.get().getSpanContext()))
                          .hasAttributesSatisfyingExactly(
                              equalTo(MESSAGING_SYSTEM, "jms"),
                              equalTo(MESSAGING_DESTINATION_NAME, "spring-jms-listener"),
                              equalTo(MESSAGING_OPERATION, "process"),
                              satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank)),
                  span -> span.hasName("consumer").hasParent(trace.getSpan(1))));
    }
  
    @ParameterizedTest
    @ValueSource(classes = {AnnotatedListenerConfig.class, ManualListenerConfig.class})
    @SuppressWarnings("unchecked")
    void shouldCaptureHeaders(Class<?> configClass)
        throws ExecutionException, InterruptedException, TimeoutException {
      // given
      SpringApplication app = new SpringApplication(configClass);
      app.setDefaultProperties(defaultConfig());
      ConfigurableApplicationContext applicationContext = app.run();
      cleanup.deferCleanup(applicationContext);
  
      JmsTemplate jmsTemplate = new JmsTemplate(applicationContext.getBean(ConnectionFactory.class));
      String message = "hello there";
  
      // when
      testing.runWithSpan(
          "parent",
          () ->
              jmsTemplate.convertAndSend(
                  "spring-jms-listener",
                  message,
                  jmsMessage -> {
                    jmsMessage.setStringProperty("test_message_header", "test");
                    jmsMessage.setIntProperty("test_message_int_header", 1234);
                    return jmsMessage;
                  }));
  
      // then
      CompletableFuture<String> receivedMessage =
          applicationContext.getBean("receivedMessage", CompletableFuture.class);
      assertThat(receivedMessage.get(10, TimeUnit.SECONDS)).isEqualTo(message);
  
      testing.waitAndAssertSortedTraces(
          orderByRootSpanKind(INTERNAL, CONSUMER),
          trace ->
              trace.hasSpansSatisfyingExactly(
                  span -> span.hasName("parent").hasNoParent(),
                  span ->
                      span.hasName("spring-jms-listener publish")
                          .hasKind(PRODUCER)
                          .hasParent(trace.getSpan(0))
                          .hasAttributesSatisfyingExactly(
                              equalTo(MESSAGING_SYSTEM, "jms"),
                              equalTo(MESSAGING_DESTINATION_NAME, "spring-jms-listener"),
                              equalTo(MESSAGING_OPERATION, "publish"),
                              satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank),
                              equalTo(
                                  stringArrayKey("messaging.header.test_message_header"),
                                  singletonList("test")),
                              equalTo(
                                  stringArrayKey("messaging.header.test_message_int_header"),
                                  singletonList("1234")))),
          trace ->
              trace.hasSpansSatisfyingExactly(
                  span ->
                      span.hasName("spring-jms-listener receive")
                          .hasKind(CONSUMER)
                          .hasNoParent()
                          .hasAttributesSatisfyingExactly(
                              equalTo(MESSAGING_SYSTEM, "jms"),
                              equalTo(MESSAGING_DESTINATION_NAME, "spring-jms-listener"),
                              equalTo(MESSAGING_OPERATION, "receive"),
                              satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank),
                              equalTo(
                                  stringArrayKey("messaging.header.test_message_header"),
                                  singletonList("test")),
                              equalTo(
                                  stringArrayKey("messaging.header.test_message_int_header"),
                                  singletonList("1234"))),
                  span ->
                      span.hasName("spring-jms-listener process")
                          .hasKind(CONSUMER)
                          .hasParent(trace.getSpan(0))
                          .hasAttributesSatisfyingExactly(
                              equalTo(MESSAGING_SYSTEM, "jms"),
                              equalTo(MESSAGING_DESTINATION_NAME, "spring-jms-listener"),
                              equalTo(MESSAGING_OPERATION, "process"),
                              satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank),
                              equalTo(
                                  stringArrayKey("messaging.header.test_message_header"),
                                  singletonList("test")),
                              equalTo(
                                  stringArrayKey("messaging.header.test_message_int_header"),
                                  singletonList("1234"))),
                  span -> span.hasName("consumer").hasParent(trace.getSpan(1))));
    }
  }
  