Skip to content

Commit 8bf5136

Browse files
committed
feat(testcontainers#421) Added AzuriteTestcontainer - basic implementation
1 parent 477776a commit 8bf5136

File tree

8 files changed

+309
-0
lines changed

8 files changed

+309
-0
lines changed

Packages.props

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0" />
55
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
66
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" />
7+
<PackageReference Update="Azure.Data.Tables" Version="12.6.1" />
78
<PackageReference Update="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="all" />
89
<PackageReference Update="Docker.DotNet" Version="3.125.10" />
910
<PackageReference Update="Microsoft.Bcl.AsyncInterfaces" Version="1.1.1" />

Testcontainers.sln.DotSettings

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/PreferQualifiedReference/@EntryValue">False</s:Boolean>
3+
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean>
4+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Azurite/@EntryIndexedValue">True</s:Boolean>
5+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Testcontainer/@EntryIndexedValue">True</s:Boolean>
6+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Testcontainers/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace DotNet.Testcontainers.Builders
2+
{
3+
using System.Collections.ObjectModel;
4+
using DotNet.Testcontainers.Configurations.Modules.Databases;
5+
using DotNet.Testcontainers.Containers;
6+
using JetBrains.Annotations;
7+
8+
/// <summary>
9+
/// This class applies the extended Testcontainer configurations for Azurite.
10+
/// </summary>
11+
[PublicAPI]
12+
public static class TestcontainersBuilderAzuriteExtension
13+
{
14+
public static ITestcontainersBuilder<AzuriteTestcontainer> WithAzurite(this ITestcontainersBuilder<AzuriteTestcontainer> builder, AzuriteTestcontainerConfiguration configuration)
15+
{
16+
return builder
17+
.WithImage(configuration.Image)
18+
.WithEnvironment(new ReadOnlyDictionary<string, string>(configuration.Environments))
19+
.WithPortBinding(configuration.BlobPort, AzuriteTestcontainerConfiguration.DefaultBlobPort)
20+
.WithPortBinding(configuration.QueuePort, AzuriteTestcontainerConfiguration.DefaultQueuePort)
21+
.WithPortBinding(configuration.TablePort, AzuriteTestcontainerConfiguration.DefaultTablePort)
22+
.WithOutputConsumer(configuration.OutputConsumer)
23+
.WithWaitStrategy(configuration.WaitStrategy)
24+
.ConfigureContainer(container =>
25+
{
26+
container.ContainerBlobPort = AzuriteTestcontainerConfiguration.DefaultBlobPort;
27+
container.ContainerQueuePort = AzuriteTestcontainerConfiguration.DefaultQueuePort;
28+
container.ContainerTablePort = AzuriteTestcontainerConfiguration.DefaultTablePort;
29+
});
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
namespace DotNet.Testcontainers.Configurations.Modules.Databases
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using DotNet.Testcontainers.Builders;
6+
using JetBrains.Annotations;
7+
8+
public class AzuriteTestcontainerConfiguration : IDisposable
9+
{
10+
/// <summary>
11+
/// Default Azurite docker image.
12+
/// </summary>
13+
public const string DefaultAzuriteImage = "mcr.microsoft.com/azure-storage/azurite:3.18.0";
14+
15+
/// <summary>
16+
/// Default Blob service listening port. Default is 10000.
17+
/// </summary>
18+
public const int DefaultBlobPort = 10000;
19+
20+
/// <summary>
21+
/// Default Queue service listening port. Default is 10001.
22+
/// </summary>
23+
public const int DefaultQueuePort = 10001;
24+
25+
/// <summary>
26+
/// Default Table service listening port. Default 10002.
27+
/// </summary>
28+
public const int DefaultTablePort = 10002;
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="AzuriteTestcontainerConfiguration" /> class with default Azurite image.
32+
/// </summary>
33+
/// <param name="image">The Docker image.</param>
34+
public AzuriteTestcontainerConfiguration()
35+
: this(DefaultAzuriteImage)
36+
{
37+
}
38+
39+
/// <summary>
40+
/// Initializes a new instance of the <see cref="AzuriteTestcontainerConfiguration" /> class.
41+
/// </summary>
42+
/// <param name="image">The Docker image.</param>
43+
protected AzuriteTestcontainerConfiguration(string image)
44+
{
45+
this.Image = image;
46+
this.Environments = new Dictionary<string, string>();
47+
this.OutputConsumer = Consume.DoNotConsumeStdoutAndStderr();
48+
}
49+
50+
/// <summary>
51+
/// Gets the Docker image.
52+
/// </summary>
53+
[PublicAPI]
54+
public string Image { get; }
55+
56+
/// <summary>
57+
/// Gets or sets the host Blob port.
58+
/// </summary>
59+
/// <remarks>
60+
/// Corresponds to the default port of the hosted service.
61+
/// </remarks>
62+
[PublicAPI]
63+
public int BlobPort { get; set; }
64+
65+
/// <summary>
66+
/// Gets or sets the host Queue port.
67+
/// </summary>
68+
/// <remarks>
69+
/// Corresponds to the default port of the hosted service.
70+
/// </remarks>
71+
[PublicAPI]
72+
public int QueuePort { get; set; }
73+
74+
/// <summary>
75+
/// Gets or sets the host Table port.
76+
/// </summary>
77+
/// <remarks>
78+
/// Corresponds to the default port of the hosted service.
79+
/// </remarks>
80+
[PublicAPI]
81+
public int TablePort { get; set; }
82+
83+
/// <summary>
84+
/// Gets the environment configuration.
85+
/// </summary>
86+
[PublicAPI]
87+
public IDictionary<string, string> Environments { get; }
88+
89+
/// <summary>
90+
/// Gets or sets the output consumer.
91+
/// </summary>
92+
/// <remarks>
93+
/// Uses <see cref="Consume.DoNotConsumeStdoutAndStderr" /> as default value.
94+
/// </remarks>
95+
[PublicAPI]
96+
[CanBeNull]
97+
public IOutputConsumer OutputConsumer { get; set; }
98+
99+
/// <summary>
100+
/// Gets the wait strategy.
101+
/// </summary>
102+
/// <remarks>
103+
/// Uses <see cref="Wait.ForUnixContainer" /> and waits for Azurite ports.
104+
/// </remarks>
105+
[PublicAPI]
106+
public IWaitForContainerOS WaitStrategy => Wait.ForUnixContainer()
107+
.UntilPortIsAvailable(DefaultBlobPort)
108+
.UntilPortIsAvailable(DefaultQueuePort)
109+
.UntilPortIsAvailable(DefaultTablePort);
110+
111+
/// <inheritdoc />
112+
public void Dispose()
113+
{
114+
this.Dispose(true);
115+
GC.SuppressFinalize(this);
116+
}
117+
118+
/// <summary>
119+
/// Releases all resources used by the <see cref="HostedServiceConfiguration" />.
120+
/// </summary>
121+
/// <param name="disposing">True if managed resources should be disposed, otherwise false..</param>
122+
[PublicAPI]
123+
protected virtual void Dispose(bool disposing)
124+
{
125+
if (disposing)
126+
{
127+
this.OutputConsumer?.Dispose();
128+
}
129+
}
130+
}
131+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
namespace DotNet.Testcontainers.Containers
2+
{
3+
using DotNet.Testcontainers.Configurations;
4+
using JetBrains.Annotations;
5+
using Microsoft.Extensions.Logging;
6+
7+
[PublicAPI]
8+
public class AzuriteTestcontainer : TestcontainersContainer
9+
{
10+
internal AzuriteTestcontainer(ITestcontainersConfiguration configuration, ILogger logger)
11+
: base(configuration, logger)
12+
{
13+
}
14+
15+
/// <summary>
16+
/// Gets the host Blob port.
17+
/// </summary>
18+
[PublicAPI]
19+
public int BlobPort => this.GetMappedPublicPort(this.ContainerBlobPort);
20+
21+
/// <summary>
22+
/// Gets the host Queue port.
23+
/// </summary>
24+
[PublicAPI]
25+
public int QueuePort => this.GetMappedPublicPort(this.ContainerQueuePort);
26+
27+
/// <summary>
28+
/// Gets the host Table port.
29+
/// </summary>
30+
[PublicAPI]
31+
public int TablePort => this.GetMappedPublicPort(this.ContainerTablePort);
32+
33+
/// <summary>
34+
/// Gets or sets the container Blob port.
35+
/// </summary>
36+
[PublicAPI]
37+
public int ContainerBlobPort { get; set; }
38+
39+
/// <summary>
40+
/// Gets or sets the container Queue port.
41+
/// </summary>
42+
[PublicAPI]
43+
public int ContainerQueuePort { get; set; }
44+
45+
/// <summary>
46+
/// Gets or sets the container Table port.
47+
/// </summary>
48+
[PublicAPI]
49+
public int ContainerTablePort { get; set; }
50+
51+
/// <summary>
52+
/// Gets the Storage connection string.
53+
/// </summary>
54+
[PublicAPI]
55+
public string ConnectionString =>
56+
$"DefaultEndpointsProtocol=http;" +
57+
$"AccountName=devstoreaccount1;" +
58+
$"AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;" +
59+
$"BlobEndpoint=http://{this.Hostname}:{this.BlobPort}/devstoreaccount1;" +
60+
$"QueueEndpoint=http://{this.Hostname}:{this.QueuePort}/devstoreaccount1;" +
61+
$"TableEndpoint=http://{this.Hostname}:{this.TablePort}/devstoreaccount1;";
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace DotNet.Testcontainers.Tests.Fixtures
2+
{
3+
using System;
4+
using System.Threading.Tasks;
5+
using Azure.Data.Tables;
6+
using DotNet.Testcontainers.Builders;
7+
using DotNet.Testcontainers.Configurations.Modules.Databases;
8+
using DotNet.Testcontainers.Containers;
9+
using Xunit;
10+
11+
public sealed class AzuriteFixture : IAsyncLifetime, IDisposable
12+
{
13+
private readonly AzuriteTestcontainerConfiguration configuration = new();
14+
15+
public AzuriteFixture()
16+
{
17+
this.Container = new TestcontainersBuilder<AzuriteTestcontainer>()
18+
.WithAzurite(this.configuration)
19+
.Build();
20+
}
21+
22+
public AzuriteTestcontainer Container { get; }
23+
24+
public TableServiceClient Connection { get; private set; }
25+
26+
public async Task InitializeAsync()
27+
{
28+
await this.Container.StartAsync()
29+
.ConfigureAwait(false);
30+
31+
this.Connection = new TableServiceClient(this.Container.ConnectionString);
32+
}
33+
34+
public async Task DisposeAsync()
35+
{
36+
await this.Container.DisposeAsync()
37+
.ConfigureAwait(false);
38+
}
39+
40+
public void Dispose()
41+
{
42+
this.configuration.Dispose();
43+
}
44+
}
45+
}

tests/Testcontainers.Tests/Testcontainers.Tests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<RootNamespace>DotNet.Testcontainers.Tests</RootNamespace>
1010
</PropertyGroup>
1111
<ItemGroup>
12+
<PackageReference Include="Azure.Data.Tables" />
1213
<PackageReference Include="Microsoft.NET.Test.Sdk" />
1314
<PackageReference Include="Microsoft.Extensions.Hosting" />
1415
<PackageReference Include="coverlet.msbuild" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace DotNet.Testcontainers.Tests.Unit
2+
{
3+
using System.Threading.Tasks;
4+
using DotNet.Testcontainers.Tests.Fixtures;
5+
using Xunit;
6+
7+
[Collection(nameof(Testcontainers))]
8+
public sealed class AzuriteTestcontainerTest : IClassFixture<AzuriteFixture>
9+
{
10+
private readonly AzuriteFixture azuriteFixture;
11+
12+
public AzuriteTestcontainerTest(AzuriteFixture azuriteFixture)
13+
{
14+
this.azuriteFixture = azuriteFixture;
15+
}
16+
17+
[Fact]
18+
public async Task ConnectionEstablished()
19+
{
20+
// Given
21+
var connection = this.azuriteFixture.Connection;
22+
23+
// When
24+
var properties = await connection.GetPropertiesAsync().ConfigureAwait(false);
25+
26+
// Then
27+
Assert.True(properties.GetRawResponse().Status is >= 200 and <= 299);
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)