Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Replace CosmosDb module #833

Merged
merged 13 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver.Tests", "tests\Testcontainers.WebDriver.Tests\Testcontainers.WebDriver.Tests.csproj", "{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.CosmosDb", "src\Testcontainers.CosmosDb\Testcontainers.CosmosDb.csproj", "{A724806F-8C94-4438-8011-04A9A1575318}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.CosmosDb.Tests", "tests\Testcontainers.CosmosDb.Tests\Testcontainers.CosmosDb.Tests.csproj", "{BD445A54-F411-4758-955E-397A1E98680C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -310,6 +314,14 @@ Global
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.Build.0 = Release|Any CPU
{A724806F-8C94-4438-8011-04A9A1575318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A724806F-8C94-4438-8011-04A9A1575318}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A724806F-8C94-4438-8011-04A9A1575318}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A724806F-8C94-4438-8011-04A9A1575318}.Release|Any CPU.Build.0 = Release|Any CPU
{BD445A54-F411-4758-955E-397A1E98680C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD445A54-F411-4758-955E-397A1E98680C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD445A54-F411-4758-955E-397A1E98680C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD445A54-F411-4758-955E-397A1E98680C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{58E94721-2681-4D82-8D94-0B2F9DB0D575} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -360,5 +372,7 @@ Global
{1A1983E6-5297-435F-B467-E8E1F11277D6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{A724806F-8C94-4438-8011-04A9A1575318} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{BD445A54-F411-4758-955E-397A1E98680C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions src/Testcontainers.CosmosDb/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
123 changes: 123 additions & 0 deletions src/Testcontainers.CosmosDb/CosmosDbBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using System.Net;

namespace Testcontainers.CosmosDb;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class CosmosDbBuilder : ContainerBuilder<CosmosDbBuilder, CosmosDbContainer, CosmosDbConfiguration>
{
public const string CosmosDbEmulatorImage = "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest";

public const ushort CosmosDbPort = 8081;

public const string DefaultAccountKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";

public const int DefaultPartitionCount = 2;

public const string DefaultIpAddressOverride = "127.0.0.1";

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

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

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

/// <summary>
/// Sets the partition count.
/// </summary>
/// <param name="partitionCount">The number of partitions.</param>
/// <returns>A configured instance of <see cref="CosmosDbBuilder" />.</returns>
public CosmosDbBuilder WithPartitionCount(int partitionCount)
{
return Merge(DockerResourceConfiguration, new CosmosDbConfiguration(partitionCount: partitionCount))
.WithEnvironment("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", partitionCount.ToString());
}

/// <summary>
/// Sets the overridden IP address.
/// </summary>
/// <param name="ipAddress">The overridden IP address.</param>
/// <returns>A configured instance of <see cref="CosmosDbBuilder" />.</returns>
public CosmosDbBuilder WithIpAddressOverride(string ipAddress)
{
return Merge(DockerResourceConfiguration, new CosmosDbConfiguration(ipAddressOverride: ipAddress))
.WithEnvironment("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", ipAddress);
}
AButler marked this conversation as resolved.
Show resolved Hide resolved

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

/// <inheritdoc />
protected override CosmosDbBuilder Init()
{
return base.Init()
.WithImage(CosmosDbEmulatorImage)
.WithPortBinding(CosmosDbPort, true)
.WithPartitionCount(DefaultPartitionCount)
.WithIpAddressOverride(DefaultIpAddressOverride)
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Started|Shutting"));
}

/// <inheritdoc />
protected override void Validate()
{
base.Validate();

if (!DockerResourceConfiguration.PartitionCount.HasValue)
{
throw new ArgumentException($"'{nameof(DockerResourceConfiguration.PartitionCount)}' cannot be null.");
}

if (DockerResourceConfiguration.PartitionCount.Value < 1)
{
throw new ArgumentException($"'{nameof(DockerResourceConfiguration.PartitionCount)}' cannot be less than 1.");
}

_ = Guard.Argument(DockerResourceConfiguration.IpAddressOverride, nameof(DockerResourceConfiguration.IpAddressOverride))
.NotNull()
.NotEmpty();

if (!IPAddress.TryParse(DockerResourceConfiguration.IpAddressOverride, out _))
{
throw new ArgumentException($"'{nameof(DockerResourceConfiguration.IpAddressOverride)}' must be a valid IP address.");
}
}

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

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

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

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class CosmosDbConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="CosmosDbConfiguration" /> class.
/// </summary>
/// <param name="partitionCount">The number of partitions to create</param>
/// <param name="ipAddressOverride">The overridden IP address.</param>
public CosmosDbConfiguration(
int? partitionCount = null,
string ipAddressOverride = null)
{
PartitionCount = partitionCount;
IpAddressOverride = ipAddressOverride;
}

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

/// <summary>
/// Initializes a new instance of the <see cref="CosmosDbConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public CosmosDbConfiguration(CosmosDbConfiguration oldValue, CosmosDbConfiguration newValue)
: base(oldValue, newValue)
{
PartitionCount = BuildConfiguration.Combine(oldValue.PartitionCount, newValue.PartitionCount);
IpAddressOverride = BuildConfiguration.Combine(oldValue.IpAddressOverride, newValue.IpAddressOverride);
}

/// <summary>
/// Gets the partition count
/// </summary>
public int? PartitionCount { get; }

/// <summary>
/// Gets the overridden IP address
/// </summary>
public string IpAddressOverride { get; }
}
64 changes: 64 additions & 0 deletions src/Testcontainers.CosmosDb/CosmosDbContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Testcontainers.CosmosDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class CosmosDbContainer : DockerContainer
{
private readonly CosmosDbConfiguration _configuration;

/// <summary>
/// Initializes a new instance of the <see cref="CosmosDbContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
/// <param name="logger">The logger.</param>
public CosmosDbContainer(CosmosDbConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
_configuration = configuration;
}

/// <summary>
/// Gets the CosmosDb connection string.
/// </summary>
/// <returns>The CosmosDb connection string.</returns>
public string GetConnectionString()
{
var properties = new Dictionary<string, string>();
properties.Add("AccountEndpoint", new UriBuilder("https", Hostname, GetMappedPublicPort(CosmosDbBuilder.CosmosDbPort)).ToString());
properties.Add("AccountKey", CosmosDbBuilder.DefaultAccountKey);
return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
}

/// <summary>
/// Gets a configured HTTP message handler that automatically trusts the CosmosDb Emulator's certificate.
/// </summary>
public HttpMessageHandler HttpMessageHandler => new UriRewriter(Hostname, GetMappedPublicPort(CosmosDbBuilder.CosmosDbPort));

/// <summary>
/// Gets a configured HTTP client that automatically trusts the CosmosDb Emulator's certificate.
/// </summary>
public HttpClient HttpClient => new HttpClient(HttpMessageHandler);

private sealed class UriRewriter : DelegatingHandler
{
private readonly string _hostname;
private readonly int _port;

public UriRewriter(string hostname, int port)
: base(new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true })
{
_hostname = hostname;
_port = port;
}

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.RequestUri = new UriBuilder("https", _hostname, _port, request.RequestUri.PathAndQuery).Uri;
return base.SendAsync(request, cancellationToken);
}
}
}
12 changes: 12 additions & 0 deletions src/Testcontainers.CosmosDb/Testcontainers.CosmosDb.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
10 changes: 10 additions & 0 deletions src/Testcontainers.CosmosDb/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
global using JetBrains.Annotations;
global using Microsoft.Extensions.Logging;

This file was deleted.

Loading