From 0bff16786769312b9cfbe788f8d208f053689f8f Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 10:34:16 +0200 Subject: [PATCH 01/12] Add AzuriteContainer using Testcontainers and wire into CheckForAzureStorage - Add AzuriteContainer static singleton that lazily starts an Azurite Testcontainer (or reuses an already-running instance on default ports). - Replace StorageEmulator usage in TestUtils.CheckForAzureStorage() with AzuriteContainer.EnsureStartedAsync(). - Fall back DataConnectionString to AzuriteContainer.ConnectionString when no explicit config is provided. - Add Testcontainers package reference to TestExtensions project. --- .../TestExtensions/AzuriteContainer.cs | 113 ++++++++++++++++++ .../TestDefaultConfiguration.cs | 2 +- .../TestExtensions/TestExtensions.csproj | 1 + .../TestExtensions/TestUtils.cs | 26 ++-- 4 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 test/TestInfrastructure/TestExtensions/AzuriteContainer.cs diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs new file mode 100644 index 00000000000..187aa662ae6 --- /dev/null +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs @@ -0,0 +1,113 @@ +using System.Net.Sockets; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Containers; + +namespace TestExtensions; + +/// +/// Manages a singleton Azurite container for Azure Storage integration tests. +/// The container is lazily started on first use and shared across all tests in the process. +/// +public static class AzuriteContainer +{ + private const int BlobPort = 10000; + private const int QueuePort = 10001; + private const int TablePort = 10002; + + // Well-known Azurite development credentials. + private const string AccountName = "devstoreaccount1"; + private const string AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; + + private static IContainer _container; + private static string _connectionString; + private static readonly object _lock = new(); + private static Task _startTask; + + /// + /// Gets the connection string for the running Azurite container. + /// Returns if the container has not been started. + /// + public static string ConnectionString => _connectionString; + + /// + /// Ensures Azurite is available. If the default ports are already reachable (e.g. an external + /// Azurite process or CI service container), the existing instance is reused. Otherwise, a + /// Testcontainer is started. + /// + /// if Azurite is available; if it could not be started. + public static Task EnsureStartedAsync() + { + if (_startTask is not null) + { + return _startTask; + } + + lock (_lock) + { + _startTask ??= StartAsync(); + } + + return _startTask; + } + + private static async Task StartAsync() + { + // First, check whether Azurite is already reachable on the default ports (e.g. CI service container or manual process). + if (IsPortReachable(BlobPort) && IsPortReachable(QueuePort) && IsPortReachable(TablePort)) + { + _connectionString = BuildConnectionString("127.0.0.1", BlobPort, QueuePort, TablePort); + return true; + } + + try + { + var container = new ContainerBuilder() + .WithImage("mcr.microsoft.com/azure-storage/azurite:latest") + .WithPortBinding(BlobPort, true) + .WithPortBinding(QueuePort, true) + .WithPortBinding(TablePort, true) + .WithWaitStrategy(Wait.ForUnixContainer() + .UntilPortIsAvailable(BlobPort) + .UntilPortIsAvailable(QueuePort) + .UntilPortIsAvailable(TablePort)) + .Build(); + + await container.StartAsync(); + + var host = container.Hostname; + var blobPort = container.GetMappedPublicPort(BlobPort); + var queuePort = container.GetMappedPublicPort(QueuePort); + var tablePort = container.GetMappedPublicPort(TablePort); + + _connectionString = BuildConnectionString(host, blobPort, queuePort, tablePort); + _container = container; + return true; + } + catch + { + return false; + } + } + + private static string BuildConnectionString(string host, int blobPort, int queuePort, int tablePort) + { + return $"DefaultEndpointsProtocol=http;AccountName={AccountName};AccountKey={AccountKey};" + + $"BlobEndpoint=http://{host}:{blobPort}/{AccountName};" + + $"QueueEndpoint=http://{host}:{queuePort}/{AccountName};" + + $"TableEndpoint=http://{host}:{tablePort}/{AccountName};"; + } + + private static bool IsPortReachable(int port) + { + try + { + using var client = new TcpClient(); + client.Connect("127.0.0.1", port); + return true; + } + catch (SocketException) + { + return false; + } + } +} diff --git a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs index a5e6316aac5..13ce8544ee5 100644 --- a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs +++ b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs @@ -39,7 +39,7 @@ public static bool UseAadAuthentication public static Uri TableEndpoint => new Uri(defaultConfiguration[nameof(TableEndpoint)]); public static Uri DataBlobUri => new Uri(defaultConfiguration[nameof(DataBlobUri)]); public static Uri DataQueueUri => new Uri(defaultConfiguration[nameof(DataQueueUri)]); - public static string DataConnectionString => defaultConfiguration[nameof(DataConnectionString)]; + public static string DataConnectionString => defaultConfiguration[nameof(DataConnectionString)] ?? AzuriteContainer.ConnectionString; public static string EventHubConnectionString => defaultConfiguration[nameof(EventHubConnectionString)]; public static string EventHubFullyQualifiedNamespace => defaultConfiguration[nameof(EventHubFullyQualifiedNamespace)]; public static string ZooKeeperConnectionString => defaultConfiguration[nameof(ZooKeeperConnectionString)]; diff --git a/test/TestInfrastructure/TestExtensions/TestExtensions.csproj b/test/TestInfrastructure/TestExtensions/TestExtensions.csproj index 7cf32e70ac6..cb714406a70 100644 --- a/test/TestInfrastructure/TestExtensions/TestExtensions.csproj +++ b/test/TestInfrastructure/TestExtensions/TestExtensions.csproj @@ -15,6 +15,7 @@ + diff --git a/test/TestInfrastructure/TestExtensions/TestUtils.cs b/test/TestInfrastructure/TestExtensions/TestUtils.cs index 3817f466bd9..233cc7888c6 100644 --- a/test/TestInfrastructure/TestExtensions/TestUtils.cs +++ b/test/TestInfrastructure/TestExtensions/TestUtils.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Net; using Orleans.Runtime; -using Orleans.TestingHost.Utils; using Xunit; using static TestExtensions.TestDefaultConfiguration; @@ -13,26 +12,27 @@ public class TestUtils public static void CheckForAzureStorage() { - if ((UseAadAuthentication && (TableEndpoint == null)) || - (!UseAadAuthentication && string.IsNullOrWhiteSpace(DataConnectionString))) + if (UseAadAuthentication) { - throw new SkipException("No connection string found. Skipping"); - } + if (TableEndpoint is null) + { + throw new SkipException("No connection string found. Skipping"); + } - bool usingLocalWAS = string.Equals(DataConnectionString, "UseDevelopmentStorage=true", StringComparison.OrdinalIgnoreCase); + return; + } - if (!usingLocalWAS) + // If a connection string is explicitly configured (e.g. real Azure), use it as-is. + if (!string.IsNullOrWhiteSpace(DataConnectionString) + && !string.Equals(DataConnectionString, "UseDevelopmentStorage=true", StringComparison.OrdinalIgnoreCase)) { - // Tests are using Azure Cloud Storage, not local WAS emulator. return; } - //Starts the storage emulator if not started already and it exists (i.e. is installed). - if (!StorageEmulator.TryStart()) + // Start Azurite via Testcontainers (or reuse an already-running instance). + if (!TestExtensions.AzuriteContainer.EnsureStartedAsync().GetAwaiter().GetResult()) { - string errorMsg = "Azure Storage Emulator could not be started."; - Console.WriteLine(errorMsg); - throw new SkipException(errorMsg); + throw new SkipException("Azurite container could not be started. Skipping."); } } From 9ee8ecd969f8b056a6b0b503a278291aa446a06c Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 10:35:52 +0200 Subject: [PATCH 02/12] Remove Azurite service container from Azure Storage CI job Azurite is now managed in-process by Testcontainers via AzuriteContainer. The Event Hubs job retains the Azurite service container because the Event Hubs emulator Docker container depends on it for blob/metadata storage, but its ORLEANSDATACONNECTIONSTRING is removed since the test process will detect the already-running Azurite on default ports. --- .github/workflows/ci.yml | 14 -------------- .../TestExtensions/AzuriteContainer.cs | 5 +---- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49dce58f4e4..7c970c895e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -257,13 +257,6 @@ jobs: matrix: framework: ["net8.0", "net10.0"] provider: ["AzureStorage"] - services: - azurite: - image: mcr.microsoft.com/azure-storage/azurite:latest - ports: - - 10000:10000 - - 10001:10001 - - 10002:10002 steps: - uses: actions/checkout@v4 - name: Setup .NET @@ -278,11 +271,6 @@ jobs: --logger "trx;LogFileName=test_results_${{ matrix.provider }}_${{ matrix.framework }}.trx" -- -parallel none -noshadow - env: -# [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Not a secret")] -# [SuppressMessage("Microsoft.Security", "CSCAN0090:ConfigFile", Justification="Not a secret")] -# [SuppressMessage("Microsoft.Security", "CSCAN0220:DefaultPasswordContexts", Justification="Not a secret")] - ORLEANSDATACONNECTIONSTRING: "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;" - name: Archive Test Results if: always() uses: actions/upload-artifact@v4 @@ -341,8 +329,6 @@ jobs: # [SuppressMessage("Microsoft.Security", "CSCAN0090:ConfigFile", Justification="Not a secret")] # [SuppressMessage("Microsoft.Security", "CSCAN0220:DefaultPasswordContexts", Justification="Not a secret")] ORLEANSEVENTHUBCONNECTIONSTRING: "Endpoint=sb://localhost;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;" -# [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Not a secret")] - ORLEANSDATACONNECTIONSTRING: "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;" - name: Clean up Event Hubs emulator if: always() run: docker rm -f eventhubs-emulator diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs index 187aa662ae6..3264304ab2b 100644 --- a/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs @@ -66,10 +66,7 @@ private static async Task StartAsync() .WithPortBinding(BlobPort, true) .WithPortBinding(QueuePort, true) .WithPortBinding(TablePort, true) - .WithWaitStrategy(Wait.ForUnixContainer() - .UntilPortIsAvailable(BlobPort) - .UntilPortIsAvailable(QueuePort) - .UntilPortIsAvailable(TablePort)) + .WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(BlobPort)) .Build(); await container.StartAsync(); From 2b0742440434d8a2e2de96a84c57b6716841b32b Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 10:55:52 +0200 Subject: [PATCH 03/12] Use official Testcontainers.Azurite module instead of manual container setup Replace manual ContainerBuilder configuration with AzuriteBuilder from the Testcontainers.Azurite NuGet package. This provides built-in image selection, port bindings, wait strategies, and a GetConnectionString() method. Also enables WithInMemoryPersistence() for faster test execution. --- Directory.Packages.props | 1 + .../TestExtensions/AzuriteContainer.cs | 63 +++---------------- .../TestExtensions/TestExtensions.csproj | 4 +- 3 files changed, 10 insertions(+), 58 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 21c418f7e0a..6d5b5408b44 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -105,6 +105,7 @@ + diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs index 3264304ab2b..52f6b36ae62 100644 --- a/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs @@ -1,6 +1,4 @@ -using System.Net.Sockets; -using DotNet.Testcontainers.Builders; -using DotNet.Testcontainers.Containers; +using Testcontainers.Azurite; namespace TestExtensions; @@ -10,15 +8,7 @@ namespace TestExtensions; /// public static class AzuriteContainer { - private const int BlobPort = 10000; - private const int QueuePort = 10001; - private const int TablePort = 10002; - - // Well-known Azurite development credentials. - private const string AccountName = "devstoreaccount1"; - private const string AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; - - private static IContainer _container; + private static Testcontainers.Azurite.AzuriteContainer _container; private static string _connectionString; private static readonly object _lock = new(); private static Task _startTask; @@ -30,9 +20,8 @@ public static class AzuriteContainer public static string ConnectionString => _connectionString; /// - /// Ensures Azurite is available. If the default ports are already reachable (e.g. an external - /// Azurite process or CI service container), the existing instance is reused. Otherwise, a - /// Testcontainer is started. + /// Ensures Azurite is available by starting an Azurite Testcontainer. + /// The container is started once and shared across all tests in the process. /// /// if Azurite is available; if it could not be started. public static Task EnsureStartedAsync() @@ -52,31 +41,15 @@ public static Task EnsureStartedAsync() private static async Task StartAsync() { - // First, check whether Azurite is already reachable on the default ports (e.g. CI service container or manual process). - if (IsPortReachable(BlobPort) && IsPortReachable(QueuePort) && IsPortReachable(TablePort)) - { - _connectionString = BuildConnectionString("127.0.0.1", BlobPort, QueuePort, TablePort); - return true; - } - try { - var container = new ContainerBuilder() - .WithImage("mcr.microsoft.com/azure-storage/azurite:latest") - .WithPortBinding(BlobPort, true) - .WithPortBinding(QueuePort, true) - .WithPortBinding(TablePort, true) - .WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(BlobPort)) + var container = new AzuriteBuilder() + .WithInMemoryPersistence() .Build(); await container.StartAsync(); - var host = container.Hostname; - var blobPort = container.GetMappedPublicPort(BlobPort); - var queuePort = container.GetMappedPublicPort(QueuePort); - var tablePort = container.GetMappedPublicPort(TablePort); - - _connectionString = BuildConnectionString(host, blobPort, queuePort, tablePort); + _connectionString = container.GetConnectionString(); _container = container; return true; } @@ -85,26 +58,4 @@ private static async Task StartAsync() return false; } } - - private static string BuildConnectionString(string host, int blobPort, int queuePort, int tablePort) - { - return $"DefaultEndpointsProtocol=http;AccountName={AccountName};AccountKey={AccountKey};" - + $"BlobEndpoint=http://{host}:{blobPort}/{AccountName};" - + $"QueueEndpoint=http://{host}:{queuePort}/{AccountName};" - + $"TableEndpoint=http://{host}:{tablePort}/{AccountName};"; - } - - private static bool IsPortReachable(int port) - { - try - { - using var client = new TcpClient(); - client.Connect("127.0.0.1", port); - return true; - } - catch (SocketException) - { - return false; - } - } } diff --git a/test/TestInfrastructure/TestExtensions/TestExtensions.csproj b/test/TestInfrastructure/TestExtensions/TestExtensions.csproj index cb714406a70..c8a2b2bc935 100644 --- a/test/TestInfrastructure/TestExtensions/TestExtensions.csproj +++ b/test/TestInfrastructure/TestExtensions/TestExtensions.csproj @@ -15,7 +15,7 @@ - + - + From 8d439060fc21a83fa4ebf103fc7f9fed96d6eb73 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 11:00:50 +0200 Subject: [PATCH 04/12] Rename AzuriteContainer to AzuriteContainerManager Resolves naming conflict with Testcontainers.Azurite.AzuriteContainer. --- NuGet.Config | 37 +++++++++++-------- src/Directory.Build.props | 2 + ...ontainer.cs => AzuriteContainerManager.cs} | 4 +- .../TestDefaultConfiguration.cs | 2 +- .../TestExtensions/TestUtils.cs | 2 +- 5 files changed, 27 insertions(+), 20 deletions(-) rename test/TestInfrastructure/TestExtensions/{AzuriteContainer.cs => AzuriteContainerManager.cs} (93%) diff --git a/NuGet.Config b/NuGet.Config index 814f4e8ca67..fa7fc9a74ea 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,25 +2,30 @@ + - - - - + - - - - - - - - - - - - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ead303c1fe8..cc8cc3ddd37 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -32,8 +32,10 @@ + diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs similarity index 93% rename from test/TestInfrastructure/TestExtensions/AzuriteContainer.cs rename to test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs index 52f6b36ae62..f624064cf60 100644 --- a/test/TestInfrastructure/TestExtensions/AzuriteContainer.cs +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs @@ -6,9 +6,9 @@ namespace TestExtensions; /// Manages a singleton Azurite container for Azure Storage integration tests. /// The container is lazily started on first use and shared across all tests in the process. /// -public static class AzuriteContainer +public static class AzuriteContainerManager { - private static Testcontainers.Azurite.AzuriteContainer _container; + private static AzuriteContainer _container; private static string _connectionString; private static readonly object _lock = new(); private static Task _startTask; diff --git a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs index 13ce8544ee5..525f7052d28 100644 --- a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs +++ b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs @@ -39,7 +39,7 @@ public static bool UseAadAuthentication public static Uri TableEndpoint => new Uri(defaultConfiguration[nameof(TableEndpoint)]); public static Uri DataBlobUri => new Uri(defaultConfiguration[nameof(DataBlobUri)]); public static Uri DataQueueUri => new Uri(defaultConfiguration[nameof(DataQueueUri)]); - public static string DataConnectionString => defaultConfiguration[nameof(DataConnectionString)] ?? AzuriteContainer.ConnectionString; + public static string DataConnectionString => defaultConfiguration[nameof(DataConnectionString)] ?? AzuriteContainerManager.ConnectionString; public static string EventHubConnectionString => defaultConfiguration[nameof(EventHubConnectionString)]; public static string EventHubFullyQualifiedNamespace => defaultConfiguration[nameof(EventHubFullyQualifiedNamespace)]; public static string ZooKeeperConnectionString => defaultConfiguration[nameof(ZooKeeperConnectionString)]; diff --git a/test/TestInfrastructure/TestExtensions/TestUtils.cs b/test/TestInfrastructure/TestExtensions/TestUtils.cs index 233cc7888c6..4f066c360a0 100644 --- a/test/TestInfrastructure/TestExtensions/TestUtils.cs +++ b/test/TestInfrastructure/TestExtensions/TestUtils.cs @@ -30,7 +30,7 @@ public static void CheckForAzureStorage() } // Start Azurite via Testcontainers (or reuse an already-running instance). - if (!TestExtensions.AzuriteContainer.EnsureStartedAsync().GetAwaiter().GetResult()) + if (!AzuriteContainerManager.EnsureStartedAsync().GetAwaiter().GetResult()) { throw new SkipException("Azurite container could not be started. Skipping."); } From fdbc124da05416e2c4f7c1c44e459850c475af7f Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 11:14:26 +0200 Subject: [PATCH 05/12] Remove UseAadAuthentication and simplify Azure test configuration Remove the AAD authentication support from test infrastructure since all Azure Storage tests now use the Azurite emulator via Testcontainers. - Remove UseAadAuthentication, TokenCredential, TableEndpoint, DataBlobUri, DataQueueUri, and EventHubFullyQualifiedNamespace from TestDefaultConfiguration - Simplify CheckForAzureStorage to only start the Azurite container - Simplify CheckForEventHub to only check connection string - Remove AAD branches from AzureStorageOperationOptionsExtensions, EventHubConfigurationExtensions, CosmosOptionsExtensions, JournalingAzureStorageTestConfiguration, and other test files - Remove unused Azure.Identity package from TestExtensions.csproj --- .../EventHubConfigurationExtensions.cs | 10 +---- .../Tester.Cosmos/CosmosOptionsExtensions.cs | 31 ++----------- .../AzureStorageOperationOptionsExtensions.cs | 44 +++---------------- .../Streaming/AQStreamingTests.cs | 14 +----- ...stAzureTableStorageStreamFailureHandler.cs | 10 +---- ...JournalingAzureStorageTestConfiguration.cs | 18 +------- .../TestDefaultConfiguration.cs | 33 +------------- .../TestExtensions/TestExtensions.csproj | 1 - .../TestExtensions/TestUtils.cs | 22 +--------- test/Tester/Forwarding/ShutdownSiloTests.cs | 5 +-- .../GeoClusterTests/BasicLogTestGrainTests.cs | 5 +-- 11 files changed, 18 insertions(+), 175 deletions(-) diff --git a/test/Extensions/ServiceBus.Tests/EventHubConfigurationExtensions.cs b/test/Extensions/ServiceBus.Tests/EventHubConfigurationExtensions.cs index 035122336ad..c1712d35773 100644 --- a/test/Extensions/ServiceBus.Tests/EventHubConfigurationExtensions.cs +++ b/test/Extensions/ServiceBus.Tests/EventHubConfigurationExtensions.cs @@ -1,4 +1,3 @@ -using Azure.Identity; using Orleans.Configuration; using Tester.AzureUtils; using TestExtensions; @@ -9,14 +8,7 @@ public static class EventHubConfigurationExtensions { public static EventHubOptions ConfigureTestDefaults(this EventHubOptions options, string eventHubName, string consumerGroup) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.ConfigureEventHubConnection(TestDefaultConfiguration.EventHubFullyQualifiedNamespace, eventHubName, consumerGroup, TestDefaultConfiguration.TokenCredential); - } - else - { - options.ConfigureEventHubConnection(TestDefaultConfiguration.EventHubConnectionString, eventHubName, consumerGroup); - } + options.ConfigureEventHubConnection(TestDefaultConfiguration.EventHubConnectionString, eventHubName, consumerGroup); return options; } diff --git a/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs b/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs index f3b5624367d..df94cba9e03 100644 --- a/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs +++ b/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs @@ -1,4 +1,3 @@ -using Azure.Identity; using Microsoft.Azure.Cosmos; using Orleans.Clustering.Cosmos; using Orleans.Persistence.Cosmos; @@ -11,43 +10,19 @@ public static class CosmosOptionsExtensions { public static void ConfigureTestDefaults(this CosmosClusteringOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.ConfigureCosmosClient(TestDefaultConfiguration.CosmosDBAccountEndpoint, TestDefaultConfiguration.TokenCredential); - } - else - { - options.ConfigureCosmosClient(GetCosmosClientUsingAccountKey()); - } - + options.ConfigureCosmosClient(GetCosmosClientUsingAccountKey()); options.IsResourceCreationEnabled = true; } public static void ConfigureTestDefaults(this CosmosGrainStorageOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.ConfigureCosmosClient(TestDefaultConfiguration.CosmosDBAccountEndpoint, TestDefaultConfiguration.TokenCredential); - } - else - { - options.ConfigureCosmosClient(GetCosmosClientUsingAccountKey()); - } - + options.ConfigureCosmosClient(GetCosmosClientUsingAccountKey()); options.IsResourceCreationEnabled = true; } public static void ConfigureTestDefaults(this CosmosReminderTableOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.ConfigureCosmosClient(TestDefaultConfiguration.CosmosDBAccountEndpoint, TestDefaultConfiguration.TokenCredential); - } - else - { - options.ConfigureCosmosClient(GetCosmosClientUsingAccountKey()); - } - + options.ConfigureCosmosClient(GetCosmosClientUsingAccountKey()); options.IsResourceCreationEnabled = true; } diff --git a/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs b/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs index 9fc2526ecf6..0e6b683bf9d 100644 --- a/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs +++ b/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs @@ -1,6 +1,4 @@ -using Azure.Core.Diagnostics; using Azure.Data.Tables; -using Azure.Identity; using Azure.Storage.Blobs; using TestExtensions; @@ -8,8 +6,6 @@ namespace Tester.AzureUtils { public static class AzureStorageOperationOptionsExtensions { - public static DefaultAzureCredential Credential = new DefaultAzureCredential(); - public static Orleans.Clustering.AzureStorage.AzureStorageOperationOptions ConfigureTestDefaults(this Orleans.Clustering.AzureStorage.AzureStorageOperationOptions options) { options.TableServiceClient = GetTableServiceClient(); @@ -19,9 +15,7 @@ public static Orleans.Clustering.AzureStorage.AzureStorageOperationOptions Confi public static TableServiceClient GetTableServiceClient() { - return TestDefaultConfiguration.UseAadAuthentication - ? new(TestDefaultConfiguration.TableEndpoint, TestDefaultConfiguration.TokenCredential) - : new(TestDefaultConfiguration.DataConnectionString); + return new(TestDefaultConfiguration.DataConnectionString); } public static Orleans.GrainDirectory.AzureStorage.AzureStorageOperationOptions ConfigureTestDefaults(this Orleans.GrainDirectory.AzureStorage.AzureStorageOperationOptions options) @@ -47,56 +41,28 @@ public static Orleans.Reminders.AzureStorage.AzureStorageOperationOptions Config public static Orleans.Configuration.AzureBlobStorageOptions ConfigureTestDefaults(this Orleans.Configuration.AzureBlobStorageOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.BlobServiceClient = new(TestDefaultConfiguration.DataBlobUri, TestDefaultConfiguration.TokenCredential); - } - else - { - options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); - } + options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); return options; } public static AzureStorageJobShardOptions ConfigureTestDefaults(this AzureStorageJobShardOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.BlobServiceClient = new(TestDefaultConfiguration.DataBlobUri, TestDefaultConfiguration.TokenCredential); - } - else - { - options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); - } + options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); return options; } public static Orleans.Configuration.AzureQueueOptions ConfigureTestDefaults(this Orleans.Configuration.AzureQueueOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.QueueServiceClient = new(TestDefaultConfiguration.DataQueueUri, TestDefaultConfiguration.TokenCredential); - } - else - { - options.QueueServiceClient = new(TestDefaultConfiguration.DataConnectionString); - } + options.QueueServiceClient = new(TestDefaultConfiguration.DataConnectionString); return options; } public static Orleans.Configuration.AzureBlobLeaseProviderOptions ConfigureTestDefaults(this Orleans.Configuration.AzureBlobLeaseProviderOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.BlobServiceClient = new(TestDefaultConfiguration.DataBlobUri, TestDefaultConfiguration.TokenCredential); - } - else - { - options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); - } + options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); return options; } diff --git a/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs b/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs index 0d1a773a3ef..cbbde487e10 100644 --- a/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs +++ b/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs @@ -49,19 +49,7 @@ void ConfigureStreaming(string option, string value) } ConfigureStreaming("ProviderType", "AzureQueueStorage"); - if (TestDefaultConfiguration.UseAadAuthentication) - { - cb.AddKeyedAzureQueueServiceClient(AzureQueueStreamProviderName, settings => - { - settings.ServiceUri = TestDefaultConfiguration.DataQueueUri; - settings.Credential = TestDefaultConfiguration.TokenCredential; - }); - ConfigureStreaming("ServiceKey", AzureQueueStreamProviderName); - } - else - { - ConfigureStreaming("ConnectionString", TestDefaultConfiguration.DataConnectionString); - } + ConfigureStreaming("ConnectionString", TestDefaultConfiguration.DataConnectionString); var names = AzureQueueUtilities.GenerateQueueNames(builder.Options.ClusterId, queueCount); for (var i = 0; i < names.Count; i++) diff --git a/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs b/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs index e216585f4be..4cf3b159df6 100644 --- a/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs +++ b/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs @@ -1,5 +1,4 @@ using Azure.Data.Tables; -using Azure.Identity; using Microsoft.Extensions.Logging.Abstractions; using Orleans.Persistence.AzureStorage; using Orleans.Providers.Streams.PersistentStreams; @@ -32,14 +31,7 @@ public static async Task DeleteAll() private static AzureTableDataManager GetDataManager() { var options = new AzureStorageOperationOptions { TableName = TableName }; - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.TableServiceClient = new(TestDefaultConfiguration.TableEndpoint, TestDefaultConfiguration.TokenCredential); - } - else - { - options.TableServiceClient = new(TestDefaultConfiguration.DataConnectionString); - } + options.TableServiceClient = new(TestDefaultConfiguration.DataConnectionString); return new AzureTableDataManager(options, NullLogger.Instance); } } diff --git a/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs b/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs index a67f8992867..07432b611a8 100644 --- a/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs +++ b/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs @@ -7,26 +7,12 @@ internal static class JournalingAzureStorageTestConfiguration { public static void CheckPreconditionsOrThrow() { - if (TestDefaultConfiguration.UseAadAuthentication) - { - Skip.If(string.IsNullOrEmpty(TestDefaultConfiguration.DataBlobUri.ToString()), "DataBlobUri is not set. Skipping test."); - } - else - { - Skip.If(string.IsNullOrEmpty(TestDefaultConfiguration.DataConnectionString), "DataConnectionString is not set. Skipping test."); - } + Skip.If(string.IsNullOrEmpty(TestDefaultConfiguration.DataConnectionString), "DataConnectionString is not set. Skipping test."); } public static AzureAppendBlobStateMachineStorageOptions ConfigureTestDefaults(this AzureAppendBlobStateMachineStorageOptions options) { - if (TestDefaultConfiguration.UseAadAuthentication) - { - options.ConfigureBlobServiceClient(TestDefaultConfiguration.DataBlobUri, TestDefaultConfiguration.TokenCredential); - } - else - { - options.ConfigureBlobServiceClient(TestDefaultConfiguration.DataConnectionString); - } + options.ConfigureBlobServiceClient(TestDefaultConfiguration.DataConnectionString); return options; } diff --git a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs index 525f7052d28..dc1351a862a 100644 --- a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs +++ b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs @@ -1,5 +1,3 @@ -using Azure.Core; -using Azure.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; @@ -25,23 +23,10 @@ public static void InitializeDefaults() } } - public static bool UseAadAuthentication - { - get - { - bool.TryParse(defaultConfiguration[nameof(UseAadAuthentication)], out var value); - return value; - } - } - public static string CosmosDBAccountEndpoint => defaultConfiguration[nameof(CosmosDBAccountEndpoint)]; public static string CosmosDBAccountKey => defaultConfiguration[nameof(CosmosDBAccountKey)]; - public static Uri TableEndpoint => new Uri(defaultConfiguration[nameof(TableEndpoint)]); - public static Uri DataBlobUri => new Uri(defaultConfiguration[nameof(DataBlobUri)]); - public static Uri DataQueueUri => new Uri(defaultConfiguration[nameof(DataQueueUri)]); - public static string DataConnectionString => defaultConfiguration[nameof(DataConnectionString)] ?? AzuriteContainerManager.ConnectionString; + public static string DataConnectionString => AzuriteContainerManager.ConnectionString; public static string EventHubConnectionString => defaultConfiguration[nameof(EventHubConnectionString)]; - public static string EventHubFullyQualifiedNamespace => defaultConfiguration[nameof(EventHubFullyQualifiedNamespace)]; public static string ZooKeeperConnectionString => defaultConfiguration[nameof(ZooKeeperConnectionString)]; public static string ConsulConnectionString => defaultConfiguration[nameof(ConsulConnectionString)]; public static string RedisConnectionString => defaultConfiguration[nameof(RedisConnectionString)]; @@ -52,22 +37,6 @@ public static bool UseAadAuthentication public static string DynamoDbAccessKey => defaultConfiguration[nameof(DynamoDbAccessKey)]; public static string DynamoDbSecretKey => defaultConfiguration[nameof(DynamoDbSecretKey)]; public static string SqsConnectionString => defaultConfiguration[nameof(SqsConnectionString)]; - public static TokenCredential TokenCredential - { - get - { - var systemAccessToken = Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN"); - if (!string.IsNullOrEmpty(systemAccessToken)) - { - // If running in an AzDo pipeline with a SYSTEM_ACCESSTOKEN available, let's try to use AzurePipelinesCredential - var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID"); - var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"); - var serviceConnectionId = Environment.GetEnvironmentVariable("SERVICE_CONNECTION_ID"); - return new AzurePipelinesCredential(tenantId, clientId, serviceConnectionId, systemAccessToken); - } - return new DefaultAzureCredential(); - } - } public static bool GetValue(string key, out string value) { diff --git a/test/TestInfrastructure/TestExtensions/TestExtensions.csproj b/test/TestInfrastructure/TestExtensions/TestExtensions.csproj index c8a2b2bc935..2c6eb0e60af 100644 --- a/test/TestInfrastructure/TestExtensions/TestExtensions.csproj +++ b/test/TestInfrastructure/TestExtensions/TestExtensions.csproj @@ -14,7 +14,6 @@ - diff --git a/test/TestInfrastructure/TestExtensions/TestUtils.cs b/test/TestInfrastructure/TestExtensions/TestUtils.cs index 4f066c360a0..66b063784d0 100644 --- a/test/TestInfrastructure/TestExtensions/TestUtils.cs +++ b/test/TestInfrastructure/TestExtensions/TestUtils.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -using System.Net; using Orleans.Runtime; +using TestExtensions; using Xunit; using static TestExtensions.TestDefaultConfiguration; @@ -12,23 +12,6 @@ public class TestUtils public static void CheckForAzureStorage() { - if (UseAadAuthentication) - { - if (TableEndpoint is null) - { - throw new SkipException("No connection string found. Skipping"); - } - - return; - } - - // If a connection string is explicitly configured (e.g. real Azure), use it as-is. - if (!string.IsNullOrWhiteSpace(DataConnectionString) - && !string.Equals(DataConnectionString, "UseDevelopmentStorage=true", StringComparison.OrdinalIgnoreCase)) - { - return; - } - // Start Azurite via Testcontainers (or reuse an already-running instance). if (!AzuriteContainerManager.EnsureStartedAsync().GetAwaiter().GetResult()) { @@ -38,8 +21,7 @@ public static void CheckForAzureStorage() public static void CheckForEventHub() { - if ((UseAadAuthentication && (EventHubFullyQualifiedNamespace == null)) || - (!UseAadAuthentication && string.IsNullOrWhiteSpace(EventHubConnectionString))) + if (string.IsNullOrWhiteSpace(EventHubConnectionString)) { throw new SkipException("No connection string found. Skipping"); } diff --git a/test/Tester/Forwarding/ShutdownSiloTests.cs b/test/Tester/Forwarding/ShutdownSiloTests.cs index 0a9ff5e26a1..a82e2819efc 100644 --- a/test/Tester/Forwarding/ShutdownSiloTests.cs +++ b/test/Tester/Forwarding/ShutdownSiloTests.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; using Azure.Data.Tables; -using Azure.Identity; using Orleans.Runtime.Placement; namespace Tester.Forwarding @@ -39,9 +38,7 @@ public void Configure(ISiloBuilder hostBuilder) private static TableServiceClient GetTableServiceClient() { - return TestDefaultConfiguration.UseAadAuthentication - ? new(TestDefaultConfiguration.TableEndpoint, TestDefaultConfiguration.TokenCredential) - : new(TestDefaultConfiguration.DataConnectionString); + return new(TestDefaultConfiguration.DataConnectionString); } } diff --git a/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs b/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs index a42f2c37688..aef1b77569f 100644 --- a/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs +++ b/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs @@ -7,7 +7,6 @@ using Orleans.Configuration; using Azure.Data.Tables; -using Azure.Identity; namespace Tests.GeoClusterTests { @@ -51,9 +50,7 @@ public void Configure(ISiloBuilder hostBuilder) private static TableServiceClient GetTableServiceClient() { - return TestDefaultConfiguration.UseAadAuthentication - ? new(TestDefaultConfiguration.TableEndpoint, TestDefaultConfiguration.TokenCredential) - : new(TestDefaultConfiguration.DataConnectionString); + return new(TestDefaultConfiguration.DataConnectionString); } } } From 7c4c8c28d253c680ea31856ba631e32a985a99a8 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 11:30:42 +0200 Subject: [PATCH 06/12] Replace DataConnectionString with AzuriteContainerManager.ConnectionString Remove the redundant DataConnectionString property from TestDefaultConfiguration and use AzuriteContainerManager.ConnectionString directly in all test files. --- test/Benchmarks/GrainStorage/GrainStorageBenchmark.cs | 6 +++--- test/Benchmarks/Transactions/TransactionBenchmark.cs | 2 +- .../Tester.Cosmos/CosmosOptionsExtensions.cs | 2 +- .../AzureStorageOperationOptionsExtensions.cs | 10 +++++----- .../TesterAzureUtils/StorageEmulatorUtilities.cs | 2 +- .../TesterAzureUtils/Streaming/AQStreamingTests.cs | 2 +- .../TestAzureTableStorageStreamFailureHandler.cs | 2 +- .../JournalingAzureStorageTestConfiguration.cs | 4 ++-- .../TestExtensions/TestDefaultConfiguration.cs | 1 - test/Tester/Forwarding/ShutdownSiloTests.cs | 2 +- .../GeoClusterTests/BasicLogTestGrainTests.cs | 8 ++++---- 11 files changed, 20 insertions(+), 21 deletions(-) diff --git a/test/Benchmarks/GrainStorage/GrainStorageBenchmark.cs b/test/Benchmarks/GrainStorage/GrainStorageBenchmark.cs index a87db9227ee..fa32841e87a 100644 --- a/test/Benchmarks/GrainStorage/GrainStorageBenchmark.cs +++ b/test/Benchmarks/GrainStorage/GrainStorageBenchmark.cs @@ -68,7 +68,7 @@ public void Configure(ISiloBuilder hostBuilder) { hostBuilder.AddAzureTableGrainStorageAsDefault(options => { - options.TableServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.TableServiceClient = new(AzuriteContainerManager.ConnectionString); }); } } @@ -79,7 +79,7 @@ public void Configure(ISiloBuilder hostBuilder) { hostBuilder.AddAzureBlobGrainStorageAsDefault(options => { - options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.BlobServiceClient = new(AzuriteContainerManager.ConnectionString); }); } } @@ -90,7 +90,7 @@ public void Configure(ISiloBuilder hostBuilder) { hostBuilder.AddAdoNetGrainStorageAsDefault(options => { - options.ConnectionString = TestDefaultConfiguration.DataConnectionString; + options.ConnectionString = AzuriteContainerManager.ConnectionString; }); } } diff --git a/test/Benchmarks/Transactions/TransactionBenchmark.cs b/test/Benchmarks/Transactions/TransactionBenchmark.cs index a9ee3d9b147..f804debc1e9 100644 --- a/test/Benchmarks/Transactions/TransactionBenchmark.cs +++ b/test/Benchmarks/Transactions/TransactionBenchmark.cs @@ -72,7 +72,7 @@ public void Configure(ISiloBuilder hostBuilder) { hostBuilder.AddAzureTableTransactionalStateStorageAsDefault(options => { - options.TableServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.TableServiceClient = new(AzuriteContainerManager.ConnectionString); }); } } diff --git a/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs b/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs index df94cba9e03..0c00f8c7ae1 100644 --- a/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs +++ b/test/Extensions/Tester.Cosmos/CosmosOptionsExtensions.cs @@ -48,4 +48,4 @@ private static Func> GetCosmosClientUs return new(new CosmosClient(TestDefaultConfiguration.CosmosDBAccountEndpoint, TestDefaultConfiguration.CosmosDBAccountKey, cosmosClientOptions)); }; } -} \ No newline at end of file +} diff --git a/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs b/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs index 0e6b683bf9d..a1715433531 100644 --- a/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs +++ b/test/Extensions/TesterAzureUtils/AzureStorageOperationOptionsExtensions.cs @@ -15,7 +15,7 @@ public static Orleans.Clustering.AzureStorage.AzureStorageOperationOptions Confi public static TableServiceClient GetTableServiceClient() { - return new(TestDefaultConfiguration.DataConnectionString); + return new(AzuriteContainerManager.ConnectionString); } public static Orleans.GrainDirectory.AzureStorage.AzureStorageOperationOptions ConfigureTestDefaults(this Orleans.GrainDirectory.AzureStorage.AzureStorageOperationOptions options) @@ -41,28 +41,28 @@ public static Orleans.Reminders.AzureStorage.AzureStorageOperationOptions Config public static Orleans.Configuration.AzureBlobStorageOptions ConfigureTestDefaults(this Orleans.Configuration.AzureBlobStorageOptions options) { - options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.BlobServiceClient = new(AzuriteContainerManager.ConnectionString); return options; } public static AzureStorageJobShardOptions ConfigureTestDefaults(this AzureStorageJobShardOptions options) { - options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.BlobServiceClient = new(AzuriteContainerManager.ConnectionString); return options; } public static Orleans.Configuration.AzureQueueOptions ConfigureTestDefaults(this Orleans.Configuration.AzureQueueOptions options) { - options.QueueServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.QueueServiceClient = new(AzuriteContainerManager.ConnectionString); return options; } public static Orleans.Configuration.AzureBlobLeaseProviderOptions ConfigureTestDefaults(this Orleans.Configuration.AzureBlobLeaseProviderOptions options) { - options.BlobServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.BlobServiceClient = new(AzuriteContainerManager.ConnectionString); return options; } diff --git a/test/Extensions/TesterAzureUtils/StorageEmulatorUtilities.cs b/test/Extensions/TesterAzureUtils/StorageEmulatorUtilities.cs index f20ceaf1f58..b77b2cd522d 100644 --- a/test/Extensions/TesterAzureUtils/StorageEmulatorUtilities.cs +++ b/test/Extensions/TesterAzureUtils/StorageEmulatorUtilities.cs @@ -7,7 +7,7 @@ public static class StorageEmulatorUtilities { public static void EnsureEmulatorIsNotUsed() { - if (TestDefaultConfiguration.DataConnectionString is { Length: > 0 } connectionString + if (AzuriteContainerManager.ConnectionString is { Length: > 0 } connectionString && (connectionString.Contains("UseDevelopmentStorage", StringComparison.OrdinalIgnoreCase) || connectionString.Contains("devstoreaccount", StringComparison.OrdinalIgnoreCase))) { diff --git a/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs b/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs index cbbde487e10..d6a89d79194 100644 --- a/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs +++ b/test/Extensions/TesterAzureUtils/Streaming/AQStreamingTests.cs @@ -49,7 +49,7 @@ void ConfigureStreaming(string option, string value) } ConfigureStreaming("ProviderType", "AzureQueueStorage"); - ConfigureStreaming("ConnectionString", TestDefaultConfiguration.DataConnectionString); + ConfigureStreaming("ConnectionString", AzuriteContainerManager.ConnectionString); var names = AzureQueueUtilities.GenerateQueueNames(builder.Options.ClusterId, queueCount); for (var i = 0; i < names.Count; i++) diff --git a/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs b/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs index 4cf3b159df6..4b1dce904cf 100644 --- a/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs +++ b/test/Extensions/TesterAzureUtils/Streaming/TestAzureTableStorageStreamFailureHandler.cs @@ -31,7 +31,7 @@ public static async Task DeleteAll() private static AzureTableDataManager GetDataManager() { var options = new AzureStorageOperationOptions { TableName = TableName }; - options.TableServiceClient = new(TestDefaultConfiguration.DataConnectionString); + options.TableServiceClient = new(AzuriteContainerManager.ConnectionString); return new AzureTableDataManager(options, NullLogger.Instance); } } diff --git a/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs b/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs index 07432b611a8..ce1a052f889 100644 --- a/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs +++ b/test/Orleans.Journaling.Tests/JournalingAzureStorageTestConfiguration.cs @@ -7,12 +7,12 @@ internal static class JournalingAzureStorageTestConfiguration { public static void CheckPreconditionsOrThrow() { - Skip.If(string.IsNullOrEmpty(TestDefaultConfiguration.DataConnectionString), "DataConnectionString is not set. Skipping test."); + Skip.If(string.IsNullOrEmpty(AzuriteContainerManager.ConnectionString), "DataConnectionString is not set. Skipping test."); } public static AzureAppendBlobStateMachineStorageOptions ConfigureTestDefaults(this AzureAppendBlobStateMachineStorageOptions options) { - options.ConfigureBlobServiceClient(TestDefaultConfiguration.DataConnectionString); + options.ConfigureBlobServiceClient(AzuriteContainerManager.ConnectionString); return options; } diff --git a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs index dc1351a862a..ba3ab80d1a7 100644 --- a/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs +++ b/test/TestInfrastructure/TestExtensions/TestDefaultConfiguration.cs @@ -25,7 +25,6 @@ public static void InitializeDefaults() public static string CosmosDBAccountEndpoint => defaultConfiguration[nameof(CosmosDBAccountEndpoint)]; public static string CosmosDBAccountKey => defaultConfiguration[nameof(CosmosDBAccountKey)]; - public static string DataConnectionString => AzuriteContainerManager.ConnectionString; public static string EventHubConnectionString => defaultConfiguration[nameof(EventHubConnectionString)]; public static string ZooKeeperConnectionString => defaultConfiguration[nameof(ZooKeeperConnectionString)]; public static string ConsulConnectionString => defaultConfiguration[nameof(ConsulConnectionString)]; diff --git a/test/Tester/Forwarding/ShutdownSiloTests.cs b/test/Tester/Forwarding/ShutdownSiloTests.cs index a82e2819efc..7bc9f31a677 100644 --- a/test/Tester/Forwarding/ShutdownSiloTests.cs +++ b/test/Tester/Forwarding/ShutdownSiloTests.cs @@ -38,7 +38,7 @@ public void Configure(ISiloBuilder hostBuilder) private static TableServiceClient GetTableServiceClient() { - return new(TestDefaultConfiguration.DataConnectionString); + return new(AzuriteContainerManager.ConnectionString); } } diff --git a/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs b/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs index aef1b77569f..64542b566da 100644 --- a/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs +++ b/test/TesterInternal/GeoClusterTests/BasicLogTestGrainTests.cs @@ -45,16 +45,16 @@ public void Configure(ISiloBuilder hostBuilder) { options.TableServiceClient = GetTableServiceClient(); })) - .AddMemoryGrainStorage("MemoryStore"); + .AddMemoryGrainStorage("MemoryStore"); } private static TableServiceClient GetTableServiceClient() { - return new(TestDefaultConfiguration.DataConnectionString); + return new(AzuriteContainerManager.ConnectionString); } } } - + public BasicLogTestGrainTests(Fixture fixture) { this.fixture = fixture; @@ -102,7 +102,7 @@ private async Task DoBasicLogTestGrainTest(string grainClass, int phases = 100) private async Task ThreeCheckers(string grainClass, int phases) { - // Global + // Global async Task checker1() { int x = GetRandom(); From ac5d9c05fee0878e79f178152e1875bff39223c1 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 11:35:37 +0200 Subject: [PATCH 07/12] remove local hacks --- NuGet.Config | 37 ++++++++++++++++--------------------- src/Directory.Build.props | 2 -- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index fa7fc9a74ea..814f4e8ca67 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,30 +2,25 @@ - - + + + + - + + + + + + + + + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cc8cc3ddd37..ead303c1fe8 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -32,10 +32,8 @@ - From 45a2b7365341f1da90b9a9e3d9f8b4f91e0d0eb0 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 11:42:30 +0200 Subject: [PATCH 08/12] nit --- .../TestExtensions/AzuriteContainerManager.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs index f624064cf60..3d4e8328656 100644 --- a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs @@ -43,9 +43,7 @@ private static async Task StartAsync() { try { - var container = new AzuriteBuilder() - .WithInMemoryPersistence() - .Build(); + var container = new AzuriteBuilder().Build(); await container.StartAsync(); From 5f95a2252a7ba725ece331d34c88695396bdd99b Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 11:43:48 +0200 Subject: [PATCH 09/12] Refactor AzuriteContainerManager to use Lazy pattern like ConsulTestUtils Use a Lazy for thread-safe singleton initialization instead of manual lock and Task caching. Add synchronous EnsureStarted() with SkipException, matching the ConsulTestUtils pattern. --- .../TestExtensions/AzuriteContainerManager.cs | 56 +++++++++---------- .../TestExtensions/TestUtils.cs | 6 +- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs index 3d4e8328656..138c5ce8a84 100644 --- a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs @@ -1,4 +1,5 @@ using Testcontainers.Azurite; +using Xunit; namespace TestExtensions; @@ -8,50 +9,49 @@ namespace TestExtensions; /// public static class AzuriteContainerManager { - private static AzuriteContainer _container; - private static string _connectionString; - private static readonly object _lock = new(); - private static Task _startTask; + private static readonly AzuriteContainer _container = new AzuriteBuilder().Build(); /// /// Gets the connection string for the running Azurite container. - /// Returns if the container has not been started. /// - public static string ConnectionString => _connectionString; - - /// - /// Ensures Azurite is available by starting an Azurite Testcontainer. - /// The container is started once and shared across all tests in the process. - /// - /// if Azurite is available; if it could not be started. - public static Task EnsureStartedAsync() + public static string ConnectionString { - if (_startTask is not null) + get { - return _startTask; + EnsureStarted(); + return _container.GetConnectionString(); } + } - lock (_lock) - { - _startTask ??= StartAsync(); - } + private static readonly Lazy _ensureStartedLazy = new(() => EnsureStartedAsync().Result); - return _startTask; + /// + /// Ensures Azurite is available by starting the container if not already running. + /// + /// Thrown if the container could not be started. + public static void EnsureStarted() + { + if (!_ensureStartedLazy.Value) + throw new SkipException("Azurite container could not be started. Skipping."); } - private static async Task StartAsync() + /// + /// Ensures Azurite is available by starting an Azurite Testcontainer. + /// The container is started once and shared across all tests in the process. + /// + /// if Azurite is available; if it could not be started. + public static async Task EnsureStartedAsync() { try { - var container = new AzuriteBuilder().Build(); - - await container.StartAsync(); - - _connectionString = container.GetConnectionString(); - _container = container; + await _container.StartAsync(); return true; } - catch + catch (HttpRequestException) + { + return false; + } + catch (OperationCanceledException) { return false; } diff --git a/test/TestInfrastructure/TestExtensions/TestUtils.cs b/test/TestInfrastructure/TestExtensions/TestUtils.cs index 66b063784d0..7b3fdce9d9f 100644 --- a/test/TestInfrastructure/TestExtensions/TestUtils.cs +++ b/test/TestInfrastructure/TestExtensions/TestUtils.cs @@ -12,11 +12,7 @@ public class TestUtils public static void CheckForAzureStorage() { - // Start Azurite via Testcontainers (or reuse an already-running instance). - if (!AzuriteContainerManager.EnsureStartedAsync().GetAwaiter().GetResult()) - { - throw new SkipException("Azurite container could not be started. Skipping."); - } + AzuriteContainerManager.EnsureStarted(); } public static void CheckForEventHub() From d08e7c24a274348227c94dfbc569a923b75be678 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 11:58:43 +0200 Subject: [PATCH 10/12] Update AzuriteContainerManager to specify Azurite image in container initialization --- .../TestExtensions/AzuriteContainerManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs index 138c5ce8a84..6f9d3565ba8 100644 --- a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs @@ -9,7 +9,9 @@ namespace TestExtensions; /// public static class AzuriteContainerManager { - private static readonly AzuriteContainer _container = new AzuriteBuilder().Build(); + private static readonly AzuriteContainer _container = new AzuriteBuilder() + .WithImage("mcr.microsoft.com/azure-storage/azurite:3.35.0") + .Build(); /// /// Gets the connection string for the running Azurite container. From 93996c03ec73c4fd51c13dbc215b305ebaae1393 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 12:20:21 +0200 Subject: [PATCH 11/12] Fix standalone silo tests by sharing Azurite connection string via env var Tests using StandaloneSiloHandle spawn silos in separate processes. Each process was starting its own Azurite container, so silos registered gateways in a different container than the one the client queried. Propagate the connection string via ORLEANS_AZURITE_CONNECTION_STRING environment variable so child processes reuse the same container. --- .../TestExtensions/AzuriteContainerManager.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs index 6f9d3565ba8..d37028918f8 100644 --- a/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs +++ b/test/TestInfrastructure/TestExtensions/AzuriteContainerManager.cs @@ -6,20 +6,30 @@ namespace TestExtensions; /// /// Manages a singleton Azurite container for Azure Storage integration tests. /// The container is lazily started on first use and shared across all tests in the process. +/// When running standalone silos (separate processes), the connection string is propagated +/// via environment variable so child processes reuse the same container. /// public static class AzuriteContainerManager { + private const string ConnectionStringEnvVar = "ORLEANS_AZURITE_CONNECTION_STRING"; + private static readonly AzuriteContainer _container = new AzuriteBuilder() .WithImage("mcr.microsoft.com/azure-storage/azurite:3.35.0") .Build(); /// /// Gets the connection string for the running Azurite container. + /// If running in a child process (e.g. standalone silo), returns the connection string + /// propagated via environment variable without starting a new container. /// public static string ConnectionString { get { + var envConnectionString = Environment.GetEnvironmentVariable(ConnectionStringEnvVar); + if (!string.IsNullOrEmpty(envConnectionString)) + return envConnectionString; + EnsureStarted(); return _container.GetConnectionString(); } @@ -29,10 +39,15 @@ public static string ConnectionString /// /// Ensures Azurite is available by starting the container if not already running. + /// If the connection string is already available via environment variable (e.g. in a child process), + /// this method is a no-op. /// /// Thrown if the container could not be started. public static void EnsureStarted() { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(ConnectionStringEnvVar))) + return; + if (!_ensureStartedLazy.Value) throw new SkipException("Azurite container could not be started. Skipping."); } @@ -40,6 +55,7 @@ public static void EnsureStarted() /// /// Ensures Azurite is available by starting an Azurite Testcontainer. /// The container is started once and shared across all tests in the process. + /// The connection string is propagated to child processes via environment variable. /// /// if Azurite is available; if it could not be started. public static async Task EnsureStartedAsync() @@ -47,6 +63,7 @@ public static async Task EnsureStartedAsync() try { await _container.StartAsync(); + Environment.SetEnvironmentVariable(ConnectionStringEnvVar, _container.GetConnectionString()); return true; } catch (HttpRequestException) From 32c086bb4448bbd01b7a911c958da64d3d552f2b Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 16 Feb 2026 14:01:01 +0200 Subject: [PATCH 12/12] run azure storage tests on dedicated workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c970c895e1..37a6ce9fcfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -581,7 +581,7 @@ jobs: - name: Test run: dotnet test --framework ${{ matrix.framework }} - --filter "Category=${{ matrix.suite }}&Category!=Consul" + --filter "Category=${{ matrix.suite }}&Category!=Consul&Category!=AzureStorage" --blame-hang-timeout 10m --logger "trx;LogFileName=test_results_${{ matrix.suite }}_${{ matrix.framework }}.trx" --