From dad2dc922fa0ee42761dd16192ec3d2cd368af4d Mon Sep 17 00:00:00 2001
From: Jesse Wellenberg <48557715+Yeseh@users.noreply.github.com>
Date: Sun, 28 Aug 2022 16:35:09 +0200
Subject: [PATCH] Restructure to model Azurite testcontainer
---
Packages.props | 1 +
.../TestcontainersBuilderCosmosDbExtension.cs | 21 ++++
.../CosmosDbTestcontainerConfiguration.cs | 74 ++++++++----
.../Databases/CosmosDbTestcontainer.cs | 108 +++---------------
.../Unix/Modules/Databases/CosmosDbFixture.cs | 93 +++++++--------
.../Testcontainers.Tests.csproj | 1 +
.../Databases/CosmosDbTestcontainerTest.cs | 61 +++++++---
7 files changed, 185 insertions(+), 174 deletions(-)
create mode 100644 src/Testcontainers/Builders/TestcontainersBuilderCosmosDbExtension.cs
diff --git a/Packages.props b/Packages.props
index 3630ba010..27b841964 100644
--- a/Packages.props
+++ b/Packages.props
@@ -27,6 +27,7 @@
+
diff --git a/src/Testcontainers/Builders/TestcontainersBuilderCosmosDbExtension.cs b/src/Testcontainers/Builders/TestcontainersBuilderCosmosDbExtension.cs
new file mode 100644
index 000000000..c43cf7f2e
--- /dev/null
+++ b/src/Testcontainers/Builders/TestcontainersBuilderCosmosDbExtension.cs
@@ -0,0 +1,21 @@
+namespace DotNet.Testcontainers.Builders
+{
+ using DotNet.Testcontainers.Configurations;
+ using DotNet.Testcontainers.Containers;
+ using JetBrains.Annotations;
+
+ [PublicAPI]
+ public static class TestcontainersBuilderCosmosDbExtension
+ {
+ public static ITestcontainersBuilder WithCosmosDb(
+ this ITestcontainersBuilder builder, CosmosDbTestcontainerConfiguration configuration)
+ {
+ builder.WithImage(configuration.Image)
+ .WithWaitStrategy(configuration.WaitStrategy)
+ .WithExposedPort(configuration.SqlApiContainerPort)
+ .WithPortBinding(configuration.SqlApiPort);
+
+ return builder;
+ }
+ }
+}
diff --git a/src/Testcontainers/Configurations/Modules/Databases/CosmosDbTestcontainerConfiguration.cs b/src/Testcontainers/Configurations/Modules/Databases/CosmosDbTestcontainerConfiguration.cs
index 602dbaa85..262b2aca6 100644
--- a/src/Testcontainers/Configurations/Modules/Databases/CosmosDbTestcontainerConfiguration.cs
+++ b/src/Testcontainers/Configurations/Modules/Databases/CosmosDbTestcontainerConfiguration.cs
@@ -1,39 +1,63 @@
namespace DotNet.Testcontainers.Configurations
{
+ using System;
using DotNet.Testcontainers.Builders;
using JetBrains.Annotations;
[PublicAPI]
public class CosmosDbTestcontainerConfiguration : TestcontainerDatabaseConfiguration
{
- private const string CosmosDbImage =
+ // TODO: WithMongoAPI extension?
+ public const string DefaultCosmosDbSqlApiImage =
"mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator";
- private const int CosmosDbPort = 8081;
+ public const string DefaultCosmosDbMongoDbApiImage =
+ "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:mongo";
- private const string CosmosDbDefaultKey =
- "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
-
- private const string CosmosDbDefaultAccountName =
- "localhost:8081";
+ private const int DefaultSqlApiPort = 8081;
+
+ private const int DefaultMongoDbApiPort = 10250;
public CosmosDbTestcontainerConfiguration()
- : this(CosmosDbImage) { }
+ : this(DefaultCosmosDbSqlApiImage)
+ {
+ }
- public CosmosDbTestcontainerConfiguration(string image)
- : base(image, CosmosDbPort, CosmosDbPort)
+ public CosmosDbTestcontainerConfiguration(string image)
+ : base(image, DefaultSqlApiPort)
{
- this.Environments.Add("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", "1");
+ this.Environments.Add("AZURE_COSMOS_EMULATOR_MONGO_DB_ENDPOINT", "");
+ this.Environments.Add("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", "1");
}
- public override string Username
+ public int MongoDbApiPort { get; set; }
+
+ public int MongoDbApiContainerPort { get; set; }
+
+ public int SqlApiPort { get; set; }
+
+ public int SqlApiContainerPort { get; set; }
+
+ public override IWaitForContainerOS WaitStrategy
{
- get => CosmosDbDefaultAccountName;
+ get
+ {
+ var waitStrategy = Wait.ForUnixContainer();
+ // waitStrategy = string.IsNullOrWhiteSpace(this.MongoDbEndpoint)
+ // ? waitStrategy.UntilPortIsAvailable(SqlApiContainerPort)
+ // : waitStrategy.UntilPortIsAvailable(MongoDbApiContainerPort);
+ waitStrategy = waitStrategy.UntilMessageIsLogged(this.OutputConsumer.Stdout, "Started");
+
+ return waitStrategy;
+ }
}
public override string Password
{
- get => CosmosDbDefaultKey;
+ // 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
@@ -42,26 +66,28 @@ public override string Database
set => this.Environments["AZURE_COSMOS_EMULATOR_DATABASE"] = value;
}
- public string PartitionCount
+ public int PartitionCount
{
- get => this.Environments["AZURE_COSMOS_EMULATOR_PARTITION_COUNT"];
- set => this.Environments["AZURE_COSMOS_EMULATOR_PARTITION_COUNT"] = value;
+ get => int.Parse(this.Environments["AZURE_COSMOS_EMULATOR_PARTITION_COUNT"]);
+ set => this.Environments["AZURE_COSMOS_EMULATOR_PARTITION_COUNT"] = value.ToString();
}
- public string EnableDataPersistence
+ public bool EnableDataPersistence
{
- get => this.Environments["AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE"];
- set => this.Environments["AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE"] = value;
+ get => this.Environments["AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE"] == "true";
+ set => this.Environments["AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE"] = value.ToString().ToLower();
}
public string IPAddressOverride
{
get => this.Environments["AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE"];
set => this.Environments["AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE"] = value;
- }
+ }
- public override IWaitForContainerOS WaitStrategy => Wait.ForUnixContainer()
- .UntilCommandIsCompleted("ls");
- //.UntilMessageIsLogged(this.OutputConsumer.Stdout, "Started");
+ public string MongoDbEndpoint
+ {
+ get => this.Environments["AZURE_COSMOS_EMULATOR_MONGO_DB_ENDPOINT"];
+ set => this.Environments["AZURE_COSMOS_EMULATOR_MONGO_DB_ENDPOINT"] = value;
+ }
}
}
diff --git a/src/Testcontainers/Containers/Modules/Databases/CosmosDbTestcontainer.cs b/src/Testcontainers/Containers/Modules/Databases/CosmosDbTestcontainer.cs
index 8d928148e..b1c3c5fb7 100644
--- a/src/Testcontainers/Containers/Modules/Databases/CosmosDbTestcontainer.cs
+++ b/src/Testcontainers/Containers/Modules/Databases/CosmosDbTestcontainer.cs
@@ -1,110 +1,36 @@
namespace DotNet.Testcontainers.Containers
{
- using System.Collections.Generic;
using System.Net.Http;
- using System.Threading.Tasks;
using DotNet.Testcontainers.Configurations;
using Microsoft.Extensions.Logging;
- using System.Text.Json;
- using System;
- using System.Threading;
- using System.Text;
+ using JetBrains.Annotations;
public sealed class CosmosDbTestcontainer : TestcontainerDatabase
{
- private string CosmosUrl;
+ [PublicAPI]
+ public int SqlApiPort
+ => this.GetMappedPublicPort(this.ContainerSqlApiPort);
- private HttpClient HttpClient;
-
- internal CosmosDbTestcontainer(ITestcontainersConfiguration configuration, ILogger logger)
- : base(configuration, logger)
- {
- CosmosUrl = $"https://{this.Username}.documents.azure.com/dbs";
- }
+ [PublicAPI]
+ public int MongoApiPort
+ => this.GetMappedPublicPort(this.ContainerMongoApiPort);
- public override string ConnectionString
- => $"AccountEndpoint=https://{this.Username}:{this.Port}.documents.azure.com:443/;AccountKey={this.Password}";
+ [PublicAPI]
+ public int ContainerSqlApiPort { get; set; }
- public async Task QueryAsync(
- string queryString, IEnumerable> parameters = default)
- {
- Console.WriteLine("Executing query...");
- var client = GetHttpClient();
- var parJsonStr =JsonSerializer.Serialize(parameters);
- var body = new { Query = queryString, Parameters = parJsonStr };
- var reqBodyStr = JsonSerializer.Serialize(body);
- var content = new StringContent(reqBodyStr);
-
- var response = await client.PostAsync(CosmosUrl, content);
+ [PublicAPI]
+ public int ContainerMongoApiPort { get; set; }
- return response;
- }
- // TODO: Call this implicitly on initialize
- public async Task CreateDatabaseAsync()
- {
- Console.WriteLine("Attempting to create database...");
- var url = $"https://localhost:8081/dbs";
- var client = GetHttpClient();
- var jsonData = JsonSerializer.Serialize(new { id = string.IsNullOrEmpty(this.Database) ? "testdb" : this.Database });
- var contentData = new StringContent(jsonData, Encoding.UTF8, "application/json");
- var response = await client.PostAsync(url, contentData).ConfigureAwait(false);
+ public string AccountEndpoint { get; }
- return response;
- }
+ private HttpClient HttpClient;
- private HttpClient GetHttpClient()
+ internal CosmosDbTestcontainer(ITestcontainersConfiguration configuration, ILogger logger)
+ : base(configuration, logger)
{
- if (HttpClient != null) return HttpClient;
-
- var handler = new CosmosDbHttpHandler(this.Password);
- var client = new HttpClient(handler);
-
- HttpClient = client;
- return HttpClient;
}
- private sealed class CosmosDbHttpHandler : HttpClientHandler
- {
- private readonly string _password;
-
- public CosmosDbHttpHandler(string password)
- {
- this._password = password;
- // Skip SSL certificate validation
- // https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator?tabs=ssl-netstd21#disable-ssl-validation
- this.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
- }
-
- // https://stackoverflow.com/questions/52262767/cosmos-db-rest-api-create-user-permissio
- protected override Task SendAsync(
- HttpRequestMessage request, CancellationToken ct)
- {
- var hmac = new System.Security.Cryptography.HMACSHA256()
- {
- Key = Convert.FromBase64String(this._password)
- };
-
- var date = DateTime.UtcNow.ToString("r");
- var payload = "POST".ToLowerInvariant() + "\n" +
- "dbs".ToLowerInvariant() + "\n" +
- "" + "\n" +
- date.ToLowerInvariant() + "\n" +
- "" + "\n";
-
- var hashPayload = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
- var signature = Convert.ToBase64String(hashPayload);
- var authHeaderValue = System.Web.HttpUtility.UrlEncode($"type=master&ver=1.0&sig={signature}");
-
- request.Headers.Add("x-ms-documentdb-isquery", "true");
- request.Headers.Add("x-ms-documentdb-query-enablecrosspartition", "true");
- request.Headers.Add("x-ms-version", "2018-12-31");
- request.Headers.Add("x-ms-date", date);
- request.Headers.Add("authorization", authHeaderValue);
- //request.Headers.Add("Content-Type", "application/query+json");
- request.Headers.Add("Accept", "application/json");
-
- return base.SendAsync(request, ct);
- }
- }
+ public override string ConnectionString =>
+ $"AccountEndpoint=https://{this.Hostname}:{this.SqlApiPort};AccountKey={this.Password}";
}
}
diff --git a/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/CosmosDbFixture.cs b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/CosmosDbFixture.cs
index bb633c395..e1fa2b7a3 100644
--- a/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/CosmosDbFixture.cs
+++ b/tests/Testcontainers.Tests/Fixtures/Containers/Unix/Modules/Databases/CosmosDbFixture.cs
@@ -1,57 +1,60 @@
namespace DotNet.Testcontainers.Tests.Fixtures
{
- using System;
- using System.Data.Common;
- using System.Threading.Tasks;
+ using System;
+ using System.Data.Common;
+ using System.IO;
+ using System.Threading.Tasks;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
+ using JetBrains.Annotations;
using Npgsql;
- public sealed class CosmosDbFixture : DatabaseFixture
+ public static class CosmosDbFixture
{
- private readonly TestcontainerDatabaseConfiguration configuration = new CosmosDbTestcontainerConfiguration
+ [UsedImplicitly]
+ public class CosmosDbDefaultFixture : DatabaseFixture
{
- Database = "testdb"
- };
-
- public CosmosDbFixture()
- {
- this.Container = new TestcontainersBuilder()
- .WithDatabase(this.configuration)
- .Build();
- }
-
- public override async Task InitializeAsync()
- {
- Console.WriteLine("Initializing CosmosDB container");
- await this.Container.StartAsync()
- .ConfigureAwait(false);
-
- // var dbResponse = await this.Container.CreateDatabaseAsync()
- // .ConfigureAwait(false);
-
- // if (dbResponse.StatusCode != System.Net.HttpStatusCode.Created)
- // {
- // throw new System.Exception("Failed to create database");
- // }
- }
-
- public override async Task DisposeAsync()
- {
- if (Connection != null && Connection.State != System.Data.ConnectionState.Closed)
- {
- this.Connection.Dispose();
-
- }
-
- await this.Container.DisposeAsync()
- .ConfigureAwait(false);
- }
-
- public override void Dispose()
- {
- this.configuration.Dispose();
+ public CosmosDbTestcontainerConfiguration Configuration { get; set; }
+
+ public CosmosDbDefaultFixture()
+ : this(new CosmosDbTestcontainerConfiguration())
+ {
+ }
+
+ protected CosmosDbDefaultFixture(CosmosDbTestcontainerConfiguration configuration)
+ {
+ this.Configuration = configuration;
+ this.Container = new TestcontainersBuilder()
+ .WithHostname("localhost")
+ .WithImage(configuration.Image)
+ .WithPortBinding(configuration.DefaultPort)
+ .WithExposedPort(configuration.DefaultPort)
+ .WithWaitStrategy(configuration.WaitStrategy)
+ .WithEnvironment("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", "1")
+ .Build();
+ }
+
+ public override Task InitializeAsync()
+ {
+ return this.Container.StartAsync();
+ }
+
+ public override async Task DisposeAsync()
+ {
+ if (Connection != null && Connection.State != System.Data.ConnectionState.Closed)
+ {
+ this.Connection.Dispose();
+ }
+
+ await this.Container.DisposeAsync()
+ .ConfigureAwait(false);
+ }
+
+ public override void Dispose()
+ {
+ this.Configuration.Dispose();
+ }
}
}
}
diff --git a/tests/Testcontainers.Tests/Testcontainers.Tests.csproj b/tests/Testcontainers.Tests/Testcontainers.Tests.csproj
index 7b404d797..99b766802 100644
--- a/tests/Testcontainers.Tests/Testcontainers.Tests.csproj
+++ b/tests/Testcontainers.Tests/Testcontainers.Tests.csproj
@@ -24,6 +24,7 @@
+
diff --git a/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/CosmosDbTestcontainerTest.cs b/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/CosmosDbTestcontainerTest.cs
index 7bec469e7..168760c53 100644
--- a/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/CosmosDbTestcontainerTest.cs
+++ b/tests/Testcontainers.Tests/Unit/Containers/Unix/Modules/Databases/CosmosDbTestcontainerTest.cs
@@ -1,25 +1,58 @@
namespace DotNet.Testcontainers.Tests.Unit
{
+ using Microsoft.Azure.Cosmos;
using DotNet.Testcontainers.Tests.Fixtures;
using System.Threading.Tasks;
using Xunit;
+ using System.Net.Http;
+ using System.Data.Common;
+ using DotNet.Testcontainers.Containers;
- public class CosmosDbTestcontainerTest : IClassFixture
- {
- private readonly CosmosDbFixture cosmosDbFixture;
+ public static class CosmosDbTestcontainerTest
+ {
+ [Collection(nameof(Testcontainers))]
+ public sealed class SqlApi : IClassFixture
+ {
+ private static readonly CosmosClientOptions skipSslValidationOptions = new CosmosClientOptions()
+ {
+ HttpClientFactory = () =>
+ {
+ HttpMessageHandler httpMessageHandler = new HttpClientHandler()
+ {
+ ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
+ };
- public CosmosDbTestcontainerTest(CosmosDbFixture cosmosDbFixture)
- {
- this.cosmosDbFixture = cosmosDbFixture;
- }
+ return new HttpClient(httpMessageHandler);
+ },
- [Fact]
- public async Task DatabaseCreated()
- {
- var dbResponse = await this.cosmosDbFixture.Container.CreateDatabaseAsync()
- .ConfigureAwait(false);
+ ConnectionMode = ConnectionMode.Gateway
+ };
- Assert.Equal(System.Net.HttpStatusCode.Created, dbResponse.StatusCode);
- }
+ private readonly CosmosDbFixture.CosmosDbDefaultFixture commonContainerPorts;
+
+ private CosmosDbTestcontainer Container;
+
+ public SqlApi(CosmosDbFixture.CosmosDbDefaultFixture commonContainerPorts)
+ {
+ this.commonContainerPorts = commonContainerPorts;
+ }
+
+ [Fact]
+ public async Task ShouldEstablishConnection()
+ {
+ var exception = await Record.ExceptionAsync(() => Task.WhenAll(EstablishConnection(commonContainerPorts)));
+ Assert.Null(exception);
+ }
+
+ private static async Task EstablishConnection(CosmosDbFixture.CosmosDbDefaultFixture cosmosDb)
+ {
+ var client = new CosmosClient(cosmosDb.Container.ConnectionString, skipSslValidationOptions);
+
+ var properties = await client.ReadAccountAsync()
+ .ConfigureAwait(false);
+
+ Assert.Equal(cosmosDb.Configuration.SqlApiPort, cosmosDb.Container.SqlApiPort);
+ }
}
+ }
}