From 8e0f6d5bd114f6d7a60f99d6c4b261600d44b04d Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Sat, 7 Dec 2024 14:42:41 +0100 Subject: [PATCH] feat: Add Azure Service Bus module (#1309) --- .github/workflows/cicd.yml | 1 + Directory.Packages.props | 1 + Testcontainers.sln | 14 ++ docs/modules/index.md | 89 +++++------ src/Testcontainers.ServiceBus/.editorconfig | 1 + .../ServiceBusBuilder.cs | 143 ++++++++++++++++++ .../ServiceBusConfiguration.cs | 61 ++++++++ .../ServiceBusContainer.cs | 68 +++++++++ .../Testcontainers.ServiceBus.csproj | 13 ++ src/Testcontainers.ServiceBus/Usings.cs | 14 ++ .../Containers/IDatabaseContainer.cs | 2 +- .../.editorconfig | 1 + .../ServiceBusContainerTest.cs | 50 ++++++ .../Testcontainers.ServiceBus.Tests.csproj | 18 +++ .../Testcontainers.ServiceBus.Tests/Usings.cs | 5 + 15 files changed, 436 insertions(+), 45 deletions(-) create mode 100644 src/Testcontainers.ServiceBus/.editorconfig create mode 100644 src/Testcontainers.ServiceBus/ServiceBusBuilder.cs create mode 100644 src/Testcontainers.ServiceBus/ServiceBusConfiguration.cs create mode 100644 src/Testcontainers.ServiceBus/ServiceBusContainer.cs create mode 100644 src/Testcontainers.ServiceBus/Testcontainers.ServiceBus.csproj create mode 100644 src/Testcontainers.ServiceBus/Usings.cs create mode 100644 tests/Testcontainers.ServiceBus.Tests/.editorconfig create mode 100644 tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs create mode 100644 tests/Testcontainers.ServiceBus.Tests/Testcontainers.ServiceBus.Tests.csproj create mode 100644 tests/Testcontainers.ServiceBus.Tests/Usings.cs diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 036861069..f2e1e531b 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -78,6 +78,7 @@ jobs: { name: "Testcontainers.RavenDb", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.Redis", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.Redpanda", runs-on: "ubuntu-22.04" }, + { name: "Testcontainers.ServiceBus", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.WebDriver", runs-on: "ubuntu-22.04" }, { name: "Testcontainers.Xunit", runs-on: "ubuntu-22.04" } ] diff --git a/Directory.Packages.props b/Directory.Packages.props index e0c04516b..27e990a97 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -31,6 +31,7 @@ + diff --git a/Testcontainers.sln b/Testcontainers.sln index 1caef1ce5..58609127f 100644 --- a/Testcontainers.sln +++ b/Testcontainers.sln @@ -95,6 +95,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redis", "src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redpanda", "src\Testcontainers.Redpanda\Testcontainers.Redpanda.csproj", "{45D6F69C-4D87-4130-AA90-0DB2F7460DAE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus", "src\Testcontainers.ServiceBus\Testcontainers.ServiceBus.csproj", "{2E39E532-B81E-4B48-A004-FAE18EDF9E79}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver", "src\Testcontainers.WebDriver\Testcontainers.WebDriver.csproj", "{64A87DE5-29B0-4A54-9E74-560484D8C7C0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Xunit", "src\Testcontainers.Xunit\Testcontainers.Xunit.csproj", "{380BB29B-F556-404D-B13B-CA250599C565}" @@ -195,6 +197,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redpanda.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ResourceReaper.Tests", "tests\Testcontainers.ResourceReaper.Tests\Testcontainers.ResourceReaper.Tests.csproj", "{9E8E6AA5-65D1-498F-BEAB-BA34723A0050}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus.Tests", "tests\Testcontainers.ServiceBus.Tests\Testcontainers.ServiceBus.Tests.csproj", "{232DD918-46ED-4BA8-B383-1A9146D83064}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tests\Testcontainers.Tests\Testcontainers.Tests.csproj", "{27CDB869-A150-4593-958F-6F26E5391E7C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver.Tests", "tests\Testcontainers.WebDriver.Tests\Testcontainers.WebDriver.Tests.csproj", "{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}" @@ -374,6 +378,10 @@ Global {45D6F69C-4D87-4130-AA90-0DB2F7460DAE}.Debug|Any CPU.Build.0 = Debug|Any CPU {45D6F69C-4D87-4130-AA90-0DB2F7460DAE}.Release|Any CPU.ActiveCfg = Release|Any CPU {45D6F69C-4D87-4130-AA90-0DB2F7460DAE}.Release|Any CPU.Build.0 = Release|Any CPU + {2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Release|Any CPU.Build.0 = Release|Any CPU {64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -574,6 +582,10 @@ Global {9E8E6AA5-65D1-498F-BEAB-BA34723A0050}.Debug|Any CPU.Build.0 = Debug|Any CPU {9E8E6AA5-65D1-498F-BEAB-BA34723A0050}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E8E6AA5-65D1-498F-BEAB-BA34723A0050}.Release|Any CPU.Build.0 = Release|Any CPU + {232DD918-46ED-4BA8-B383-1A9146D83064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {232DD918-46ED-4BA8-B383-1A9146D83064}.Debug|Any CPU.Build.0 = Debug|Any CPU + {232DD918-46ED-4BA8-B383-1A9146D83064}.Release|Any CPU.ActiveCfg = Release|Any CPU + {232DD918-46ED-4BA8-B383-1A9146D83064}.Release|Any CPU.Build.0 = Release|Any CPU {27CDB869-A150-4593-958F-6F26E5391E7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27CDB869-A150-4593-958F-6F26E5391E7C}.Debug|Any CPU.Build.0 = Debug|Any CPU {27CDB869-A150-4593-958F-6F26E5391E7C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -629,6 +641,7 @@ Global {F6394475-D6F1-46E2-81BF-4BA78A40B878} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {BFDA179A-40EB-4CEB-B8E9-0DF32C65E2C5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {45D6F69C-4D87-4130-AA90-0DB2F7460DAE} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} + {2E39E532-B81E-4B48-A004-FAE18EDF9E79} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {64A87DE5-29B0-4A54-9E74-560484D8C7C0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {380BB29B-F556-404D-B13B-CA250599C565} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {84911C93-C2A9-46E9-AE5E-D567306589E5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} @@ -679,6 +692,7 @@ Global {31EE94A0-E721-4073-B6F1-DD912D004DEF} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {867BD04E-4670-4FBA-98D5-9F83220E6DFB} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {9E8E6AA5-65D1-498F-BEAB-BA34723A0050} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} + {232DD918-46ED-4BA8-B383-1A9146D83064} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {E901DF14-6F05-4FC2-825A-3055FAD33561} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} diff --git a/docs/modules/index.md b/docs/modules/index.md index cec9c0e32..48a75587b 100644 --- a/docs/modules/index.md +++ b/docs/modules/index.md @@ -19,50 +19,51 @@ await moduleNameContainer.StartAsync(); We will add module-specific documentations soon. -| Module | Image | NuGet | Source | -|------------------|---------------------------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| -| ActiveMQ Artemis | `apache/activemq-artemis:2.31.2` | [NuGet](https://www.nuget.org/packages/Testcontainers.ActiveMq) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ActiveMq) | -| ArangoDB | `arangodb:3.11.5` | [NuGet](https://www.nuget.org/packages/Testcontainers.ArangoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ArangoDb) | -| Azure Cosmos DB | `mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.CosmosDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CosmosDb) | -| Azurite | `mcr.microsoft.com/azure-storage/azurite:3.24.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Azurite) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Azurite) | -| BigQuery | `ghcr.io/goccy/bigquery-emulator:0.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.BigQuery) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.BigQuery) | -| Bigtable | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.Bigtable) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Bigtable) | -| ClickHouse | `clickhouse/clickhouse-server:23.6-alpine` | [NuGet](https://www.nuget.org/packages/Testcontainers.ClickHouse) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ClickHouse) | -| CockroachDB | `cockroachdb:23.1.13` | [NuGet](https://www.nuget.org/packages/Testcontainers.CockroachDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CockroachDb) | -| Consul | `consul:1.15` | [NuGet](https://www.nuget.org/packages/Testcontainers.Consul) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Consul) | -| Couchbase | `couchbase:community-7.0.2` | [NuGet](https://www.nuget.org/packages/Testcontainers.Couchbase) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Couchbase) | -| CouchDB | `couchdb:3.3` | [NuGet](https://www.nuget.org/packages/Testcontainers.CouchDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CouchDb) | -| DynamoDB | `amazon/dynamodb-local:1.21.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.DynamoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.DynamoDb) | -| Elasticsearch | `elasticsearch:8.6.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.Elasticsearch) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Elasticsearch) | -| EventStoreDB | `eventstore/eventstore:22.10.1-buster-slim` | [NuGet](https://www.nuget.org/packages/Testcontainers.EventStoreDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.EventStoreDb) | -| FakeGcsServer | `fsouza/fake-gcs-server:1.47` | [NuGet](https://www.nuget.org/packages/Testcontainers.FakeGcsServer) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.FakeGcsServer) | -| Firebird | `jacobalberty/firebird:v4.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.FirebirdSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.FirebirdSql) | -| Firestore | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.Firestore) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Firestore) | -| InfluxDB | `influxdb:2.7` | [NuGet](https://www.nuget.org/packages/Testcontainers.InfluxDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.InfluxDb) | -| JanusGraph | `janusgraph/janusgraph:1.0.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.JanusGraph) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.JanusGraph) | -| K3s | `rancher/k3s:v1.26.2-k3s1` | [NuGet](https://www.nuget.org/packages/Testcontainers.K3s) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.K3s) | -| Kafka | `confluentinc/cp-kafka:6.1.9` | [NuGet](https://www.nuget.org/packages/Testcontainers.Kafka) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Kafka) | -| Keycloak | `quay.io/keycloak/keycloak:21.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.Keycloak) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Keycloak) | -| Kusto emulator | `mcr.microsoft.com/azuredataexplorer/kustainer-linux:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Kusto) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Kusto) | -| LocalStack | `localstack/localstack:2.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.LocalStack) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.LocalStack) | -| MariaDB | `mariadb:10.10` | [NuGet](https://www.nuget.org/packages/Testcontainers.MariaDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MariaDb) | -| Milvus | `milvusdb/milvus:v2.3.10` | [NuGet](https://www.nuget.org/packages/Testcontainers.Milvus) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Milvus) | -| MinIO | `minio/minio:RELEASE.2023-01-31T02-24-19Z` | [NuGet](https://www.nuget.org/packages/Testcontainers.Minio) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Minio) | -| MongoDB | `mongo:6.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MongoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MongoDb) | -| MySQL | `mysql:8.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MySql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MySql) | -| NATS | `nats:2.9` | [NuGet](https://www.nuget.org/packages/Testcontainers.Nats) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Nats) | -| Neo4j | `neo4j:5.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.Neo4j) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Neo4j) | -| Oracle | `gvenzl/oracle-xe:21.3.0-slim-faststart` | [NuGet](https://www.nuget.org/packages/Testcontainers.Oracle) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Oracle) | -| Papercut | `changemakerstudiosus/papercut-smtp:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Papercut) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Papercut) | -| PostgreSQL | `postgres:15.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.PostgreSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PostgreSql) | -| PubSub | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.PubSub) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PubSub) | -| Pulsar | `apachepulsar/pulsar:3.0.6` | [NuGet](https://www.nuget.org/packages/Testcontainers.Pulsar) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Pulsar) | -| RabbitMQ | `rabbitmq:3.11` | [NuGet](https://www.nuget.org/packages/Testcontainers.RabbitMq) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.RabbitMq) | -| RavenDB | `ravendb/ravendb:5.4-ubuntu-latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.RavenDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.RavenDb) | -| Redis | `redis:7.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Redis) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Redis) | -| Redpanda | `docker.redpanda.com/redpandadata/redpanda:v22.2.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.Redpanda) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Redpanda) | -| SQL Server | `mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04` | [NuGet](https://www.nuget.org/packages/Testcontainers.MsSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MsSql) | -| WebDriver | `selenium/standalone-chrome:110.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.WebDriver) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.WebDriver) | +| Module | Image | NuGet | Source | +|-------------------|---------------------------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| ActiveMQ Artemis | `apache/activemq-artemis:2.31.2` | [NuGet](https://www.nuget.org/packages/Testcontainers.ActiveMq) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ActiveMq) | +| ArangoDB | `arangodb:3.11.5` | [NuGet](https://www.nuget.org/packages/Testcontainers.ArangoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ArangoDb) | +| Azure Cosmos DB | `mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.CosmosDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CosmosDb) | +| Azure Service Bus | `mcr.microsoft.com/azure-messaging/servicebus-emulator:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.ServiceBus) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ServiceBus) | +| Azurite | `mcr.microsoft.com/azure-storage/azurite:3.24.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Azurite) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Azurite) | +| BigQuery | `ghcr.io/goccy/bigquery-emulator:0.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.BigQuery) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.BigQuery) | +| Bigtable | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.Bigtable) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Bigtable) | +| ClickHouse | `clickhouse/clickhouse-server:23.6-alpine` | [NuGet](https://www.nuget.org/packages/Testcontainers.ClickHouse) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ClickHouse) | +| CockroachDB | `cockroachdb:23.1.13` | [NuGet](https://www.nuget.org/packages/Testcontainers.CockroachDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CockroachDb) | +| Consul | `consul:1.15` | [NuGet](https://www.nuget.org/packages/Testcontainers.Consul) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Consul) | +| Couchbase | `couchbase:community-7.0.2` | [NuGet](https://www.nuget.org/packages/Testcontainers.Couchbase) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Couchbase) | +| CouchDB | `couchdb:3.3` | [NuGet](https://www.nuget.org/packages/Testcontainers.CouchDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CouchDb) | +| DynamoDB | `amazon/dynamodb-local:1.21.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.DynamoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.DynamoDb) | +| Elasticsearch | `elasticsearch:8.6.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.Elasticsearch) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Elasticsearch) | +| EventStoreDB | `eventstore/eventstore:22.10.1-buster-slim` | [NuGet](https://www.nuget.org/packages/Testcontainers.EventStoreDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.EventStoreDb) | +| FakeGcsServer | `fsouza/fake-gcs-server:1.47` | [NuGet](https://www.nuget.org/packages/Testcontainers.FakeGcsServer) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.FakeGcsServer) | +| Firebird | `jacobalberty/firebird:v4.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.FirebirdSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.FirebirdSql) | +| Firestore | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.Firestore) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Firestore) | +| InfluxDB | `influxdb:2.7` | [NuGet](https://www.nuget.org/packages/Testcontainers.InfluxDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.InfluxDb) | +| JanusGraph | `janusgraph/janusgraph:1.0.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.JanusGraph) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.JanusGraph) | +| K3s | `rancher/k3s:v1.26.2-k3s1` | [NuGet](https://www.nuget.org/packages/Testcontainers.K3s) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.K3s) | +| Kafka | `confluentinc/cp-kafka:6.1.9` | [NuGet](https://www.nuget.org/packages/Testcontainers.Kafka) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Kafka) | +| Keycloak | `quay.io/keycloak/keycloak:21.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.Keycloak) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Keycloak) | +| Kusto emulator | `mcr.microsoft.com/azuredataexplorer/kustainer-linux:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Kusto) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Kusto) | +| LocalStack | `localstack/localstack:2.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.LocalStack) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.LocalStack) | +| MariaDB | `mariadb:10.10` | [NuGet](https://www.nuget.org/packages/Testcontainers.MariaDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MariaDb) | +| Milvus | `milvusdb/milvus:v2.3.10` | [NuGet](https://www.nuget.org/packages/Testcontainers.Milvus) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Milvus) | +| MinIO | `minio/minio:RELEASE.2023-01-31T02-24-19Z` | [NuGet](https://www.nuget.org/packages/Testcontainers.Minio) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Minio) | +| MongoDB | `mongo:6.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MongoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MongoDb) | +| MySQL | `mysql:8.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MySql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MySql) | +| NATS | `nats:2.9` | [NuGet](https://www.nuget.org/packages/Testcontainers.Nats) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Nats) | +| Neo4j | `neo4j:5.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.Neo4j) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Neo4j) | +| Oracle | `gvenzl/oracle-xe:21.3.0-slim-faststart` | [NuGet](https://www.nuget.org/packages/Testcontainers.Oracle) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Oracle) | +| Papercut | `changemakerstudiosus/papercut-smtp:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Papercut) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Papercut) | +| PostgreSQL | `postgres:15.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.PostgreSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PostgreSql) | +| PubSub | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.PubSub) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PubSub) | +| Pulsar | `apachepulsar/pulsar:3.0.6` | [NuGet](https://www.nuget.org/packages/Testcontainers.Pulsar) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Pulsar) | +| RabbitMQ | `rabbitmq:3.11` | [NuGet](https://www.nuget.org/packages/Testcontainers.RabbitMq) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.RabbitMq) | +| RavenDB | `ravendb/ravendb:5.4-ubuntu-latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.RavenDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.RavenDb) | +| Redis | `redis:7.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Redis) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Redis) | +| Redpanda | `docker.redpanda.com/redpandadata/redpanda:v22.2.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.Redpanda) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Redpanda) | +| SQL Server | `mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04` | [NuGet](https://www.nuget.org/packages/Testcontainers.MsSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MsSql) | +| WebDriver | `selenium/standalone-chrome:110.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.WebDriver) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.WebDriver) | ## Implement a module diff --git a/src/Testcontainers.ServiceBus/.editorconfig b/src/Testcontainers.ServiceBus/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/src/Testcontainers.ServiceBus/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/src/Testcontainers.ServiceBus/ServiceBusBuilder.cs b/src/Testcontainers.ServiceBus/ServiceBusBuilder.cs new file mode 100644 index 000000000..12d4d0b84 --- /dev/null +++ b/src/Testcontainers.ServiceBus/ServiceBusBuilder.cs @@ -0,0 +1,143 @@ +namespace Testcontainers.ServiceBus; + +/// +[PublicAPI] +public sealed class ServiceBusBuilder : ContainerBuilder +{ + public const string ServiceBusNetworkAlias = "servicebus-container"; + + public const string DatabaseNetworkAlias = "database-container"; + + public const string ServiceBusImage = "mcr.microsoft.com/azure-messaging/servicebus-emulator:latest"; + + public const ushort ServiceBusPort = 5672; + + private const string AcceptLicenseAgreementEnvVar = "ACCEPT_EULA"; + + private const string AcceptLicenseAgreement = "Y"; + + private const string DeclineLicenseAgreement = "N"; + + /// + /// Initializes a new instance of the class. + /// + public ServiceBusBuilder() + : this(new ServiceBusConfiguration()) + { + DockerResourceConfiguration = Init().DockerResourceConfiguration; + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + private ServiceBusBuilder(ServiceBusConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + DockerResourceConfiguration = resourceConfiguration; + } + + /// + protected override ServiceBusConfiguration DockerResourceConfiguration { get; } + + /// + /// Accepts the license agreement. + /// + /// + /// When is set to true, the Azure Service Bus Emulator license is accepted. + /// + /// A boolean value indicating whether the Azure Service Bus Emulator license agreement is accepted. + /// A configured instance of . + public ServiceBusBuilder WithAcceptLicenseAgreement(bool acceptLicenseAgreement) + { + var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement; + return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement); + } + + /// + public override ServiceBusContainer Build() + { + Validate(); + return new ServiceBusContainer(DockerResourceConfiguration); + } + + /// + protected override void Validate() + { + const string message = "The image '{0}' requires you to accept a license agreement."; + + base.Validate(); + + Predicate licenseAgreementNotAccepted = value => + !value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal); + + _ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image)) + .ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name)); + } + + /// + protected override ServiceBusBuilder Init() + { + return base.Init() + .WithImage(ServiceBusImage) + .WithNetwork(new NetworkBuilder().Build()) + .WithNetworkAliases(ServiceBusNetworkAlias) + .WithPortBinding(ServiceBusPort, true) + .WithMsSqlContainer() + .WithWaitStrategy(Wait.ForUnixContainer() + .UntilMessageIsLogged("Emulator Service is Successfully Up!") + .AddCustomWaitStrategy(new WaitTwoSeconds())); + } + + /// + protected override ServiceBusBuilder Clone(IResourceConfiguration resourceConfiguration) + { + return Merge(DockerResourceConfiguration, new ServiceBusConfiguration(resourceConfiguration)); + } + + /// + protected override ServiceBusBuilder Clone(IContainerConfiguration resourceConfiguration) + { + return Merge(DockerResourceConfiguration, new ServiceBusConfiguration(resourceConfiguration)); + } + + /// + protected override ServiceBusBuilder Merge(ServiceBusConfiguration oldValue, ServiceBusConfiguration newValue) + { + return new ServiceBusBuilder(new ServiceBusConfiguration(oldValue, newValue)); + } + + /// + /// Configures the dependent MSSQL container. + /// + /// A configured instance of . + private ServiceBusBuilder WithMsSqlContainer() + { + var msSqlContainer = new MsSqlBuilder() + .WithNetwork(DockerResourceConfiguration.Networks.Single()) + .WithNetworkAliases(DatabaseNetworkAlias) + .Build(); + + return Merge(DockerResourceConfiguration, new ServiceBusConfiguration(databaseContainer: msSqlContainer)) + .WithEnvironment("MSSQL_SA_PASSWORD", MsSqlBuilder.DefaultPassword) + .WithEnvironment("SQL_SERVER", DatabaseNetworkAlias); + } + + /// + /// + /// This is a workaround to ensure that the wait strategy does not indicate + /// readiness too early: + /// https://github.com/Azure/azure-service-bus-emulator-installer/issues/35#issuecomment-2497164533. + /// + private sealed class WaitTwoSeconds : IWaitUntil + { + /// + public async Task UntilAsync(IContainer container) + { + await Task.Delay(TimeSpan.FromSeconds(2)) + .ConfigureAwait(false); + + return true; + } + } +} \ No newline at end of file diff --git a/src/Testcontainers.ServiceBus/ServiceBusConfiguration.cs b/src/Testcontainers.ServiceBus/ServiceBusConfiguration.cs new file mode 100644 index 000000000..d12b8ac71 --- /dev/null +++ b/src/Testcontainers.ServiceBus/ServiceBusConfiguration.cs @@ -0,0 +1,61 @@ +namespace Testcontainers.ServiceBus; + +/// +[PublicAPI] +public sealed class ServiceBusConfiguration : ContainerConfiguration +{ + /// + /// Initializes a new instance of the class. + /// + /// The database container. + public ServiceBusConfiguration(IDatabaseContainer databaseContainer = null) + { + DatabaseContainer = databaseContainer; + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public ServiceBusConfiguration(IResourceConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public ServiceBusConfiguration(IContainerConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public ServiceBusConfiguration(ServiceBusConfiguration resourceConfiguration) + : this(new ServiceBusConfiguration(), resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The old Docker resource configuration. + /// The new Docker resource configuration. + public ServiceBusConfiguration(ServiceBusConfiguration oldValue, ServiceBusConfiguration newValue) + : base(oldValue, newValue) + { + DatabaseContainer = BuildConfiguration.Combine(oldValue.DatabaseContainer, newValue.DatabaseContainer); + } + + /// + /// Gets the database container. + /// + public IDatabaseContainer DatabaseContainer { get; } +} \ No newline at end of file diff --git a/src/Testcontainers.ServiceBus/ServiceBusContainer.cs b/src/Testcontainers.ServiceBus/ServiceBusContainer.cs new file mode 100644 index 000000000..0d676f809 --- /dev/null +++ b/src/Testcontainers.ServiceBus/ServiceBusContainer.cs @@ -0,0 +1,68 @@ +namespace Testcontainers.ServiceBus; + +/// +[PublicAPI] +public sealed class ServiceBusContainer : DockerContainer +{ + private readonly ServiceBusConfiguration _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The container configuration. + public ServiceBusContainer(ServiceBusConfiguration configuration) + : base(configuration) + { + _configuration = configuration; + } + + /// + /// Gets the Service Bus connection string. + /// + /// The Service Bus connection string. + public string GetConnectionString() + { + var properties = new Dictionary(); + properties.Add("Endpoint", new UriBuilder("amqp", Hostname, GetMappedPublicPort(ServiceBusBuilder.ServiceBusPort)).ToString()); + properties.Add("SharedAccessKeyName", "RootManageSharedAccessKey"); + properties.Add("SharedAccessKey", "SAS_KEY_VALUE"); + properties.Add("UseDevelopmentEmulator", "true"); + return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value))); + } + + /// + protected override async Task UnsafeCreateAsync(CancellationToken ct = default) + { + await _configuration.Networks.Single().CreateAsync(ct) + .ConfigureAwait(false); + + await base.UnsafeCreateAsync(ct) + .ConfigureAwait(false); + } + + /// + protected override Task UnsafeDeleteAsync(CancellationToken ct = default) + { + return Task.WhenAll(base.UnsafeDeleteAsync(ct), _configuration.DatabaseContainer.DisposeAsync().AsTask()); + } + + /// + protected override async Task UnsafeStartAsync(CancellationToken ct = default) + { + await _configuration.DatabaseContainer.StartAsync(ct) + .ConfigureAwait(false); + + await base.UnsafeStartAsync(ct) + .ConfigureAwait(false); + } + + /// + protected override async Task UnsafeStopAsync(CancellationToken ct = default) + { + await base.UnsafeStopAsync(ct) + .ConfigureAwait(false); + + await _configuration.DatabaseContainer.StopAsync(ct) + .ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Testcontainers.ServiceBus/Testcontainers.ServiceBus.csproj b/src/Testcontainers.ServiceBus/Testcontainers.ServiceBus.csproj new file mode 100644 index 000000000..f24f1b880 --- /dev/null +++ b/src/Testcontainers.ServiceBus/Testcontainers.ServiceBus.csproj @@ -0,0 +1,13 @@ + + + net8.0;net9.0;netstandard2.0;netstandard2.1 + latest + + + + + + + + + \ No newline at end of file diff --git a/src/Testcontainers.ServiceBus/Usings.cs b/src/Testcontainers.ServiceBus/Usings.cs new file mode 100644 index 000000000..6f294326f --- /dev/null +++ b/src/Testcontainers.ServiceBus/Usings.cs @@ -0,0 +1,14 @@ +global using System; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text.RegularExpressions; +global using System.Threading; +global using System.Threading.Tasks; +global using Docker.DotNet.Models; +global using DotNet.Testcontainers; +global using DotNet.Testcontainers.Builders; +global using DotNet.Testcontainers.Configurations; +global using DotNet.Testcontainers.Containers; +global using DotNet.Testcontainers.Images; +global using JetBrains.Annotations; +global using Testcontainers.MsSql; \ No newline at end of file diff --git a/src/Testcontainers/Containers/IDatabaseContainer.cs b/src/Testcontainers/Containers/IDatabaseContainer.cs index 8a97e6766..171c266b1 100644 --- a/src/Testcontainers/Containers/IDatabaseContainer.cs +++ b/src/Testcontainers/Containers/IDatabaseContainer.cs @@ -6,7 +6,7 @@ namespace DotNet.Testcontainers.Containers /// Represents a database container instance that can be accessed with an ADO.NET provider. /// [PublicAPI] - public interface IDatabaseContainer + public interface IDatabaseContainer : IContainer { /// /// Gets the database connection string. diff --git a/tests/Testcontainers.ServiceBus.Tests/.editorconfig b/tests/Testcontainers.ServiceBus.Tests/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/tests/Testcontainers.ServiceBus.Tests/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs b/tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs new file mode 100644 index 000000000..d35a88117 --- /dev/null +++ b/tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs @@ -0,0 +1,50 @@ +namespace Testcontainers.ServiceBus; + +public sealed class ServiceBusContainerTest : IAsyncLifetime +{ + private readonly ServiceBusContainer _serviceBusContainer = new ServiceBusBuilder().WithAcceptLicenseAgreement(true).Build(); + + public Task InitializeAsync() + { + return _serviceBusContainer.StartAsync(); + } + + public Task DisposeAsync() + { + return _serviceBusContainer.DisposeAsync().AsTask(); + } + + [Fact] + [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] + public async Task ReceiveMessageReturnsSentMessage() + { + // Given + const string helloServiceBus = "Hello, Service Bus!"; + + // By default, the emulator uses the following configuration: + // https://learn.microsoft.com/en-us/azure/service-bus-messaging/test-locally-with-service-bus-emulator?tabs=automated-script#interact-with-the-emulator. + + // Upload a custom configuration before the container starts using the + // `WithResourceMapping(string, string)` API or one of its overloads: + // `WithResourceMapping("Config.json", "/ServiceBus_Emulator/ConfigFiles/")`. + const string queueName = "queue.1"; + + var message = new ServiceBusMessage(helloServiceBus); + + await using var client = new ServiceBusClient(_serviceBusContainer.GetConnectionString()); + + var sender = client.CreateSender(queueName); + + var receiver = client.CreateReceiver(queueName); + + // When + await sender.SendMessageAsync(message) + .ConfigureAwait(true); + + var receivedMessage = await receiver.ReceiveMessageAsync() + .ConfigureAwait(true); + + // Then + Assert.Equal(helloServiceBus, receivedMessage.Body.ToString()); + } +} \ No newline at end of file diff --git a/tests/Testcontainers.ServiceBus.Tests/Testcontainers.ServiceBus.Tests.csproj b/tests/Testcontainers.ServiceBus.Tests/Testcontainers.ServiceBus.Tests.csproj new file mode 100644 index 000000000..835a69422 --- /dev/null +++ b/tests/Testcontainers.ServiceBus.Tests/Testcontainers.ServiceBus.Tests.csproj @@ -0,0 +1,18 @@ + + + net9.0 + false + false + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Testcontainers.ServiceBus.Tests/Usings.cs b/tests/Testcontainers.ServiceBus.Tests/Usings.cs new file mode 100644 index 000000000..9ddd0ad38 --- /dev/null +++ b/tests/Testcontainers.ServiceBus.Tests/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Threading.Tasks; +global using Azure.Messaging.ServiceBus; +global using DotNet.Testcontainers.Commons; +global using Xunit; \ No newline at end of file