Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Microcks.Testcontainers/MicrocksBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public MicrocksBuilder WithMainRemoteArtifacts(params string[] urls)
_mainRemoteArtifacts = new List<RemoteArtifact>(urls.Length);
}
_mainRemoteArtifacts.AddRange(urls.Select(url => new RemoteArtifact(url, null)));

return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,56 +24,34 @@
using Microcks.Testcontainers.Model;
using Microsoft.Extensions.Logging;
using Testcontainers.Kafka;
using DotNet.Testcontainers.Networks;

namespace Microcks.Testcontainers.Tests.Async.Kafka;

[Collection(nameof(KafkaCollection))]
public sealed class MicrocksAsyncKafkaFunctionalityTest : IAsyncLifetime
public sealed class MicrocksAsyncKafkaFunctionalityTest
: IClassFixture<MicrocksAsyncKafkaFunctionalityTest.KafkaFixture>
{
/// <summary>
/// Image name for the Microcks container.
/// </summary>
private const string MicrocksImage = "quay.io/microcks/microcks-uber:1.10.0";

private MicrocksContainerEnsemble _microcksContainerEnsemble;
private readonly KafkaFixture _fixture;

private KafkaContainer _kafkaContainer;

public async ValueTask DisposeAsync()
{
await this._microcksContainerEnsemble.DisposeAsync();
await this._kafkaContainer.DisposeAsync();
}

public async ValueTask InitializeAsync()
public MicrocksAsyncKafkaFunctionalityTest(KafkaFixture fixture)
{
var network = new NetworkBuilder().Build();

this._kafkaContainer = new KafkaBuilder()
.WithImage("confluentinc/cp-kafka:7.8.0")
.WithNetwork(network)
.WithNetworkAliases("kafka")
.WithListener("kafka:19092")
.Build();

// Start the Kafka container
await this._kafkaContainer.StartAsync();

this._microcksContainerEnsemble = new MicrocksContainerEnsemble(network, MicrocksImage)
.WithMainArtifacts("pastry-orders-asyncapi.yml")
.WithKafkaConnection(new KafkaConnection($"kafka:19092"));

await this._microcksContainerEnsemble.StartAsync();
_fixture = fixture;
}

[Fact]
public void ShouldReceivedKafkaMessageWhenMessageIsEmitted()
{
const string expectedMessage = "{\"id\":\"4dab240d-7847-4e25-8ef3-1530687650c8\",\"customerId\":\"fe1088b3-9f30-4dc1-a93d-7b74f0a072b9\",\"status\":\"VALIDATED\",\"productQuantities\":[{\"quantity\":2,\"pastryName\":\"Croissant\"},{\"quantity\":1,\"pastryName\":\"Millefeuille\"}]}";
var kafkaTopic = this._microcksContainerEnsemble.AsyncMinionContainer
var kafkaTopic = _fixture.MicrocksContainerEnsemble.AsyncMinionContainer
.GetKafkaMockTopic("Pastry orders API", "0.1.0", "SUBSCRIBE pastry/orders");

var bootstrapServers = this._kafkaContainer.GetBootstrapAddress()
var bootstrapServers = _fixture.KafkaContainer.GetBootstrapAddress()
.Replace("PLAINTEXT://", "", StringComparison.OrdinalIgnoreCase);

// Initialize Kafka consumer to receive message
Expand Down Expand Up @@ -127,7 +105,7 @@ public async Task ShouldReturnsCorrectStatusContractWhenGoodMessageIsEmitted()
// Init Kafka producer to send a message
var producerConfig = new ProducerConfig
{
BootstrapServers = this._kafkaContainer.GetBootstrapAddress()
BootstrapServers = _fixture.KafkaContainer.GetBootstrapAddress()
.Replace("PLAINTEXT://", "", StringComparison.OrdinalIgnoreCase),
ClientId = $"test-client-{DateTime.Now.Ticks}",
};
Expand All @@ -137,15 +115,15 @@ public async Task ShouldReturnsCorrectStatusContractWhenGoodMessageIsEmitted()
.SetValueSerializer(Serializers.Utf8)
.SetErrorHandler((_, e) =>
{
this._kafkaContainer.Logger.LogError("Error: {Reason}", e.Reason);
_fixture.KafkaContainer.Logger.LogError("Error: {Reason}", e.Reason);
})
.SetLogHandler((_, logMessage) =>
{
this._kafkaContainer.Logger.LogInformation("{Name} sending {Message}", logMessage.Name, logMessage.Message);
_fixture.KafkaContainer.Logger.LogInformation("{Name} sending {Message}", logMessage.Name, logMessage.Message);
})
.Build();

var taskTestResult = this._microcksContainerEnsemble
var taskTestResult = _fixture.MicrocksContainerEnsemble
.MicrocksContainer
.TestEndpointAsync(testRequest, TestContext.Current.CancellationToken);

Expand Down Expand Up @@ -197,7 +175,7 @@ public async Task ShouldReturnsCorrectStatusContractWhenBadMessageIsEmitted()
// Init Kafka producer to send a message
var producerConfig = new ProducerConfig
{
BootstrapServers = this._kafkaContainer.GetBootstrapAddress()
BootstrapServers = _fixture.KafkaContainer.GetBootstrapAddress()
.Replace("PLAINTEXT://", "", StringComparison.OrdinalIgnoreCase),
ClientId = $"test-client-{DateTime.Now.Ticks}",
};
Expand All @@ -207,15 +185,15 @@ public async Task ShouldReturnsCorrectStatusContractWhenBadMessageIsEmitted()
.SetValueSerializer(Serializers.Utf8)
.SetErrorHandler((_, e) =>
{
this._kafkaContainer.Logger.LogError("Error: {Reason}", e.Reason);
_fixture.KafkaContainer.Logger.LogError("Error: {Reason}", e.Reason);
})
.SetLogHandler((_, logMessage) =>
{
this._kafkaContainer.Logger.LogInformation("{Name} sending {Message}", logMessage.Name, logMessage.Message);
_fixture.KafkaContainer.Logger.LogInformation("{Name} sending {Message}", logMessage.Name, logMessage.Message);
})
.Build();

var taskTestResult = this._microcksContainerEnsemble
var taskTestResult = _fixture.MicrocksContainerEnsemble
.MicrocksContainer
.TestEndpointAsync(testRequest, TestContext.Current.CancellationToken);
await Task.Delay(750, TestContext.Current.CancellationToken);
Expand Down Expand Up @@ -249,7 +227,7 @@ public async Task ShouldReturnsCorrectStatusContractWhenBadMessageIsEmitted()
Assert.Contains("object has missing required properties ([\"status\"]", testStepResults.First().Message);

// Retrieve event messages for the failing test case.
var events = await _microcksContainerEnsemble.MicrocksContainer
var events = await _fixture.MicrocksContainerEnsemble.MicrocksContainer
.GetEventMessagesForTestCaseAsync(testResult, "SUBSCRIBE pastry/orders", TestContext.Current.CancellationToken);
// We should have at least 4 events.
Assert.True(events.Count >= 4);
Expand All @@ -263,4 +241,41 @@ public async Task ShouldReturnsCorrectStatusContractWhenBadMessageIsEmitted()
});
}

}
// Inner fixture to share Kafka and Microcks ensemble between tests
public sealed class KafkaFixture : IAsyncDisposable
{
private const string MicrocksImage = "quay.io/microcks/microcks-uber:1.10.0";

public INetwork Network { get; }
public KafkaContainer KafkaContainer { get; }
public MicrocksContainerEnsemble MicrocksContainerEnsemble { get; }

public KafkaFixture()
{
Network = new NetworkBuilder().Build();

KafkaContainer = new KafkaBuilder()
.WithImage("confluentinc/cp-kafka:7.8.0")
.WithNetwork(Network)
.WithNetworkAliases("kafka")
.WithListener("kafka:19092")
.Build();

KafkaContainer.StartAsync().GetAwaiter().GetResult();

MicrocksContainerEnsemble = new MicrocksContainerEnsemble(Network, MicrocksImage)
.WithMainArtifacts("pastry-orders-asyncapi.yml")
.WithKafkaConnection(new KafkaConnection($"kafka:19092"));

MicrocksContainerEnsemble.StartAsync().GetAwaiter().GetResult();
}

public async ValueTask DisposeAsync()
{
await MicrocksContainerEnsemble.DisposeAsync();
await KafkaContainer.DisposeAsync();
await Network.DisposeAsync();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public async Task ShouldReturnsCorrectStatusContractWhenGoodMessageIsEmitted()
Assert.True(string.IsNullOrEmpty(testResult.TestCaseResults.First().TestStepResults.First().Message));
}

/// <summary>
/// <summary>
/// Test that verifies that the WaitForConditionAsync method throws a TaskCanceledException
/// when the specified timeout is reached.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// 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;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Networks;

namespace Microcks.Testcontainers.Tests.Fixtures;

public sealed class MicrocksContractTestingFixture : IAsyncLifetime
{
private static readonly string BAD_PASTRY_IMAGE = "quay.io/microcks/contract-testing-demo:01";
private static readonly string GOOD_PASTRY_IMAGE = "quay.io/microcks/contract-testing-demo:02";

public INetwork Network { get; }
public MicrocksContainer MicrocksContainer { get; }
public IContainer BadImpl { get; }
public IContainer GoodImpl { get; }

public MicrocksContractTestingFixture()
{
Network = new NetworkBuilder().Build();

MicrocksContainer = new MicrocksBuilder()
.WithNetwork(Network)
.Build();

BadImpl = new ContainerBuilder()
.WithImage(BAD_PASTRY_IMAGE)
.WithNetwork(Network)
.WithNetworkAliases("bad-impl")
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged(".*Example app listening on port 3001.*"))
.Build();

GoodImpl = new ContainerBuilder()
.WithImage(GOOD_PASTRY_IMAGE)
.WithNetwork(Network)
.WithNetworkAliases("good-impl")
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged(".*Example app listening on port 3002.*"))
.Build();
}

public async ValueTask InitializeAsync()
{
MicrocksContainer.Started +=
(_, _) => MicrocksContainer.ImportAsMainArtifact("apipastries-openapi.yaml");

await Network.CreateAsync();
await MicrocksContainer.StartAsync();
await BadImpl.StartAsync();
await GoodImpl.StartAsync();
}

public async ValueTask DisposeAsync()
{
// Dispose of the containers in reverse order of creation
await MicrocksContainer.DisposeAsync();
await BadImpl.DisposeAsync();
await GoodImpl.DisposeAsync();
await Network.DisposeAsync();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// 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.Tests.Fixtures;

[CollectionDefinition("MicrocksTests")]
public class MicrocksTestCollection : ICollectionFixture<MicrocksContractTestingFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
Loading
Loading