166 lines
7.3 KiB
C#
166 lines
7.3 KiB
C#
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
using IntegrationTests.Helpers;
|
|
using OpenTelemetry.Proto.Common.V1;
|
|
using OpenTelemetry.Proto.Trace.V1;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace IntegrationTests;
|
|
|
|
[Collection(RabbitMqCollection.Name)]
|
|
public class RabbitMqTests : TestHelper
|
|
{
|
|
// https://github.com/open-telemetry/semantic-conventions/blob/d515887174e20a3546e89df5cb5a306231e1424b/docs/messaging/rabbitmq.md
|
|
|
|
// Required messaging attributes set by the instrumentation
|
|
private const string MessagingSystemAttributeName = "messaging.system";
|
|
private const string MessagingOperationAttributeName = "messaging.operation";
|
|
private const string MessagingDestinationAttributeName = "messaging.destination.name";
|
|
|
|
// Required RabbitMQ attributes set by the instrumentation
|
|
private const string RabbitMqRoutingKeyAttributeName = "messaging.rabbitmq.destination.routing_key";
|
|
private const string RabbitMqDeliveryTagAttributeName = "messaging.rabbitmq.delivery_tag";
|
|
|
|
// Recommended messaging attributes set by the instrumentation
|
|
private const string MessagingBodySizeAttributeName = "messaging.message.body.size";
|
|
|
|
// Required network attributes set by the instrumentation
|
|
private const string ServerAddressAttributeName = "server.address";
|
|
private const string ServerPortAttributeName = "server.port";
|
|
|
|
// Recommended network attributes set by the instrumentation
|
|
private const string NetworkTypeAttributeName = "network.type";
|
|
private const string NetworkPeerAddressAttributeName = "network.peer.address";
|
|
private const string NetworkPeerPortAttributeName = "network.peer.port";
|
|
|
|
private readonly RabbitMqFixture _rabbitMq;
|
|
|
|
public RabbitMqTests(ITestOutputHelper output, RabbitMqFixture rabbitMq)
|
|
: base("RabbitMq", output)
|
|
{
|
|
_rabbitMq = rabbitMq;
|
|
}
|
|
|
|
[SkippableTheory]
|
|
[Trait("Category", "EndToEnd")]
|
|
[Trait("Containers", "Linux")]
|
|
[MemberData(nameof(LibraryVersion.RabbitMq), MemberType = typeof(LibraryVersion))]
|
|
public void SubmitsTraces(string packageVersion)
|
|
{
|
|
// Skip the test if fixture does not support current platform
|
|
_rabbitMq.SkipIfUnsupportedPlatform();
|
|
|
|
if (string.IsNullOrEmpty(packageVersion) || Version.Parse(packageVersion) >= new Version(7, 0, 0))
|
|
{
|
|
TestRabbitMq7Plus(packageVersion);
|
|
}
|
|
else
|
|
{
|
|
TestRabbitMqLegacy(packageVersion);
|
|
}
|
|
}
|
|
|
|
private static bool ValidatePropagation(ICollection<MockSpansCollector.Collected> collected)
|
|
{
|
|
var producerSpans = collected.Where(span => span.Span.Kind == Span.Types.SpanKind.Producer).ToList();
|
|
|
|
Assert.Equal(3, producerSpans.Count);
|
|
|
|
return producerSpans.All(span => VerifySingleMatchingConsumerSpan(span));
|
|
|
|
bool VerifySingleMatchingConsumerSpan(MockSpansCollector.Collected producerSpan)
|
|
{
|
|
return collected.Count(spans =>
|
|
spans.Span.Kind == Span.Types.SpanKind.Consumer &&
|
|
spans.Span.Links[0].TraceId == producerSpan.Span.TraceId &&
|
|
spans.Span.Links[0].SpanId == producerSpan.Span.SpanId) == 1;
|
|
}
|
|
}
|
|
|
|
private static bool ValidateBasicSpanAttributes(IReadOnlyCollection<KeyValue> attributes, string operationName)
|
|
{
|
|
var messagingSystem = attributes.Single(kv => kv.Key == MessagingSystemAttributeName).Value.StringValue;
|
|
var messagingOperation = attributes.Single(kv => kv.Key == MessagingOperationAttributeName).Value.StringValue;
|
|
var destinationName = attributes.Single(kv => kv.Key == MessagingDestinationAttributeName).Value.StringValue;
|
|
var routingKey = attributes.Single(kv => kv.Key == RabbitMqRoutingKeyAttributeName).Value.StringValue;
|
|
var bodySize = attributes.Single(kv => kv.Key == MessagingBodySizeAttributeName).Value.IntValue;
|
|
|
|
return messagingSystem == "rabbitmq" &&
|
|
messagingOperation == operationName &&
|
|
destinationName == "amq.default" &&
|
|
routingKey == "hello" &&
|
|
bodySize == 13;
|
|
}
|
|
|
|
private void TestRabbitMq7Plus(string packageVersion)
|
|
{
|
|
using var collector = new MockSpansCollector(Output);
|
|
SetExporter(collector);
|
|
collector.Expect("RabbitMQ.Client.Publisher");
|
|
collector.Expect("RabbitMQ.Client.Subscriber");
|
|
|
|
RunTestApplication(new()
|
|
{
|
|
Arguments = $"--rabbitmq {_rabbitMq.Port}",
|
|
PackageVersion = packageVersion
|
|
});
|
|
|
|
collector.AssertExpectations();
|
|
}
|
|
|
|
private void TestRabbitMqLegacy(string packageVersion)
|
|
{
|
|
using var collector = new MockSpansCollector(Output);
|
|
SetExporter(collector);
|
|
|
|
collector.Expect("OpenTelemetry.AutoInstrumentation.RabbitMq", span => ValidateProducerSpan(span));
|
|
collector.Expect("OpenTelemetry.AutoInstrumentation.RabbitMq", span => ValidateProducerSpan(span));
|
|
collector.Expect("OpenTelemetry.AutoInstrumentation.RabbitMq", span => ValidateProducerSpan(span));
|
|
collector.Expect("OpenTelemetry.AutoInstrumentation.RabbitMq", span => ValidateConsumerSpan(span, "receive"));
|
|
collector.Expect("OpenTelemetry.AutoInstrumentation.RabbitMq", span => ValidateConsumerSpan(span, "deliver"));
|
|
collector.Expect("OpenTelemetry.AutoInstrumentation.RabbitMq", span => ValidateConsumerSpan(span, "deliver"));
|
|
|
|
collector.ExpectCollected(collected => ValidatePropagation(collected));
|
|
|
|
EnableBytecodeInstrumentation();
|
|
RunTestApplication(new()
|
|
{
|
|
Arguments = $"--rabbitmq {_rabbitMq.Port}",
|
|
PackageVersion = packageVersion
|
|
});
|
|
|
|
collector.AssertExpectations();
|
|
}
|
|
|
|
private bool ValidateConsumerSpan(Span span, string operationName)
|
|
{
|
|
var deliveryTag = span.Attributes.SingleOrDefault(kv => kv.Key == RabbitMqDeliveryTagAttributeName)?.Value.StringValue;
|
|
return span.Kind == Span.Types.SpanKind.Consumer &&
|
|
span.Links.Count == 1 &&
|
|
ValidateBasicSpanAttributes(span.Attributes, operationName) &&
|
|
(operationName != "receive" || ValidateNetworkAttributes(span.Attributes)) &&
|
|
!string.IsNullOrEmpty(deliveryTag);
|
|
}
|
|
|
|
private bool ValidateProducerSpan(Span span)
|
|
{
|
|
return span.Kind == Span.Types.SpanKind.Producer && ValidateBasicSpanAttributes(span.Attributes, "publish") && ValidateNetworkAttributes(span.Attributes);
|
|
}
|
|
|
|
private bool ValidateNetworkAttributes(IReadOnlyCollection<KeyValue> spanAttributes)
|
|
{
|
|
var serverAddress = spanAttributes.Single(kv => kv.Key == ServerAddressAttributeName).Value.StringValue;
|
|
var serverPort = spanAttributes.Single(kv => kv.Key == ServerPortAttributeName).Value.IntValue;
|
|
var networkType = spanAttributes.Single(kv => kv.Key == NetworkTypeAttributeName).Value.StringValue;
|
|
var networkPeerAddress = spanAttributes.Single(kv => kv.Key == NetworkPeerAddressAttributeName).Value.StringValue;
|
|
var networkPeerPort = spanAttributes.Single(kv => kv.Key == NetworkPeerPortAttributeName).Value.IntValue;
|
|
|
|
return serverAddress == "localhost" &&
|
|
serverPort == _rabbitMq.Port &&
|
|
networkPeerAddress is "127.0.0.1" or "::1" &&
|
|
networkPeerPort == _rabbitMq.Port &&
|
|
networkType is "ipv4" or "ipv6";
|
|
}
|
|
}
|