Skip to content

Commit

Permalink
feat: add Kafka functionality test with TestContainers
Browse files Browse the repository at this point in the history
Signed-off-by: SebastienDegodez <[email protected]>
  • Loading branch information
SebastienDegodez committed Dec 19, 2024
1 parent d56b906 commit e6a288c
Show file tree
Hide file tree
Showing 9 changed files with 778 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/Microcks.Testcontainers/Connection/KafkaConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Copyright The Microcks Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

namespace Microcks.Testcontainers.Connection;

public class KafkaConnection
{
public KafkaConnection(string bootstrapServers)
{
this.BootstrapServers = bootstrapServers;
}

public string BootstrapServers { get; }
}
102 changes: 102 additions & 0 deletions src/Microcks.Testcontainers/MicrocksAsyncMinionBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// Copyright The Microcks Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//


using DotNet.Testcontainers.Networks;
using Microcks.Testcontainers.Connection;

namespace Microcks.Testcontainers;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
public sealed class MicrocksAsyncMinionBuilder
: ContainerBuilder<MicrocksAsyncMinionBuilder, MicrocksAsyncMinionContainer, MicrocksAsyncMinionConfiguration>
{
public const int MicrocksAsyncMinionHttpPort = 8081;
private const string MicrocksAsyncMinionFullImageName = "quay.io/microcks/microcks-uber-async-minion";

private HashSet<string> extraProtocols = [];
private INetwork _network;

public MicrocksAsyncMinionBuilder(INetwork network)
: this(new MicrocksAsyncMinionConfiguration())
{
this._network = network;
DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

private MicrocksAsyncMinionBuilder(MicrocksAsyncMinionConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
DockerResourceConfiguration = resourceConfiguration;
}

protected override MicrocksAsyncMinionConfiguration DockerResourceConfiguration { get; }

public override MicrocksAsyncMinionContainer Build()
{
Validate();

return new MicrocksAsyncMinionContainer(DockerResourceConfiguration);
}

protected override MicrocksAsyncMinionBuilder Init()
{
return base.Init()
.WithImage(MicrocksAsyncMinionFullImageName)
.WithNetwork(this._network)
.WithNetworkAliases("microcks-async-minion")
.WithEnvironment("MICROCKS_HOST_PORT", "microcks:" + MicrocksBuilder.MicrocksHttpPort)
.WithExposedPort(MicrocksAsyncMinionHttpPort)
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged(".*Profile prod activated\\..*"));
}

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

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

/// <inheritdoc />
protected override MicrocksAsyncMinionBuilder Merge(MicrocksAsyncMinionConfiguration oldValue, MicrocksAsyncMinionConfiguration newValue)
{
return new MicrocksAsyncMinionBuilder(new MicrocksAsyncMinionConfiguration(oldValue, newValue));
}


/// <summary>
/// Configures the MicrocksAsyncMinionBuilder to use a Kafka connection.
/// </summary>
/// <param name="kafkaConnection">The Kafka connection details.</param>
/// <returns>The updated MicrocksAsyncMinionBuilder instance.</returns>
public MicrocksAsyncMinionBuilder WithKafkaConnection(KafkaConnection kafkaConnection)
{
extraProtocols.Add("KAFKA");
var environments = new Dictionary<string, string>
{
{ "ASYNC_PROTOCOLS", $",{string.Join(",", extraProtocols)}" },
{ "KAFKA_BOOTSTRAP_SERVER", kafkaConnection.BootstrapServers },
};

return Merge(DockerResourceConfiguration, new MicrocksAsyncMinionConfiguration(new ContainerConfiguration(environments: environments)));
}
}
67 changes: 67 additions & 0 deletions src/Microcks.Testcontainers/MicrocksAsyncMinionConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// Copyright The Microcks Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

namespace Microcks.Testcontainers;

/// <inheritdoc cref="ContainerConfiguration" />
public sealed class MicrocksAsyncMinionConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="MicrocksAsyncMinionConfiguration" /> class.
/// </summary>
/// <param name="config">The Microcks config.</param>
public MicrocksAsyncMinionConfiguration(object config = null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="MicrocksAsyncMinionConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public MicrocksAsyncMinionConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
: base(resourceConfiguration)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="MicrocksAsyncMinionConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public MicrocksAsyncMinionConfiguration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="MicrocksAsyncMinionConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public MicrocksAsyncMinionConfiguration(MicrocksAsyncMinionConfiguration resourceConfiguration)
: this(new MicrocksAsyncMinionConfiguration(), resourceConfiguration)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="MicrocksAsyncMinionConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public MicrocksAsyncMinionConfiguration(MicrocksAsyncMinionConfiguration oldValue, MicrocksAsyncMinionConfiguration newValue)
: base(oldValue, newValue)
{
}
}
44 changes: 44 additions & 0 deletions src/Microcks.Testcontainers/MicrocksAsyncMinionContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// Copyright The Microcks Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//


namespace Microcks.Testcontainers;

