diff --git a/.github/workflows/mevd.yml b/.github/workflows/mevd.yml
index 5fb7471..392e301 100644
--- a/.github/workflows/mevd.yml
+++ b/.github/workflows/mevd.yml
@@ -155,10 +155,9 @@ jobs:
fail-fast: false
matrix:
provider: ${{ fromJson(needs.discover.outputs.all_providers) }}
- # AzureAISearch and AzureDocumentDB require cloud instances and can't run in CI.
+ # AzureAISearch requires a cloud instance and can't run in CI.
exclude:
- provider: AzureAISearch
- - provider: AzureDocumentDB
steps:
- uses: actions/checkout@v6
diff --git a/MEVD/test/AzureDocumentDB.ConformanceTests/AzureDocumentDB.ConformanceTests.csproj b/MEVD/test/AzureDocumentDB.ConformanceTests/AzureDocumentDB.ConformanceTests.csproj
index 03ee156..2e5e008 100644
--- a/MEVD/test/AzureDocumentDB.ConformanceTests/AzureDocumentDB.ConformanceTests.csproj
+++ b/MEVD/test/AzureDocumentDB.ConformanceTests/AzureDocumentDB.ConformanceTests.csproj
@@ -15,6 +15,7 @@
+
diff --git a/MEVD/test/AzureDocumentDB.ConformanceTests/Support/DocumentDBTestEnvironment.cs b/MEVD/test/AzureDocumentDB.ConformanceTests/Support/DocumentDBTestEnvironment.cs
deleted file mode 100644
index 404c7d1..0000000
--- a/MEVD/test/AzureDocumentDB.ConformanceTests/Support/DocumentDBTestEnvironment.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.Extensions.Configuration;
-
-namespace AzureDocumentDB.ConformanceTests.Support;
-
-#pragma warning disable CA1810 // Initialize all static fields when those fields are declared
-
-public static class DocumentDBTestEnvironment
-{
- public static readonly string? ConnectionString;
-
- public static bool IsConnectionStringDefined => ConnectionString is not null;
-
- static DocumentDBTestEnvironment()
- {
- var configuration = new ConfigurationBuilder()
- .AddJsonFile(path: "testsettings.json", optional: true)
- .AddJsonFile(path: "testsettings.development.json", optional: true)
- .AddEnvironmentVariables()
- .AddUserSecrets()
- .Build();
-
- ConnectionString = configuration["AzureDocumentDB:ConnectionString"];
- }
-}
diff --git a/MEVD/test/AzureDocumentDB.ConformanceTests/Support/DocumentDBTestStore.cs b/MEVD/test/AzureDocumentDB.ConformanceTests/Support/DocumentDBTestStore.cs
index 8983043..5c91a77 100644
--- a/MEVD/test/AzureDocumentDB.ConformanceTests/Support/DocumentDBTestStore.cs
+++ b/MEVD/test/AzureDocumentDB.ConformanceTests/Support/DocumentDBTestStore.cs
@@ -1,7 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Net.Security;
+using System.Security.Authentication;
using CommunityToolkit.VectorData.AzureDocumentDB;
+using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Containers;
+using MongoDB.Bson;
using MongoDB.Driver;
using VectorData.ConformanceTests.Support;
@@ -11,36 +16,80 @@ namespace AzureDocumentDB.ConformanceTests.Support;
public sealed class DocumentDBTestStore : TestStore
#pragma warning restore CA1001
{
+ private const string Username = "testuser";
+ private const string Password = "TestPassword123!";
+ private const ushort DocumentDBPort = 10260;
+
public static DocumentDBTestStore Instance { get; } = new();
+ private readonly IContainer _container = new ContainerBuilder("ghcr.io/microsoft/documentdb/documentdb-local:latest")
+ .WithPortBinding(DocumentDBPort, assignRandomHostPort: true)
+ .WithEnvironment("USERNAME", Username)
+ .WithEnvironment("PASSWORD", Password)
+ .WithWaitStrategy(Wait.ForUnixContainer()
+ .UntilInternalTcpPortIsAvailable(DocumentDBPort, strategy => strategy.WithTimeout(TimeSpan.FromMinutes(5))))
+ .Build();
+
private MongoClient? _client;
private IMongoDatabase? _database;
- public MongoClient Client => this._client ?? throw new InvalidOperationException("Not initialized");
- public IMongoDatabase Database => this._database ?? throw new InvalidOperationException("Not initialized");
+ public MongoClient Client => _client ?? throw new InvalidOperationException("Not initialized");
+ public IMongoDatabase Database => _database ?? throw new InvalidOperationException("Not initialized");
public override string DefaultIndexKind => Microsoft.Extensions.VectorData.IndexKind.IvfFlat;
public override string DefaultDistanceFunction => Microsoft.Extensions.VectorData.DistanceFunction.CosineDistance;
public DocumentDBVectorStore GetVectorStore(DocumentDBVectorStoreOptions options)
- => new(this.Database, options);
+ => new(Database, options);
private DocumentDBTestStore()
{
}
- protected override Task StartAsync()
+ protected override async Task StartAsync()
{
- if (string.IsNullOrWhiteSpace(DocumentDBTestEnvironment.ConnectionString))
+ await _container.StartAsync();
+ string connectionString = $"mongodb://{Username}:{Password}@{_container.Hostname}:{_container.GetMappedPublicPort(DocumentDBPort)}/?tls=true";
+
+ MongoClientSettings settings = MongoClientSettings.FromConnectionString(connectionString);
+ settings.SslSettings = new SslSettings
{
- throw new InvalidOperationException("Connection string is not configured, set the AzureDocumentDB:ConnectionString environment variable");
- }
+ EnabledSslProtocols = SslProtocols.Tls12,
+ ServerCertificateValidationCallback = static (_, _, _, _) => true,
+ };
- this._client = new MongoClient(DocumentDBTestEnvironment.ConnectionString);
- this._database = this._client.GetDatabase("VectorSearchTests");
- this.DefaultVectorStore = new DocumentDBVectorStore(this._database);
+ _client = new MongoClient(settings);
+ await WaitForMongoServiceAsync(_client, TimeSpan.FromMinutes(5));
+ _database = _client.GetDatabase("VectorSearchTests");
+ DefaultVectorStore = new DocumentDBVectorStore(_database);
+ }
+
+ protected override Task StopAsync()
+ {
+ _client?.Dispose();
+ return _container.StopAsync();
+ }
+
+ private async Task WaitForMongoServiceAsync(MongoClient client, TimeSpan timeout)
+ {
+ DateTimeOffset deadline = DateTimeOffset.UtcNow.Add(timeout);
+ Exception? lastException = null;
+
+ while (DateTimeOffset.UtcNow < deadline)
+ {
+ try
+ {
+ await client.GetDatabase("admin").RunCommandAsync(new BsonDocument("ping", 1));
+ return;
+ }
+ catch (Exception ex)
+ {
+ lastException = ex;
+ await Task.Delay(TimeSpan.FromMilliseconds(200));
+ }
+ }
- return Task.CompletedTask;
+ throw new TimeoutException($"Timed out waiting for the DocumentDB container to accept MongoDB connections. Last error: {lastException?.Message}", lastException);
}
}