Skip to content

Commit

Permalink
feat(#421): Add Cosmos DB Linux Emulator
Browse files Browse the repository at this point in the history
Co-authored-by: Tommy Alander <[email protected]>
  • Loading branch information
2 people authored and HofmeisterAn committed Oct 5, 2022
1 parent fe00966 commit 07c6786
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 0 deletions.
1 change: 1 addition & 0 deletions Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<PackageReference Update="Azure.Data.Tables" Version="12.6.1" />
<PackageReference Update="Azure.Storage.Blobs" Version="12.13.0" />
<PackageReference Update="Azure.Storage.Queues" Version="12.11.0" />
<PackageReference Update="Microsoft.Azure.Cosmos" Version="3.30.0" />
<PackageReference Update="Confluent.Kafka" Version="1.8.2" />
<PackageReference Update="CouchbaseNetClient" Version="3.2.9" />
<PackageReference Update="Elastic.Clients.Elasticsearch" Version="8.0.0-beta.3" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
namespace DotNet.Testcontainers.Configurations
{
using System;
using System.Globalization;
using System.IO;
using DotNet.Testcontainers.Builders;
using JetBrains.Annotations;

[PublicAPI]
public sealed class CosmosDbTestcontainerConfiguration : TestcontainerDatabaseConfiguration
{
public const string DefaultCosmosDbApiImage =
"mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator";

private const int DefaultCosmosPort = 8081;

public CosmosDbTestcontainerConfiguration()
: this(DefaultCosmosDbApiImage)
{
}

public CosmosDbTestcontainerConfiguration(string image)
: base(image, DefaultCosmosPort)
{
this.PartitionCount = 2;
this.IpAddressOverride = "127.0.0.1";
this.OutputConsumer = Consume.RedirectStdoutAndStderrToStream(new MemoryStream(), new MemoryStream());
this.Database = "default";
this.WaitStrategy = Wait.ForUnixContainer().UntilMessageIsLogged(this.OutputConsumer.Stdout, "Started|Shutting");
}

public override IOutputConsumer OutputConsumer { get; }

public override IWaitForContainerOS WaitStrategy { get; }

public override string Password
{
// Default key for the emulator
// See: https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator?tabs=ssl-netstd21#authenticate-requests
get => "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
set => throw new NotImplementedException();
}

public override string Database
{
get => this.Environments["AZURE_COSMOS_EMULATOR_DATABASE"];
set => this.Environments["AZURE_COSMOS_EMULATOR_DATABASE"] = value;
}

public int PartitionCount
{
get => int.TryParse(this.Environments["AZURE_COSMOS_EMULATOR_PARTITION_COUNT"], NumberStyles.Integer, CultureInfo.InvariantCulture, out var partitionCount) ? partitionCount : 1;
set => this.Environments["AZURE_COSMOS_EMULATOR_PARTITION_COUNT"] = value.ToString(CultureInfo.InvariantCulture);
}

public bool EnableDataPersistence
{
get => bool.TryParse(this.Environments["AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE"], out var enableDataPersistence) && enableDataPersistence;
set => this.Environments["AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE"] = value.ToString().ToLowerInvariant();
}

public string IpAddressOverride
{
get => this.Environments["AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE"];
set => this.Environments["AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE"] = value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace DotNet.Testcontainers.Containers
{
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using DotNet.Testcontainers.Configurations;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;

[PublicAPI]
public sealed class CosmosDbTestcontainer : TestcontainerDatabase
{
internal CosmosDbTestcontainer(ITestcontainersConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
}

public HttpMessageHandler HttpMessageHandler => new UriRewriter(this.Hostname, this.Port);

public HttpClient HttpClient => new HttpClient(this.HttpMessageHandler);

public override string ConnectionString =>
$"AccountEndpoint=https://{this.Hostname}:{this.Port};AccountKey={this.Password}";

private sealed class UriRewriter : DelegatingHandler
{
private readonly string hostname;

private readonly int port;

public UriRewriter(string hostname, int port)
: base(new HttpClientHandler { ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true })
{
this.hostname = hostname;
this.port = port;
}

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.RequestUri = new UriBuilder("https", this.hostname, this.port, request.RequestUri.PathAndQuery).Uri;
return base.SendAsync(request, cancellationToken);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace DotNet.Testcontainers.Tests.Fixtures
{
using System;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using JetBrains.Annotations;
using Microsoft.Azure.Cosmos;

[UsedImplicitly]
public sealed class CosmosDbFixture : DatabaseFixture<CosmosDbTestcontainer, CosmosClient>
{
private readonly CosmosDbTestcontainerConfiguration configuration = new CosmosDbTestcontainerConfiguration();

private readonly CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));

public CosmosDbFixture()
{
this.Container = new TestcontainersBuilder<CosmosDbTestcontainer>()
.WithDatabase(this.configuration)
.Build();
}

public override Task InitializeAsync()
{
return this.Container.StartAsync(this.cts.Token);
}

public override Task DisposeAsync()
{
return this.Container.DisposeAsync().AsTask();
}

public override void Dispose()
{
this.configuration.Dispose();
}
}
}
1 change: 1 addition & 0 deletions tests/Testcontainers.Tests/Testcontainers.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<PackageReference Include="Azure.Data.Tables" />
<PackageReference Include="Azure.Storage.Blobs" />
<PackageReference Include="Azure.Storage.Queues" />
<PackageReference Include="Microsoft.Azure.Cosmos" />
<PackageReference Include="Confluent.Kafka" />
<PackageReference Include="CouchbaseNetClient" />
<PackageReference Include="Elastic.Clients.Elasticsearch" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace DotNet.Testcontainers.Tests.Unit
{
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using DotNet.Testcontainers.Tests.Fixtures;
using Microsoft.Azure.Cosmos;
using Xunit;

public static class CosmosDbTestcontainerTest
{
[Collection(nameof(Testcontainers))]
public sealed class ConnectionTests : IClassFixture<CosmosDbFixture>, IDisposable
{
private readonly HttpClient httpClient;

private readonly CosmosClient cosmosClient;

public ConnectionTests(CosmosDbFixture cosmosDbFixture)
: this(cosmosDbFixture.Container.HttpClient, cosmosDbFixture.Container.ConnectionString)
{
}

private ConnectionTests(HttpClient httpClient, string connectionString)
{
var cosmosClientOptions = new CosmosClientOptions { ConnectionMode = ConnectionMode.Gateway, HttpClientFactory = () => httpClient };
this.httpClient = httpClient;
this.cosmosClient = new CosmosClient(connectionString, cosmosClientOptions);
}

[Fact(Skip = "Waiting for a working cosmosdb emulator, https://github.com/Azure/azure-cosmos-db-emulator-docker/issues/45")]
public async Task ShouldEstablishConnection()
{
var accountProperties = await this.cosmosClient.ReadAccountAsync()
.ConfigureAwait(false);

Assert.Equal("localhost", accountProperties.Id);
}

[Fact(Skip = "Waiting for a working cosmosdb emulator, see https://github.com/Azure/azure-cosmos-db-emulator-docker/issues/45")]
public async Task CreateDatabaseTest()
{
var databaseResponse = await this.cosmosClient.CreateDatabaseIfNotExistsAsync("db")
.ConfigureAwait(false);

Assert.Equal(HttpStatusCode.Created, databaseResponse.StatusCode);
}

public void Dispose()
{
this.cosmosClient.Dispose();
this.httpClient.Dispose();
}
}
}
}

0 comments on commit 07c6786

Please sign in to comment.