Skip to content

Commit

Permalink
feat: Add PubSub module (#1005)
Browse files Browse the repository at this point in the history
Co-authored-by: Andre Hofmeister <[email protected]>
  • Loading branch information
dejandjenic and HofmeisterAn authored Oct 15, 2023
1 parent 44a1d3a commit db16f85
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Oracle", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PostgreSql", "src\Testcontainers.PostgreSql\Testcontainers.PostgreSql.csproj", "{8AB91636-9055-4900-A72A-7CFFACDFDBF0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PubSub", "src\Testcontainers.PubSub\Testcontainers.PubSub.csproj", "{E6642255-667D-476B-B584-089AA5E6C0B1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.RabbitMq", "src\Testcontainers.RabbitMq\Testcontainers.RabbitMq.csproj", "{A6D480BC-FDE8-4B92-A2A6-FF16BEE486AE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.RavenDb", "src\Testcontainers.RavenDb\Testcontainers.RavenDb.csproj", "{F6394475-D6F1-46E2-81BF-4BA78A40B878}"
Expand Down Expand Up @@ -131,6 +133,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Platform.Win
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PostgreSql.Tests", "tests\Testcontainers.PostgreSql.Tests\Testcontainers.PostgreSql.Tests.csproj", "{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PubSub.Tests", "tests\Testcontainers.PubSub.Tests\Testcontainers.PubSub.Tests.csproj", "{0F86BCE8-62E1-4BFC-AA84-63C7514C90AC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.RabbitMq.Tests", "tests\Testcontainers.RabbitMq.Tests\Testcontainers.RabbitMq.Tests.csproj", "{19564567-1736-4626-B406-17E4E02F18B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.RavenDb.Tests", "tests\Testcontainers.RavenDb.Tests\Testcontainers.RavenDb.Tests.csproj", "{D53726B6-5447-47E6-B881-A44EFF6E5534}"
Expand Down Expand Up @@ -252,6 +256,10 @@ Global
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Release|Any CPU.Build.0 = Release|Any CPU
{E6642255-667D-476B-B584-089AA5E6C0B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6642255-667D-476B-B584-089AA5E6C0B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6642255-667D-476B-B584-089AA5E6C0B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6642255-667D-476B-B584-089AA5E6C0B1}.Release|Any CPU.Build.0 = Release|Any CPU
{A6D480BC-FDE8-4B92-A2A6-FF16BEE486AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6D480BC-FDE8-4B92-A2A6-FF16BEE486AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6D480BC-FDE8-4B92-A2A6-FF16BEE486AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -392,6 +400,10 @@ Global
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}.Release|Any CPU.Build.0 = Release|Any CPU
{0F86BCE8-62E1-4BFC-AA84-63C7514C90AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F86BCE8-62E1-4BFC-AA84-63C7514C90AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F86BCE8-62E1-4BFC-AA84-63C7514C90AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F86BCE8-62E1-4BFC-AA84-63C7514C90AC}.Release|Any CPU.Build.0 = Release|Any CPU
{19564567-1736-4626-B406-17E4E02F18B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19564567-1736-4626-B406-17E4E02F18B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19564567-1736-4626-B406-17E4E02F18B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -450,6 +462,7 @@ Global
{ADC2372B-6FE0-421D-8277-BB628E8EFC22} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{596EAFC1-0496-495C-B382-D57415FA456A} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{8AB91636-9055-4900-A72A-7CFFACDFDBF0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{E6642255-667D-476B-B584-089AA5E6C0B1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{A6D480BC-FDE8-4B92-A2A6-FF16BEE486AE} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{F6394475-D6F1-46E2-81BF-4BA78A40B878} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{BFDA179A-40EB-4CEB-B8E9-0DF32C65E2C5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -485,6 +498,7 @@ Global
{DA1D7ADE-452C-4369-83CC-56289176EACD} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{3E55CBE8-AFE8-426D-9470-49D63CD1051C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{0F86BCE8-62E1-4BFC-AA84-63C7514C90AC} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{19564567-1736-4626-B406-17E4E02F18B2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{D53726B6-5447-47E6-B881-A44EFF6E5534} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{31EE94A0-E721-4073-B6F1-DD912D004DEF} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.PubSub/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
68 changes: 68 additions & 0 deletions src/Testcontainers.PubSub/PubSubBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
namespace Testcontainers.PubSub;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class PubSubBuilder : ContainerBuilder<PubSubBuilder, PubSubContainer, PubSubConfiguration>
{
public const string GoogleCloudCliImage = "gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators";

public const ushort PubSubPort = 8085;

/// <summary>
/// Initializes a new instance of the <see cref="PubSubBuilder" /> class.
/// </summary>
public PubSubBuilder()
: this(new PubSubConfiguration())
{
DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

/// <summary>
/// Initializes a new instance of the <see cref="PubSubBuilder" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
private PubSubBuilder(PubSubConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
DockerResourceConfiguration = resourceConfiguration;
}

/// <inheritdoc />
protected override PubSubConfiguration DockerResourceConfiguration { get; }

/// <inheritdoc />
public override PubSubContainer Build()
{
Validate();
return new PubSubContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
}

/// <inheritdoc />
protected override PubSubBuilder Init()
{
return base.Init()
.WithImage(GoogleCloudCliImage)
.WithPortBinding(PubSubPort, true)
.WithEntrypoint("gcloud")
.WithCommand("beta", "emulators", "pubsub", "start", "--host-port", "0.0.0.0:" + PubSubPort)
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("(?s).*started.*$"));
}

/// <inheritdoc />
protected override PubSubBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new PubSubConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override PubSubBuilder Clone(IContainerConfiguration resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new PubSubConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override PubSubBuilder Merge(PubSubConfiguration oldValue, PubSubConfiguration newValue)
{
return new PubSubBuilder(new PubSubConfiguration(oldValue, newValue));
}
}
53 changes: 53 additions & 0 deletions src/Testcontainers.PubSub/PubSubConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace Testcontainers.PubSub;

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class PubSubConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="PubSubConfiguration" /> class.
/// </summary>
public PubSubConfiguration()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="PubSubConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PubSubConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PubSubConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PubSubConfiguration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PubSubConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PubSubConfiguration(PubSubConfiguration resourceConfiguration)
: this(new PubSubConfiguration(), resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PubSubConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public PubSubConfiguration(PubSubConfiguration oldValue, PubSubConfiguration newValue)
: base(oldValue, newValue)
{
}
}
25 changes: 25 additions & 0 deletions src/Testcontainers.PubSub/PubSubContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Testcontainers.PubSub;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class PubSubContainer : DockerContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="PubSubContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
/// <param name="logger">The logger.</param>
public PubSubContainer(PubSubConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
}

/// <summary>
/// Gets the PubSub emulator endpoint.
/// </summary>
/// <returns>The PubSub emulator endpoint.</returns>
public string GetEmulatorEndpoint()
{
return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(PubSubBuilder.PubSubPort)).ToString();
}
}
13 changes: 13 additions & 0 deletions src/Testcontainers.PubSub/Testcontainers.PubSub.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions src/Testcontainers.PubSub/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
global using System;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
global using JetBrains.Annotations;
global using Microsoft.Extensions.Logging;
1 change: 1 addition & 0 deletions tests/Testcontainers.PubSub.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
70 changes: 70 additions & 0 deletions tests/Testcontainers.PubSub.Tests/PubSubContainerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
namespace Testcontainers.PubSub;

public sealed class PubSubContainerTests : IAsyncLifetime
{
private readonly PubSubContainer _pubSubContainer = new PubSubBuilder().Build();

public Task InitializeAsync()
{
return _pubSubContainer.StartAsync();
}

public Task DisposeAsync()
{
return _pubSubContainer.DisposeAsync().AsTask();
}

[Fact]
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
public async Task SubTopicReturnsPubMessage()
{
// Given
const string helloPubSub = "Hello, PubSub!";

const string projectId = "hello-pub-sub";

const string topicId = "hello-topic";

const string subscriptionId = "hello-subscription";

var topicName = new TopicName(projectId, topicId);

var subscriptionName = new SubscriptionName(projectId, subscriptionId);

var message = new PubsubMessage();
message.Data = ByteString.CopyFromUtf8(helloPubSub);

var publisherClientBuilder = new PublisherServiceApiClientBuilder();
publisherClientBuilder.Endpoint = _pubSubContainer.GetEmulatorEndpoint();
publisherClientBuilder.ChannelCredentials = ChannelCredentials.Insecure;

var subscriberClientBuilder = new SubscriberServiceApiClientBuilder();
subscriberClientBuilder.Endpoint = _pubSubContainer.GetEmulatorEndpoint();
subscriberClientBuilder.ChannelCredentials = ChannelCredentials.Insecure;

// When
var publisher = await publisherClientBuilder.BuildAsync()
.ConfigureAwait(false);

_ = await publisher.CreateTopicAsync(topicName)
.ConfigureAwait(false);

var subscriber = await subscriberClientBuilder.BuildAsync()
.ConfigureAwait(false);

_ = await subscriber.CreateSubscriptionAsync(subscriptionName, topicName, null, 60)
.ConfigureAwait(false);

_ = await publisher.PublishAsync(topicName, new[] { message })
.ConfigureAwait(false);

var response = await subscriber.PullAsync(subscriptionName, 1)
.ConfigureAwait(false);

await subscriber.AcknowledgeAsync(subscriptionName, response.ReceivedMessages.Select(receivedMessage => receivedMessage.AckId))
.ConfigureAwait(false);

// Then
Assert.Equal(helloPubSub, response.ReceivedMessages.Single().Message.Data.ToStringUtf8());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0"/>
<PackageReference Include="xunit" Version="2.5.0"/>
<PackageReference Include="Google.Cloud.PubSub.V1" Version="3.5.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers.PubSub/Testcontainers.PubSub.csproj"/>
<ProjectReference Include="$(SolutionDir)tests/Testcontainers.Commons/Testcontainers.Commons.csproj"/>
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions tests/Testcontainers.PubSub.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
global using System.Linq;
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Commons;
global using Google.Cloud.PubSub.V1;
global using Google.Protobuf;
global using Grpc.Core;
global using Xunit;

0 comments on commit db16f85

Please sign in to comment.