/// <inheritdoc cref="DockerContainer" />
public sealed class MicrocksAsyncMinionContainer : DockerContainer
{
private const string DESTINATION_PATTERN = "{0}-{1}-{2}";

public MicrocksAsyncMinionContainer(MicrocksAsyncMinionConfiguration configuration)
: base(configuration)
{
}


public string GetKafkaMockTopic(string service, string version, string operationName)
{
// operationName may start with SUBSCRIBE or PUBLISH.
if (operationName.Contains(" "))
{
operationName = operationName.Split(' ')[1];
}
return String.Format(DESTINATION_PATTERN,
service.Replace(" ", "").Replace("-", ""),
version,
operationName.Replace("/", "-"));
}
}
141 changes: 141 additions & 0 deletions src/Microcks.Testcontainers/MicrocksContainerEnsemble.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//
// Copyright The Microcks Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

using System.Threading.Tasks;
using DotNet.Testcontainers.Networks;
using Microcks.Testcontainers.Connection;

namespace Microcks.Testcontainers;

/// <inheritdoc cref="DockerContainer" />
public class MicrocksContainerEnsemble : IAsyncDisposable
{
private readonly MicrocksBuilder _microcksBuilder;

private MicrocksAsyncMinionBuilder _asyncMinionBuilder;

/// <summary>
/// Gets the Microcks asynchronous minion container.
/// </summary>
public MicrocksAsyncMinionContainer AsyncMinionContainer { get; private set; }

/// <summary>
/// Gets the Microcks container.
/// </summary>
public MicrocksContainer MicrocksContainer { get; private set; }

private INetwork _network;

private readonly string _microcksImage;

public MicrocksContainerEnsemble(string microcksImage)
: this(new NetworkBuilder().Build(), microcksImage)
{
}

public MicrocksContainerEnsemble(INetwork network, string microcksImage)
{
this._microcksImage = microcksImage;
this._network = network;

this._microcksBuilder = new MicrocksBuilder()
.WithNetwork(this._network)
.WithNetworkAliases("microcks")
.WithExposedPort(MicrocksBuilder.MicrocksHttpPort)
.WithExposedPort(MicrocksBuilder.MicrocksGrpcPort)
.WithImage(this._microcksImage)
.WithEnvironment("TEST_CALLBACK_URL", "http://microcks:" + MicrocksBuilder.MicrocksHttpPort)
.WithEnvironment("ASYNC_MINION_URL", "http://microcks-async-minion:" + MicrocksAsyncMinionBuilder.MicrocksAsyncMinionHttpPort);
}

public MicrocksContainerEnsemble WithMainArtifacts(params string[] mainArtifacts)
{
this._microcksBuilder.WithMainArtifacts(mainArtifacts);
return this;
}

/// <summary>
/// Configures the Microcks container ensemble to use the asynchronous feature.
/// </summary>
/// <returns>
/// The <see cref="MicrocksContainerEnsemble"/> instance with the asynchronous feature configured.
/// </returns>
/// <remarks>
/// This method modifies the Microcks image to use the asynchronous version by replacing "microcks-uber" with "microcks-uber-async".
/// If the image name ends with "-native", it removes the "-native" suffix.
/// It also sets up the asynchronous minion builder with the modified image, network, and network aliases.
/// </remarks>
public MicrocksContainerEnsemble WithAsyncFeature()
{
var image = this._microcksImage.Replace("microcks-uber", "microcks-uber-async-minion");
if (image.EndsWith("-native"))
{
image = image.Replace("-native", "");
}

this._asyncMinionBuilder = new MicrocksAsyncMinionBuilder(this._network)
.WithImage(image);

return this;
}

/// <summary>
/// Configures the Microcks container ensemble with a Kafka connection.
/// </summary>
/// <param name="kafkaConnection">The Kafka connection details.</param>
/// <returns>The updated <see cref="MicrocksContainerEnsemble"/> instance.</returns>
public MicrocksContainerEnsemble WithKafkaConnection(KafkaConnection kafkaConnection)
{
if (this._asyncMinionBuilder == null)
{
this.WithAsyncFeature();
}

this._asyncMinionBuilder = (_asyncMinionBuilder ?? throw new NullReferenceException("MicrocksAsyncMinionBuilder is null"))
.WithKafkaConnection(kafkaConnection);

return this;
}

public async Task StartAsync()
{
this.MicrocksContainer = this._microcksBuilder.Build();
await this.MicrocksContainer.StartAsync().ConfigureAwait(false);

if (this._asyncMinionBuilder != null)
{
this.AsyncMinionContainer = this._asyncMinionBuilder
.DependsOn(this.MicrocksContainer)
.Build();

await this.AsyncMinionContainer.StartAsync().ConfigureAwait(false);
}
}

public async ValueTask DisposeAsync()
{
if (this.AsyncMinionContainer != null)
{
await this.AsyncMinionContainer.DisposeAsync();
}

if (this.MicrocksContainer != null)
{
await this.MicrocksContainer.DisposeAsync();
}
}
}
Loading

0 comments on commit e6a288c

Please sign in to comment.