Skip to content

Commit

Permalink
Add integration tests for MassTransit (#2014)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
nr-ahemsath and chynesNR authored Oct 27, 2023
1 parent 88f48b1 commit 760f41c
Show file tree
Hide file tree
Showing 12 changed files with 374 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ SPDX-License-Identifier: Apache-2.0
<match assemblyName="MassTransit" className="MassTransit.Configuration.TransportRegistrationBusFactory`1" minVersion="8.0.0">
<exactMethodMatcher methodName="CreateBus" />
</match>
<match assemblyName="MassTransit" className="MassTransit.BusFactoryExtensions" minVersion="8.0.0">
<exactMethodMatcher methodName="Build" parameters="MassTransit.IBusFactory,MassTransit.Configuration.IBusConfiguration,System.Collections.Generic.IEnumerable`1[MassTransit.ValidationResult]" />
</match>
</tracerFactory>

</instrumentation>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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<IBusFactoryConfigurator>(0);

var spec = new NewRelicPipeSpecification(agent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,18 @@ SPDX-License-Identifier: Apache-2.0

<tracerFactory name="TransportConfigLegacyWrapper">

<match assemblyName="MassTransit" className="MassTransit.Registration.TransportRegistrationBusFactory" maxVersion="7.99.0">
<!--This classname works for MassTransit v7.0.0-->
<match assemblyName="MassTransit" className="MassTransit.Registration.TransportRegistrationBusFactory" maxVersion="8.0.0">
<exactMethodMatcher methodName="CreateBus" />
</match>
<!--This classname works for MassTransit v7.3.1-->
<match assemblyName="MassTransit" className="MassTransit.Registration.TransportRegistrationBusFactory`1" maxVersion="8.0.0">
<exactMethodMatcher methodName="CreateBus" />
</match>

<match assemblyName="MassTransit" className="MassTransit.BusFactoryExtensions" maxVersion="8.0.0">
<exactMethodMatcher methodName="Build" parameters="MassTransit.IBusFactory,MassTransit.Configuration.IBusConfiguration,System.Collections.Generic.IEnumerable`1[GreenPipes.ValidationResult]" />
</match>
</tracerFactory>

</instrumentation>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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<IBusFactoryConfigurator>(0);

var spec = new NewRelicPipeSpecification(agent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,9 @@ public static Metric TryFindMetric(ExpectedMetric expectedMetric, IEnumerable<Me
continue;
if (!expectedMetric.IsRegexName && expectedMetric.metricName != actualMetric.MetricSpec.Name)
continue;
if (expectedMetric.metricScope != actualMetric.MetricSpec.Scope)
if (expectedMetric.IsRegexScope && !Regex.IsMatch(actualMetric.MetricSpec.Scope ?? string.Empty, expectedMetric.metricScope))
continue;
if (!expectedMetric.IsRegexScope && expectedMetric.metricScope != actualMetric.MetricSpec.Scope)
continue;

return actualMetric;
Expand All @@ -587,7 +589,8 @@ public static List<Metric> 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;
Expand Down Expand Up @@ -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} }}";
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<TFixture> : NewRelicIntegrationTest<TFixture>
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<Assertions.ExpectedMetric>
{
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<ConsoleDynamicMethodFixtureFW462>
{
public MassTransitTests_StartHost_FW462(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output)
: base(fixture, output, false)
{
}
}
[NetFrameworkTest]
public class MassTransitTests_StartHost_FWLatest : MassTransitTestsBase<ConsoleDynamicMethodFixtureFWLatest>
{
public MassTransitTests_StartHost_FWLatest(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output)
: base(fixture, output, false)
{
}
}
[NetCoreTest]
public class MassTransitTests_StartHost_Core60 : MassTransitTestsBase<ConsoleDynamicMethodFixtureCore60>
{
public MassTransitTests_StartHost_Core60(ConsoleDynamicMethodFixtureCore60 fixture, ITestOutputHelper output)
: base(fixture, output, false)
{
}
}
[NetCoreTest]
public class MassTransitTests_StartHost_CoreLatest : MassTransitTestsBase<ConsoleDynamicMethodFixtureCoreLatest>
{
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<ConsoleDynamicMethodFixtureFW462>
{
public MassTransitTests_StartBus_FW462(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output)
: base(fixture, output, true)
{
}
}
[NetFrameworkTest]
public class MassTransitTests_StartBus_FWLatest : MassTransitTestsBase<ConsoleDynamicMethodFixtureFWLatest>
{
public MassTransitTests_StartBus_FWLatest(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output)
: base(fixture, output, true)
{
}
}
[NetCoreTest]
public class MassTransitTests_StartBus_Core60 : MassTransitTestsBase<ConsoleDynamicMethodFixtureCore60>
{
public MassTransitTests_StartBus_Core60(ConsoleDynamicMethodFixtureCore60 fixture, ITestOutputHelper output)
: base(fixture, output, true)
{
}
}
[NetCoreTest]
public class MassTransitTests_StartBus_CoreLatest : MassTransitTestsBase<ConsoleDynamicMethodFixtureCoreLatest>
{
public MassTransitTests_StartBus_CoreLatest(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output)
: base(fixture, output, true)
{
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,35 @@
<Reference Include="System.Messaging" Condition="'$(TargetFramework)' == 'net481'" />
</ItemGroup>

<!--MassTransit-->
<ItemGroup>
<PackageReference Include="MassTransit" Version="7.3.1" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="MassTransit" Version="7.3.1" Condition="'$(TargetFramework)' == 'net471'" />
<PackageReference Include="MassTransit" Version="7.3.1" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="MassTransit" Version="8.1.1" Condition="'$(TargetFramework)' == 'net481'" />
<PackageReference Include="MassTransit" Version="7.3.1" Condition="'$(TargetFramework)' == 'net6.0'" />
<PackageReference Include="MassTransit" Version="8.1.1" Condition="'$(TargetFramework)' == 'net7.0'" />

<PackageReference Include="MassTransit.AspNetCore" Version="7.3.1" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="MassTransit.AspNetCore" Version="7.3.1" Condition="'$(TargetFramework)' == 'net471'" />
<PackageReference Include="MassTransit.AspNetCore" Version="7.3.1" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="MassTransit.AspNetCore" Version="7.3.1" Condition="'$(TargetFramework)' == 'net6.0'" />

<PackageReference Include="MassTransit.Extensions.DependencyInjection" Version="7.3.1" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="MassTransit.Extensions.DependencyInjection" Version="7.3.1" Condition="'$(TargetFramework)' == 'net471'" />
<PackageReference Include="MassTransit.Extensions.DependencyInjection" Version="7.3.1" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="MassTransit.Extensions.DependencyInjection" Version="7.3.1" Condition="'$(TargetFramework)' == 'net6.0'" />

<!--These references are needed for the MassTransit exerciser. They need to use a version this old because they have
a dependency on Microsoft.Extensions.Logging for the same version or greater, and we have references to old versions
of MEL for testing MEL instrumentation.-->
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" Condition="'$(TargetFramework)' == 'net481'" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" Condition="'$(TargetFramework)' == 'net471'" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />
</ItemGroup>

<!-- RestSharp -->
<ItemGroup>
<!-- This version is used to test against our minimum supported version called out in docs, and
Expand Down
Loading

0 comments on commit 760f41c

Please sign in to comment.