From 760f41c2aff5d4646096ccc677dcb0b8dc5fe3f0 Mon Sep 17 00:00:00 2001
From: Alex Hemsath <57361211+nr-ahemsath@users.noreply.github.com>
Date: Fri, 27 Oct 2023 09:30:42 -0700
Subject: [PATCH] Add integration tests for MassTransit (#2014)
* Initial exerciser implementation
* Cleanup
* Build works for all TFMS
* Add legacy (7.x) exerciser
Also cleanup, and rename start/stop methods
* Unify v7/v8 exercisers
Also pass in queue name as a parameter to StartBus to help with test assertions
* Implement send
* Adding alternate instrumentation point and first pass at tests (#2001)
* Checkpoint trying to get hosted service model working for v7
* Message consume is working in v7!
* Add instrumentation project readmes
* Added regex scope, tests are passing
* Test send as well as publish; cleanup
* Expanded tests, updated instrumentation for v7
* Make regex metric name assertions more specific
---------
Co-authored-by: Chris Hynes <111462425+chynesNR@users.noreply.github.com>
---
.../Wrapper/MassTransit/Instrumentation.xml | 3 +
.../Providers/Wrapper/MassTransit/README.md | 5 +
.../MassTransit/TransportConfigWrapper.cs | 2 +
.../MassTransitLegacy/Instrumentation.xml | 11 +-
.../Wrapper/MassTransitLegacy/README.md | 5 +
.../TransportConfigLegacyWrapper.cs | 2 +
.../IntegrationTestHelpers/Assertions.cs | 10 +-
.../MassTransit/MassTransitTests.cs | 161 ++++++++++++++++++
.../MultiFunctionApplicationHelpers.csproj | 29 ++++
.../MassTransit/MassTransitExerciser.cs | 123 +++++++++++++
.../MassTransit/Message.cs | 10 ++
.../MassTransit/MessageConsumer.cs | 17 ++
12 files changed, 374 insertions(+), 4 deletions(-)
create mode 100644 src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/README.md
create mode 100644 src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/README.md
create mode 100644 tests/Agent/IntegrationTests/IntegrationTests/MassTransit/MassTransitTests.cs
create mode 100644 tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/MassTransit/MassTransitExerciser.cs
create mode 100644 tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/MassTransit/Message.cs
create mode 100644 tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/MassTransit/MessageConsumer.cs
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml
index ff57986f1d..c4568da3ca 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml
@@ -12,6 +12,9 @@ SPDX-License-Identifier: Apache-2.0
+
+
+
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/README.md b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/README.md
new file mode 100644
index 0000000000..ced3c1a943
--- /dev/null
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/README.md
@@ -0,0 +1,5 @@
+This is the wrapper and instrumentation project for version 8.0.0 and higher of [MassTransit](https://masstransit.io/).
+
+The code in this project is nearly identical to that in the `MassTransitLegacy` project, which is for MassTransit v7. However, different MassTransit types are referenced
+in the two projects. The team decided at the time this instrumentation was introduced that eliminating the duplicate code would make things far less readable,
+and that code duplication was the lesser of two evils.
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs
index 34ffa7252b..3c65cffe08 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs
@@ -22,6 +22,8 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
{
// This will be run for each bus. Each bus gets one transport.
// We can support more than one transport with this setup.
+ // Note that there are two instrumentation points that can get us here, and the first parameter is different for both,
+ // but they both implement IBusFactoryConfigurator, which is what we need
var configurator = instrumentedMethodCall.MethodCall.MethodArguments.ExtractNotNullAs(0);
var spec = new NewRelicPipeSpecification(agent);
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml
index f3ebecb3ec..cf00b0a35c 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml
@@ -9,9 +9,18 @@ SPDX-License-Identifier: Apache-2.0
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/README.md b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/README.md
new file mode 100644
index 0000000000..96a3b02c57
--- /dev/null
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/README.md
@@ -0,0 +1,5 @@
+This is the wrapper and instrumentation project for version 7.x of [MassTransit](https://masstransit.io/).
+
+The code in this project is nearly identical to that in the `MassTransit` project, which is for MassTransit v8. However, different MassTransit types are referenced
+in the two projects. The team decided at the time this instrumentation was introduced that eliminating the duplicate code would make things far less readable,
+and that code duplication was the lesser of two evils.
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs
index df42f021ce..845d574fa3 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs
@@ -22,6 +22,8 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
{
// This will be run for each bus. Each bus gets one transport.
// We can support more than one transport with this setup.
+ // Note that there are two instrumentation points that can get us here, and the first parameter is different for both,
+ // but they both implement IBusFactoryConfigurator, which is what we need
var configurator = instrumentedMethodCall.MethodCall.MethodArguments.ExtractNotNullAs(0);
var spec = new NewRelicPipeSpecification(agent);
diff --git a/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs b/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs
index fb8adc6963..0b3496cc2b 100644
--- a/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs
+++ b/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs
@@ -573,7 +573,9 @@ public static Metric TryFindMetric(ExpectedMetric expectedMetric, IEnumerable TryFindMetrics(ExpectedMetric expectedMetric, IEnumer
var foundMetrics = actualMetrics
.Where(actualMetric => (expectedMetric.IsRegexName && Regex.IsMatch(actualMetric.MetricSpec.Name, expectedMetric.metricName)) ||
(!expectedMetric.IsRegexName && expectedMetric.metricName == actualMetric.MetricSpec.Name))
- .Where(actualMetric => expectedMetric.metricScope == actualMetric.MetricSpec.Scope)
+ .Where(actualMetric => (expectedMetric.IsRegexScope && Regex.IsMatch(actualMetric.MetricSpec.Scope, expectedMetric.metricScope)) ||
+ (!expectedMetric.IsRegexScope && expectedMetric.metricScope == actualMetric.MetricSpec.Scope))
.ToList();
return foundMetrics;
@@ -1113,10 +1116,11 @@ public class ExpectedMetric
public decimal? callCount = null;
public decimal? CallCountAllHarvests = null;
public bool IsRegexName = false;
+ public bool IsRegexScope = false;
public override string ToString()
{
- return $"{{ metricName: {metricName} metricScope: {metricScope}, IsRegexName: {IsRegexName}, callCount: {callCount}, CallCountAllHarvests: {CallCountAllHarvests} }}";
+ return $"{{ metricName: {metricName} metricScope: {metricScope}, IsRegexName: {IsRegexName}, IsRegexScope: {IsRegexScope}, callCount: {callCount}, CallCountAllHarvests: {CallCountAllHarvests} }}";
}
}
diff --git a/tests/Agent/IntegrationTests/IntegrationTests/MassTransit/MassTransitTests.cs b/tests/Agent/IntegrationTests/IntegrationTests/MassTransit/MassTransitTests.cs
new file mode 100644
index 0000000000..1e1075b3b4
--- /dev/null
+++ b/tests/Agent/IntegrationTests/IntegrationTests/MassTransit/MassTransitTests.cs
@@ -0,0 +1,161 @@
+// Copyright 2020 New Relic, Inc. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using System.Collections.Generic;
+using System.Linq;
+using NewRelic.Agent.IntegrationTestHelpers;
+using Xunit;
+using Xunit.Abstractions;
+using System;
+using NewRelic.Agent.IntegrationTestHelpers.RemoteServiceFixtures;
+
+namespace NewRelic.Agent.IntegrationTests.MassTransit
+{
+ public abstract class MassTransitTestsBase : NewRelicIntegrationTest
+ where TFixture : ConsoleDynamicMethodFixture
+ {
+ private readonly TFixture _fixture;
+
+ public MassTransitTestsBase(TFixture fixture, ITestOutputHelper output, bool useStartBus) : base(fixture)
+ {
+ _fixture = fixture;
+ _fixture.SetTimeout(TimeSpan.FromMinutes(2));
+ _fixture.TestLogger = output;
+
+ if (useStartBus)
+ {
+ _fixture.AddCommand($"MassTransitExerciser StartBus");
+ }
+ else
+ {
+ _fixture.AddCommand("MassTransitExerciser StartHost");
+ }
+ _fixture.AddCommand("MassTransitExerciser Publish publishedMessageOne");
+ _fixture.AddCommand("MassTransitExerciser Publish publishedMessageTwo");
+ _fixture.AddCommand("MassTransitExerciser Send sentMessageOne");
+ _fixture.AddCommand("MassTransitExerciser Send sentMessageTwo");
+
+ if (useStartBus)
+ {
+ _fixture.AddCommand($"MassTransitExerciser StopBus");
+ }
+ else
+ {
+ _fixture.AddCommand("MassTransitExerciser StopHost");
+ }
+
+ _fixture.AddActions
+ (
+ setupConfiguration: () =>
+ {
+ var configModifier = new NewRelicConfigModifier(fixture.DestinationNewRelicConfigFilePath);
+ configModifier.ForceTransactionTraces();
+ configModifier.SetLogLevel("finest");
+ },
+ exerciseApplication: () =>
+ {
+ _fixture.AgentLog.WaitForLogLine(AgentLogBase.MetricDataLogLineRegex, TimeSpan.FromMinutes(2));
+ }
+ );
+
+ _fixture.Initialize();
+ }
+
+ [Fact]
+ public void Test()
+ {
+
+ var massTransitMetricNameRegexBase = @"MessageBroker\/MassTransit\/Queue\/";
+ var queueNameRegex = @"Named\/(.{26})"; // The auto-generated in-memory queue names have 26 chars
+ var massTransitProduceMetricNameRegex = massTransitMetricNameRegexBase + @"Produce\/" + queueNameRegex;
+ var massTransitConsumeMetricNameRegex = massTransitMetricNameRegexBase + @"Consume\/" + queueNameRegex;
+
+ var metrics = _fixture.AgentLog.GetMetrics().ToList();
+
+ var expectedMetrics = new List
+ {
+ new Assertions.ExpectedMetric { metricName = massTransitConsumeMetricNameRegex, callCount = 4, IsRegexName = true},
+ new Assertions.ExpectedMetric { metricName = massTransitProduceMetricNameRegex, callCount = 4, IsRegexName = true},
+
+ new Assertions.ExpectedMetric { metricName = massTransitConsumeMetricNameRegex, callCount = 4, IsRegexName = true, metricScope = @"OtherTransaction\/Message\/MassTransit\/Queue\/" + queueNameRegex, IsRegexScope = true},
+ new Assertions.ExpectedMetric { metricName = massTransitProduceMetricNameRegex, callCount = 2, IsRegexName = true, metricScope = "OtherTransaction/Custom/MultiFunctionApplicationHelpers.NetStandardLibraries.MassTransitExerciser/Publish"},
+ new Assertions.ExpectedMetric { metricName = massTransitProduceMetricNameRegex, callCount = 2, IsRegexName = true, metricScope = "OtherTransaction/Custom/MultiFunctionApplicationHelpers.NetStandardLibraries.MassTransitExerciser/Send"},
+ };
+
+ Assertions.MetricsExist(expectedMetrics, metrics);
+
+ var transactionEvent = _fixture.AgentLog.TryGetTransactionEvent($"OtherTransaction/Custom/MultiFunctionApplicationHelpers.NetStandardLibraries.MassTransitExerciser/Publish");
+
+ Assert.NotNull( transactionEvent );
+ }
+ }
+
+ // Tests using StartHost (hosted service configuration method)
+ [NetFrameworkTest]
+ public class MassTransitTests_StartHost_FW462 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_FW462(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+ [NetFrameworkTest]
+ public class MassTransitTests_StartHost_FWLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_FWLatest(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartHost_Core60 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_Core60(ConsoleDynamicMethodFixtureCore60 fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartHost_CoreLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_CoreLatest(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+
+ // Tests using StartBus (bus factory configuration method)
+ [NetFrameworkTest]
+ public class MassTransitTests_StartBus_FW462 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_FW462(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+ [NetFrameworkTest]
+ public class MassTransitTests_StartBus_FWLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_FWLatest(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartBus_Core60 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_Core60(ConsoleDynamicMethodFixtureCore60 fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartBus_CoreLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_CoreLatest(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+
+}
diff --git a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj
index f3564ea268..691dddac80 100644
--- a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj
+++ b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj
@@ -262,6 +262,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+