Skip to content

Commit 06c3735

Browse files
authored
Run integration tests using RabbitMQ in Docker (#1054)
1 parent 37d5e2e commit 06c3735

31 files changed

+1701
-1
lines changed

Build/EasyNetQ.proj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<TestProjects Include="$(Source)\EasyNetQ.Hosepipe.Tests\EasyNetQ.Hosepipe.Tests.csproj" />
7171
<TestProjects Include="$(Source)\EasyNetQ.Scheduler.Tests\EasyNetQ.Scheduler.Tests.csproj" />
7272
<TestProjects Include="$(Source)\EasyNetQ.Scheduler.Mongo.Tests\EasyNetQ.Scheduler.Mongo.Tests.csproj" />
73+
<TestProjects Include="$(Source)\EasyNetQ.IntegrationTests\EasyNetQ.IntegrationTests.csproj" />
7374
</ItemGroup>
7475

7576
<Exec WorkingDirectory="%(TestProjects.RootDir)\%(TestProjects.Directory)"
@@ -85,8 +86,9 @@
8586
<ItemGroup>
8687
<ClientLibraries Include="$(Source)\EasyNetQ\EasyNetQ.csproj" />
8788
<ClientLibraries Include="$(Source)\EasyNetQ.DI.*\EasyNetQ.DI.*.csproj" Exclude="$(Source)\*Tests*\*.csproj" />
88-
89+
8990
<FilesToDelete Include="$(NuGetPackageDirectory)\*.nupkg" />
91+
<FilesToDelete Include="$(NuGetPackageDirectory)\*.snupkg" />
9092
</ItemGroup>
9193

9294
<MakeDir Directories="$(NuGetPackageDirectory)" Condition="!Exists('$(NuGetPackageDirectory)')" />
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.InteropServices;
6+
using EasyNetQ.Management.Client.Model;
7+
using Microsoft.Extensions.Configuration;
8+
9+
namespace EasyNetQ.IntegrationTests
10+
{
11+
internal sealed class Configuration
12+
{
13+
private static readonly IList<OSPlatform> OperatingSystems = new List<OSPlatform>
14+
{
15+
OSPlatform.Windows,
16+
OSPlatform.Linux,
17+
OSPlatform.OSX
18+
};
19+
20+
private static readonly Lazy<Configuration> LazyInstance = new Lazy<Configuration>(() => new Configuration());
21+
private readonly string dockerHttpApiUri;
22+
private readonly Dictionary<OSPlatform, IConfigurationSection> osSpecificSettings;
23+
private readonly int rabbitMqClientPort;
24+
private readonly string rabbitMqHostName;
25+
private readonly int rabbitMqManagementPort;
26+
private readonly string rabbitMqPassword;
27+
private readonly string rabbitMqUser;
28+
private readonly Vhost rabbitMqVirtualHost;
29+
private readonly string rabbitMqVirtualHostName;
30+
31+
private Configuration()
32+
{
33+
var settings = new ConfigurationBuilder()
34+
.SetBasePath(Directory.GetCurrentDirectory())
35+
.AddJsonFile("settings.json")
36+
.Build();
37+
dockerHttpApiUri = settings["dockerHttpApiUri"];
38+
rabbitMqHostName = settings["rabbitMQHostName"];
39+
rabbitMqClientPort = int.Parse(settings["rabbitMQClientPort"]);
40+
rabbitMqManagementPort = int.Parse(settings["rabbitMQManagementPort"]);
41+
rabbitMqVirtualHostName = settings["rabbitMQVirtualHost"];
42+
rabbitMqVirtualHost = new Vhost {Name = rabbitMqVirtualHostName, Tracing = false};
43+
rabbitMqUser = settings["rabbitMQUser"];
44+
rabbitMqPassword = settings["rabbitMQPassword"];
45+
osSpecificSettings = OperatingSystems
46+
.Select(x => new {Os = x, ConfigSection = settings.GetSection(x.ToString().ToLowerInvariant())})
47+
.ToDictionary(x => x.Os, x => x.ConfigSection);
48+
}
49+
50+
private static Configuration Instance => LazyInstance.Value;
51+
52+
public static string DockerHttpApiUri => Instance.dockerHttpApiUri;
53+
54+
public static string RabbitMqHostName => Instance.rabbitMqHostName;
55+
56+
public static int RabbitMqClientPort => Instance.rabbitMqClientPort;
57+
58+
public static int RabbitMqManagementPort => Instance.rabbitMqManagementPort;
59+
60+
public static string RabbitMqVirtualHostName => Instance.rabbitMqVirtualHostName;
61+
62+
public static Vhost RabbitMqVirtualHost => Instance.rabbitMqVirtualHost;
63+
64+
public static string RabbitMqUser => Instance.rabbitMqUser;
65+
66+
public static string RabbitMqPassword => Instance.rabbitMqPassword;
67+
68+
public static string RabbitMQDockerImageName(OSPlatform dockerEngineOsPlatform)
69+
{
70+
return Instance.osSpecificSettings[dockerEngineOsPlatform]["rabbitMQDockerImageName"];
71+
}
72+
73+
public static string RabbitMQDockerImageTag(OSPlatform dockerEngineOsPlatform)
74+
{
75+
return Instance.osSpecificSettings[dockerEngineOsPlatform]["rabbitMQDockerImageTag"];
76+
}
77+
}
78+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Docker.DotNet;
8+
using Docker.DotNet.Models;
9+
10+
namespace EasyNetQ.IntegrationTests
11+
{
12+
public class DockerProxy : IDisposable
13+
{
14+
private readonly DockerClient client;
15+
private readonly DockerClientConfiguration dockerConfiguration;
16+
17+
public DockerProxy(Uri uri)
18+
{
19+
dockerConfiguration = new DockerClientConfiguration(uri);
20+
client = dockerConfiguration.CreateClient();
21+
}
22+
23+
public void Dispose()
24+
{
25+
dockerConfiguration.Dispose();
26+
client.Dispose();
27+
}
28+
29+
public async Task<OSPlatform> GetDockerEngineOsAsync(CancellationToken token = default)
30+
{
31+
var response = await client.System.GetSystemInfoAsync(token).ConfigureAwait(false);
32+
return OSPlatform.Create(response.OSType.ToUpper());
33+
}
34+
35+
public async Task CreateNetworkAsync(string name, CancellationToken token = default)
36+
{
37+
var networksCreateParameters = new NetworksCreateParameters
38+
{
39+
Name = name
40+
};
41+
await client.Networks.CreateNetworkAsync(networksCreateParameters, token).ConfigureAwait(false);
42+
}
43+
44+
public async Task PullImageAsync(string image, string tag, CancellationToken token = default)
45+
{
46+
var createParameters = new ImagesCreateParameters
47+
{
48+
FromImage = image,
49+
Tag = tag
50+
};
51+
var progress = new Progress<JSONMessage>(jsonMessage => { });
52+
await client.Images.CreateImageAsync(createParameters, null, progress, token).ConfigureAwait(false);
53+
}
54+
55+
public async Task<string> CreateContainerAsync(string image, string name,
56+
IDictionary<string, ISet<string>> portMappings, string networkName = null, IList<string> envVars = null,
57+
CancellationToken token = default)
58+
{
59+
var createParameters = new CreateContainerParameters
60+
{
61+
Image = image,
62+
Env = envVars ?? Enumerable.Empty<string>().ToList(),
63+
Name = name,
64+
Hostname = name,
65+
HostConfig = new HostConfig
66+
{
67+
PortBindings = PortBindings(portMappings),
68+
NetworkMode = networkName
69+
},
70+
ExposedPorts = portMappings.ToDictionary(x => x.Key, x => new EmptyStruct())
71+
};
72+
var response = await client.Containers.CreateContainerAsync(createParameters, token).ConfigureAwait(false);
73+
return response.ID;
74+
}
75+
76+
public async Task StartContainerAsync(string id, CancellationToken token = default)
77+
{
78+
await client.Containers.StartContainerAsync(id, new ContainerStartParameters(), token)
79+
.ConfigureAwait(false);
80+
}
81+
82+
public async Task<string> GetContainerIpAsync(string id, CancellationToken token = default)
83+
{
84+
var response = await client.Containers.InspectContainerAsync(id, token).ConfigureAwait(false);
85+
var networks = response.NetworkSettings.Networks;
86+
return networks.Select(x => x.Value.IPAddress).First(x => !string.IsNullOrEmpty(x));
87+
}
88+
89+
public async Task StopContainerAsync(string name, CancellationToken token = default)
90+
{
91+
var ids = await FindContainerIdsAsync(name).ConfigureAwait(false);
92+
var stopTasks = ids.Select(x =>
93+
client.Containers.StopContainerAsync(x, new ContainerStopParameters(), token));
94+
await Task.WhenAll(stopTasks);
95+
}
96+
97+
public async Task RemoveContainerAsync(string name, CancellationToken token = default)
98+
{
99+
var ids = await FindContainerIdsAsync(name).ConfigureAwait(false);
100+
var containerRemoveParameters = new ContainerRemoveParameters {Force = true, RemoveVolumes = true};
101+
var removeTasks =
102+
ids.Select(x => client.Containers.RemoveContainerAsync(x, containerRemoveParameters, token));
103+
await Task.WhenAll(removeTasks);
104+
}
105+
106+
public async Task DeleteNetworkAsync(string name, CancellationToken token = default)
107+
{
108+
var ids = await FindNetworkIdsAsync(name).ConfigureAwait(false);
109+
var deleteTasks = ids.Select(x => client.Networks.DeleteNetworkAsync(x, token));
110+
await Task.WhenAll(deleteTasks);
111+
}
112+
113+
private static IDictionary<string, IList<PortBinding>> PortBindings(
114+
IDictionary<string, ISet<string>> portMappings)
115+
{
116+
return portMappings
117+
.Select(x => new {ContainerPort = x.Key, HostPorts = HostPorts(x.Value)})
118+
.ToDictionary(x => x.ContainerPort, x => (IList<PortBinding>) x.HostPorts);
119+
}
120+
121+
private static List<PortBinding> HostPorts(IEnumerable<string> hostPorts)
122+
{
123+
return hostPorts.Select(x => new PortBinding {HostPort = x}).ToList();
124+
}
125+
126+
private async Task<IEnumerable<string>> FindContainerIdsAsync(string name)
127+
{
128+
var containers = await client.Containers
129+
.ListContainersAsync(new ContainersListParameters {All = true, Filters = ListFilters(name)})
130+
.ConfigureAwait(false);
131+
return containers.Select(x => x.ID);
132+
}
133+
134+
private async Task<IEnumerable<string>> FindNetworkIdsAsync(string name)
135+
{
136+
var networks = await client.Networks
137+
.ListNetworksAsync(new NetworksListParameters {Filters = ListFilters(name)}).ConfigureAwait(false);
138+
return networks.Select(x => x.ID);
139+
}
140+
141+
private static Dictionary<string, IDictionary<string, bool>> ListFilters(string name)
142+
{
143+
return new Dictionary<string, IDictionary<string, bool>>
144+
{
145+
{"name", new Dictionary<string, bool> {{name, true}}}
146+
};
147+
}
148+
}
149+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.props))\build.props" />
3+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.targets))\build.targets" />
4+
<PropertyGroup>
5+
<Description>EasyNetQ.IntegrationTests</Description>
6+
<TargetFrameworks>netcoreapp3.1;</TargetFrameworks>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<ProjectReference Include="..\EasyNetQ\EasyNetQ.csproj" />
10+
</ItemGroup>
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
13+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
14+
<PackageReference Include="EasyNetQ.Management.Client" Version="1.3.0" />
15+
<PackageReference Include="FluentAssertions" Version="5.10.3" />
16+
<PackageReference Include="GitVersionTask" Version="5.0.1">
17+
<PrivateAssets>all</PrivateAssets>
18+
</PackageReference>
19+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
20+
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
21+
<PackageReference Include="NSubstitute" Version="4.2.1" />
22+
<PackageReference Include="RabbitMQ.Client" Version="[6.0.0,7.0.0)" />
23+
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
24+
<PackageReference Include="System.Net.Http" Version="4.3.4" />
25+
<PackageReference Include="xunit" Version="2.4.1" />
26+
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
27+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
28+
<PackageReference Include="docker.dotnet" Version="3.125.2" />
29+
</ItemGroup>
30+
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
31+
<Reference Include="System" />
32+
<Reference Include="System.Core" />
33+
<Reference Include="System.Net.Http" />
34+
<Reference Include="Microsoft.CSharp" />
35+
</ItemGroup>
36+
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
37+
<DefineConstants>$(DefineConstants);NET_CORE</DefineConstants>
38+
</PropertyGroup>
39+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net461' ">
40+
<DefineConstants>$(DefineConstants);NETFX</DefineConstants>
41+
</PropertyGroup>
42+
<ItemGroup>
43+
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
44+
</ItemGroup>
45+
<ItemGroup>
46+
<None Remove="settings.json" />
47+
<Content Include="settings.json">
48+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
49+
</Content>
50+
</ItemGroup>
51+
</Project>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using EasyNetQ.IntegrationTests.Utils;
6+
using FluentAssertions;
7+
using Xunit;
8+
9+
namespace EasyNetQ.IntegrationTests.PubSub
10+
{
11+
[Collection("RabbitMQ")]
12+
public class When_publish_and_subscribe_polymorphic : IDisposable
13+
{
14+
public When_publish_and_subscribe_polymorphic(RabbitMQFixture fixture)
15+
{
16+
bus = RabbitHutch.CreateBus($"host={fixture.Host};prefetchCount=1");
17+
}
18+
19+
public void Dispose()
20+
{
21+
bus.Dispose();
22+
}
23+
24+
private const int MessagesCount = 10;
25+
26+
private readonly IBus bus;
27+
28+
[Fact]
29+
public async Task Test()
30+
{
31+
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
32+
33+
var subscriptionId = Guid.NewGuid().ToString();
34+
35+
var bunniesSink = new MessagesSink(MessagesCount);
36+
var rabbitsSink = new MessagesSink(MessagesCount);
37+
var bunnies = MessagesFactories.Create(MessagesCount, i => new BunnyMessage(i));
38+
var rabbits = MessagesFactories.Create(MessagesCount, MessagesCount, i => new RabbitMessage(i));
39+
40+
using (bus.Subscribe<Message>(subscriptionId, x =>
41+
{
42+
switch (x)
43+
{
44+
case BunnyMessage _:
45+
bunniesSink.Receive(x);
46+
break;
47+
case RabbitMessage _:
48+
rabbitsSink.Receive(x);
49+
break;
50+
default:
51+
throw new ArgumentOutOfRangeException(nameof(x), x, null);
52+
}
53+
}))
54+
{
55+
await bus.PublishBatchAsync(bunnies.Concat(rabbits), timeoutCts.Token)
56+
.ConfigureAwait(false);
57+
58+
await Task.WhenAll(
59+
bunniesSink.WaitAllReceivedAsync(timeoutCts.Token),
60+
rabbitsSink.WaitAllReceivedAsync(timeoutCts.Token)
61+
).ConfigureAwait(false);
62+
63+
bunniesSink.ReceivedMessages.Should().Equal(bunnies);
64+
rabbitsSink.ReceivedMessages.Should().Equal(rabbits);
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)