diff --git a/.github/actions/cosmosdb-setup/action.yml b/.github/actions/cosmosdb-setup/action.yml
index 5389412..d283f95 100644
--- a/.github/actions/cosmosdb-setup/action.yml
+++ b/.github/actions/cosmosdb-setup/action.yml
@@ -4,11 +4,21 @@ description: "Setup Cosmos DB Emulator environment"
runs:
using: "composite"
steps:
+ - name: Check for Docker Installation
+ shell: bash
+ run: |
+ if ! command -v docker &> /dev/null; then
+ echo "Docker could not be found. Install Docker before running this action."
+ exit 1
+ fi
+
- name: Pull and Run Cosmos DB Emulator
shell: bash
run: |
+ set -e
docker pull mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest
docker run \
+ --rm \
--publish 8081:8081 \
--name cosmosdb-linux-emulator \
--detach \
@@ -18,7 +28,7 @@ runs:
shell: bash
run: |
echo "Waiting for Cosmos DB Emulator to start... (it may require a few minutes)"
- while ! curl --insecure https://localhost:8081/_explorer/emulator.pem >/dev/null 2>&1; do
+ until curl --insecure https://localhost:8081/_explorer/emulator.pem >/dev/null 2>&1; do
sleep 5
done
@@ -33,4 +43,4 @@ runs:
run: |
sudo cp ~/emulatorcert.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
- echo "Certificate updated. Cosmos DB Emulator configured successfully!"
+ echo "Certificate updated. Cosmos DB Emulator configured successfully!"
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c5e63f0..cf734f5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -24,6 +24,7 @@ jobs:
dotnet-version: |
6.0.x
8.0.x
+ 9.0.x
- name: Run build
shell: pwsh
diff --git a/.github/workflows/pack&push.yml b/.github/workflows/pack&push.yml
index e3bb090..ae91a58 100644
--- a/.github/workflows/pack&push.yml
+++ b/.github/workflows/pack&push.yml
@@ -26,6 +26,7 @@ jobs:
dotnet-version: |
6.0.x
8.0.x
+ 9.0.x
- name: Pack & Push to NuGet
shell: pwsh
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 4f33757..8cd4f5b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -70,7 +70,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- test_type: [
+ testType: [
{ name: "Abstractions", condition: "abstractions_changed" },
{ name: "CosmosDB", condition: "cosmosdb_changed" },
{ name: "Dapper", condition: "dapper_changed" },
@@ -87,12 +87,13 @@ jobs:
dotnet-version: |
6.0.x
8.0.x
+ 9.0.x
- name: Start CosmosDB Emulator
- if: matrix.test_type.name == 'CosmosDB' && needs.checks_tests_need.outputs.cosmosdb_changed == 'true'
+ if: matrix.testType.name == 'CosmosDB' && needs.checks_tests_need.outputs.cosmosdb_changed == 'true'
uses: ./.github/actions/cosmosdb-setup
- - name: Run ${{ matrix.test_type.name }} tests
- if: needs.checks_tests_need.outputs[matrix.test_type.condition] == 'true'
+ - name: Run ${{ matrix.testType.name }} tests
+ if: needs.checks_tests_need.outputs[matrix.testType.condition] == 'true'
shell: pwsh
- run: ./test.ps1 -Configuration ${{ env.CONFIGURATION }} -TestType ${{ matrix.test_type.name }}
+ run: ./test.ps1 -Configuration ${{ env.CONFIGURATION }} -TestType ${{ matrix.testType.name }}
diff --git a/FeatureManagement.Database.slnx b/FeatureManagement.Database.slnx
index 3cb6a75..11d1c45 100644
--- a/FeatureManagement.Database.slnx
+++ b/FeatureManagement.Database.slnx
@@ -1,45 +1,42 @@
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
diff --git a/README.md b/README.md
index 6dc4faf..d82432b 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ It includes abstractions and default implementations to facilitate easy integrat
The Wiki contains comprehensive information on the following topics:
+
* **[Packages](https://github.com/teociaps/FeatureManagement.Database/wiki/Packages)**: Overview of the available NuGet packages.
* **[Getting Started](https://github.com/teociaps/FeatureManagement.Database/wiki/Getting-Started)**: Instructions on how to begin using the library.
* **[Consumption](https://github.com/teociaps/FeatureManagement.Database/wiki/Consumption)**: Details on how to use feature flags in your application.
diff --git a/build.ps1 b/build.ps1
index 959520c..69c7d64 100644
--- a/build.ps1
+++ b/build.ps1
@@ -1,11 +1,12 @@
# build.ps1
param(
- [string]$Configuration = "Release"
+ [string]$Configuration = "Release",
+ [string]$SolutionFile = "FeatureManagement.Database.sln"
)
# Restore dependencies
Write-Host "Restoring dependencies..."
-dotnet restore
+dotnet restore $SolutionFile
if ($LASTEXITCODE -ne 0) {
Write-Error "dotnet restore failed"
exit $LASTEXITCODE
@@ -13,7 +14,7 @@ if ($LASTEXITCODE -ne 0) {
# Build the project
Write-Host "Building project..."
-dotnet build --no-restore --configuration $Configuration
+dotnet build $SolutionFile --no-restore --configuration $Configuration
if ($LASTEXITCODE -ne 0) {
Write-Error "dotnet build failed"
exit $LASTEXITCODE
diff --git a/build/Common.props b/build/Common.props
index c63cb05..d72efa8 100644
--- a/build/Common.props
+++ b/build/Common.props
@@ -1,7 +1,7 @@
- 12.0
+ 13.0
enable
diff --git a/samples/ConsoleApp/ConsoleApp.csproj b/samples/ConsoleApp/ConsoleApp.csproj
index 65e4577..3d45ff8 100644
--- a/samples/ConsoleApp/ConsoleApp.csproj
+++ b/samples/ConsoleApp/ConsoleApp.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net9.0
enable
enable
false
@@ -10,7 +10,7 @@
-
+
diff --git a/samples/WebApiApp/Controllers/WeatherForecastController.cs b/samples/WebApiApp/Controllers/WeatherForecastController.cs
index b199196..feefa61 100644
--- a/samples/WebApiApp/Controllers/WeatherForecastController.cs
+++ b/samples/WebApiApp/Controllers/WeatherForecastController.cs
@@ -15,13 +15,6 @@ public class WeatherForecastController : ControllerBase
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
- private readonly ILogger _logger;
-
- public WeatherForecastController(ILogger logger)
- {
- _logger = logger;
- }
-
[HttpGet(Name = "GetWeatherForecast")]
[FeatureGate(Features.Weather)]
public IEnumerable Get()
diff --git a/samples/WebApiApp/Program.cs b/samples/WebApiApp/Program.cs
index 9ab4480..2cc6779 100644
--- a/samples/WebApiApp/Program.cs
+++ b/samples/WebApiApp/Program.cs
@@ -21,22 +21,18 @@
// Seed with feature
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService();
-if (!dbContext.Features.Any())
+if (!await dbContext.Features.AnyAsync())
{
dbContext.Features.Add(new Feature
{
Name = WebApiApp.Features.Weather,
Settings = [new FeatureSettings { FilterType = FeatureFilterType.Percentage, Parameters = """{ "Value": 50 }""" }]
});
- dbContext.SaveChanges();
+ await dbContext.SaveChangesAsync();
}
-// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
-{
- app.UseSwagger();
- app.UseSwaggerUI();
-}
+app.UseSwagger();
+app.UseSwaggerUI();
app.UseHttpsRedirection();
@@ -44,4 +40,4 @@
app.MapControllers();
-app.Run();
\ No newline at end of file
+await app.RunAsync();
\ No newline at end of file
diff --git a/samples/WebApiApp/WebApiApp.csproj b/samples/WebApiApp/WebApiApp.csproj
index 507ed43..179d52c 100644
--- a/samples/WebApiApp/WebApiApp.csproj
+++ b/samples/WebApiApp/WebApiApp.csproj
@@ -1,15 +1,15 @@
- net8.0
+ net9.0
enable
enable
-
+
-
+
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index a1d917e..5d80d7e 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -3,19 +3,20 @@
true
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/FeatureManagement.Database.Abstractions/FeatureManagement.Database.Abstractions.csproj b/src/FeatureManagement.Database.Abstractions/FeatureManagement.Database.Abstractions.csproj
index 1618926..a9da358 100644
--- a/src/FeatureManagement.Database.Abstractions/FeatureManagement.Database.Abstractions.csproj
+++ b/src/FeatureManagement.Database.Abstractions/FeatureManagement.Database.Abstractions.csproj
@@ -1,7 +1,7 @@
- netstandard2.1;net6.0;net8.0
+ netstandard2.1;net6.0;net8.0;net9.0
@@ -17,10 +17,20 @@
+
+
+
+
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FeatureManagement.Database.Abstractions/FeatureManagement/Database/CachedFeatureStore.cs b/src/FeatureManagement.Database.Abstractions/FeatureManagement/Database/CachedFeatureStore.cs
index 93541f2..ebab392 100644
--- a/src/FeatureManagement.Database.Abstractions/FeatureManagement/Database/CachedFeatureStore.cs
+++ b/src/FeatureManagement.Database.Abstractions/FeatureManagement/Database/CachedFeatureStore.cs
@@ -1,12 +1,19 @@
// Copyright (c) Matteo Ciapparelli.
// Licensed under the MIT license.
-using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;
+
+#if NET9_0_OR_GREATER
+using Microsoft.Extensions.Caching.Hybrid;
+#else
+
+using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
using System.Text.Json.Serialization;
+#endif
+
namespace FeatureManagement.Database;
///
@@ -15,21 +22,31 @@ namespace FeatureManagement.Database;
public class CachedFeatureStore : IFeatureStore
{
private readonly IFeatureStore _featureStore;
+#if NET9_0_OR_GREATER
+ private readonly HybridCache _cache;
+#else
private readonly IDistributedCache _cache;
- private readonly FeatureCacheOptions _cacheOptions;
- private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions(JsonSerializerOptions.Default)
+ private readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerOptions.Default)
{
ReferenceHandler = ReferenceHandler.IgnoreCycles
};
+#endif
+ private readonly FeatureCacheOptions _cacheOptions;
+
///
/// Initializes a new instance of the class.
///
/// The concrete feature store.
/// The cache service.
/// The cache options.
- public CachedFeatureStore(IFeatureStore featureStore, IDistributedCache cache,
+ public CachedFeatureStore(IFeatureStore featureStore,
+#if NET9_0_OR_GREATER
+ HybridCache cache,
+#else
+ IDistributedCache cache,
+#endif
IOptions options)
{
_featureStore = featureStore ?? throw new ArgumentNullException(nameof(featureStore));
@@ -69,23 +86,56 @@ public async Task> GetFeaturesAsync()
private async Task GetCacheAsync(string key) where TData : class
{
- var cachedData = await _cache.GetAsync(FeatureCacheOptions.CachePrefix + key);
+ var cacheKey = FeatureCacheOptions.CachePrefix + key;
+
+#if NET9_0_OR_GREATER
+ return await _cache.GetOrCreateAsync(cacheKey, static (_) => ValueTask.FromResult(default(TData)));
+#else
+ var cachedData = await _cache.GetAsync(cacheKey);
if (cachedData is null)
return null;
return await JsonSerializer.DeserializeAsync(new MemoryStream(cachedData), _jsonOptions);
+#endif
}
- private async Task SetCacheAsync(string key, TData data) where TData : class
+ private Task SetCacheAsync(string key, TData data) where TData : class
{
- await _cache.SetAsync(FeatureCacheOptions.CachePrefix + key,
- JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions), new DistributedCacheEntryOptions
- {
- AbsoluteExpiration = _cacheOptions.AbsoluteExpiration,
- AbsoluteExpirationRelativeToNow = _cacheOptions.AbsoluteExpirationRelativeToNow,
- SlidingExpiration = _cacheOptions.SlidingExpiration
- });
+ var cacheKey = FeatureCacheOptions.CachePrefix + key;
+
+#if NET9_0_OR_GREATER
+ TimeSpan? expiration = null;
+ if (_cacheOptions.AbsoluteExpirationRelativeToNow.HasValue)
+ {
+ expiration = _cacheOptions.AbsoluteExpirationRelativeToNow.Value;
+ }
+ else if (_cacheOptions.SlidingExpiration.HasValue)
+ {
+ // Note: HybridCache treats 'Expiration' more like an absolute TTL from creation/access.
+ // Using SlidingExpiration here effectively sets an absolute expiry based on this duration.
+ expiration = _cacheOptions.SlidingExpiration.Value;
+ }
+ else if (_cacheOptions.AbsoluteExpiration.HasValue && _cacheOptions.AbsoluteExpiration > DateTimeOffset.UtcNow)
+ {
+ expiration = _cacheOptions.AbsoluteExpiration.Value - DateTimeOffset.UtcNow;
+ }
+
+ var entryOptions = new HybridCacheEntryOptions
+ {
+ Expiration = expiration > TimeSpan.Zero ? expiration : null
+ };
+
+ return _cache.SetAsync(cacheKey, data, entryOptions).AsTask();
+#else
+ var options = new DistributedCacheEntryOptions
+ {
+ AbsoluteExpiration = _cacheOptions.AbsoluteExpiration,
+ AbsoluteExpirationRelativeToNow = _cacheOptions.AbsoluteExpirationRelativeToNow,
+ SlidingExpiration = _cacheOptions.SlidingExpiration
+ };
+ return _cache.SetAsync(cacheKey, JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions), options);
+#endif
}
#endregion Private
diff --git a/src/FeatureManagement.Database.Abstractions/FeatureManagement/Database/FeatureHybridCacheSerializer.cs b/src/FeatureManagement.Database.Abstractions/FeatureManagement/Database/FeatureHybridCacheSerializer.cs
new file mode 100644
index 0000000..2fa7563
--- /dev/null
+++ b/src/FeatureManagement.Database.Abstractions/FeatureManagement/Database/FeatureHybridCacheSerializer.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Matteo Ciapparelli.
+// Licensed under the MIT license.
+
+#if NET9_0_OR_GREATER
+using Microsoft.Extensions.Caching.Hybrid;
+using System.Buffers;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace FeatureManagement.Database;
+
+///
+/// Custom serializer and deserializer for Feature objects.
+///
+public class FeatureHybridCacheSerializer : IHybridCacheSerializer
+{
+ private readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerOptions.Default)
+ {
+ ReferenceHandler = ReferenceHandler.IgnoreCycles
+ };
+
+ ///
+ /// Deserializes a ReadOnlySequence of bytes to a Feature object.
+ ///
+ /// The ReadOnlySequence of bytes to deserialize.
+ /// The deserialized Feature object.
+ public Feature Deserialize(ReadOnlySequence source)
+ {
+ var reader = new Utf8JsonReader(source);
+ return JsonSerializer.Deserialize(ref reader, _jsonOptions);
+ }
+
+ ///
+ /// Serializes a Feature object to an IBufferWriter of bytes.
+ ///
+ /// The Feature object to serialize.
+ /// The IBufferWriter to write the serialized bytes to.
+ public void Serialize(Feature value, IBufferWriter target)
+ {
+ using var writer = new Utf8JsonWriter(target);
+ JsonSerializer.Serialize(writer, value, _jsonOptions);
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/DecoratorServiceCollectionExtensions.cs b/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/DecoratorServiceCollectionExtensions.cs
index 2b71659..4b1fdef 100644
--- a/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/DecoratorServiceCollectionExtensions.cs
+++ b/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/DecoratorServiceCollectionExtensions.cs
@@ -21,7 +21,7 @@ internal static IServiceCollection Decorate(this IServiceC
where TDecorator : class, TService
{
if (services.TryGetDescriptors(typeof(TService), out var descriptors))
- return services.DecorateDescriptors(descriptors.ToArray(), x => x.Decorate(typeof(TDecorator)));
+ return services.DecorateDescriptors([.. descriptors], x => x.Decorate(typeof(TDecorator)));
return services;
}
diff --git a/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs b/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs
index a1911de..25e6834 100644
--- a/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/FeatureManagement.Database.Abstractions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs
@@ -125,7 +125,11 @@ private static void AddDatabaseFeatureDefinitionProvider(this IServiceCollection
private static IServiceCollection AddDatabaseFeatureManagementCacheServices(this IServiceCollection services)
{
+#if NET9_0_OR_GREATER
+ services.AddHybridCache().AddSerializer();
+#else
services.AddDistributedMemoryCache();
+#endif
return services.Decorate();
}
diff --git a/src/FeatureManagement.Database.CosmosDB/FeatureManagement.Database.CosmosDB.csproj b/src/FeatureManagement.Database.CosmosDB/FeatureManagement.Database.CosmosDB.csproj
index cf0225a..2401bb4 100644
--- a/src/FeatureManagement.Database.CosmosDB/FeatureManagement.Database.CosmosDB.csproj
+++ b/src/FeatureManagement.Database.CosmosDB/FeatureManagement.Database.CosmosDB.csproj
@@ -1,7 +1,7 @@
- netstandard2.1;net6.0;net8.0
+ netstandard2.1;net6.0;net8.0;net9.0
true
diff --git a/src/FeatureManagement.Database.CosmosDB/FeatureManagement/Database/CosmosDB/CosmosDBConnectionFactory.cs b/src/FeatureManagement.Database.CosmosDB/FeatureManagement/Database/CosmosDB/CosmosDBConnectionFactory.cs
index cc46617..0863344 100644
--- a/src/FeatureManagement.Database.CosmosDB/FeatureManagement/Database/CosmosDB/CosmosDBConnectionFactory.cs
+++ b/src/FeatureManagement.Database.CosmosDB/FeatureManagement/Database/CosmosDB/CosmosDBConnectionFactory.cs
@@ -9,9 +9,10 @@ namespace FeatureManagement.Database.CosmosDB;
///
/// Default implementation of .
///
-public class CosmosDBConnectionFactory : ICosmosDBConnectionFactory
+public class CosmosDBConnectionFactory : ICosmosDBConnectionFactory, IDisposable
{
private readonly CosmosClient _client;
+ private bool _disposedValue;
private readonly Container _featuresContainer;
private readonly Container _featureSettingsContainer;
@@ -59,4 +60,28 @@ public virtual Container GetFeatureSettingsContainer()
{
return _useSeparateContainers ? _featureSettingsContainer : _featuresContainer;
}
+
+ ///
+ /// Releases the unmanaged resources used by the CosmosDBConnectionFactory and optionally releases the managed resources.
+ ///
+ /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ _client?.Dispose();
+ }
+
+ _disposedValue = true;
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ Dispose(disposing: true);
+ }
}
\ No newline at end of file
diff --git a/src/FeatureManagement.Database.Dapper/FeatureManagement.Database.Dapper.csproj b/src/FeatureManagement.Database.Dapper/FeatureManagement.Database.Dapper.csproj
index d747328..c625c57 100644
--- a/src/FeatureManagement.Database.Dapper/FeatureManagement.Database.Dapper.csproj
+++ b/src/FeatureManagement.Database.Dapper/FeatureManagement.Database.Dapper.csproj
@@ -1,7 +1,7 @@
- netstandard2.1;net6.0;net8.0
+ netstandard2.1;net6.0;net8.0;net9.0
@@ -16,10 +16,13 @@
-
+
+
+
+
diff --git a/src/FeatureManagement.Database.EntityFrameworkCore.MySql/FeatureManagement.Database.EntityFrameworkCore.MySql.csproj b/src/FeatureManagement.Database.EntityFrameworkCore.MySql/FeatureManagement.Database.EntityFrameworkCore.MySql.csproj
index 8f6f692..4abf3e0 100644
--- a/src/FeatureManagement.Database.EntityFrameworkCore.MySql/FeatureManagement.Database.EntityFrameworkCore.MySql.csproj
+++ b/src/FeatureManagement.Database.EntityFrameworkCore.MySql/FeatureManagement.Database.EntityFrameworkCore.MySql.csproj
@@ -1,7 +1,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
@@ -16,10 +16,14 @@
-
+
+
+
+
+
diff --git a/src/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.csproj b/src/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.csproj
index 1ced4ed..699ea9e 100644
--- a/src/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.csproj
+++ b/src/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.csproj
@@ -1,7 +1,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
@@ -16,10 +16,14 @@
-
+
+
+
+
+
diff --git a/src/FeatureManagement.Database.EntityFrameworkCore.SqlServer/FeatureManagement.Database.EntityFrameworkCore.SqlServer.csproj b/src/FeatureManagement.Database.EntityFrameworkCore.SqlServer/FeatureManagement.Database.EntityFrameworkCore.SqlServer.csproj
index 6a2fbe9..f82def1 100644
--- a/src/FeatureManagement.Database.EntityFrameworkCore.SqlServer/FeatureManagement.Database.EntityFrameworkCore.SqlServer.csproj
+++ b/src/FeatureManagement.Database.EntityFrameworkCore.SqlServer/FeatureManagement.Database.EntityFrameworkCore.SqlServer.csproj
@@ -1,7 +1,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
@@ -16,10 +16,14 @@
-
+
+
+
+
+
diff --git a/src/FeatureManagement.Database.EntityFrameworkCore.Sqlite/FeatureManagement.Database.EntityFrameworkCore.Sqlite.csproj b/src/FeatureManagement.Database.EntityFrameworkCore.Sqlite/FeatureManagement.Database.EntityFrameworkCore.Sqlite.csproj
index dac732b..fb80d92 100644
--- a/src/FeatureManagement.Database.EntityFrameworkCore.Sqlite/FeatureManagement.Database.EntityFrameworkCore.Sqlite.csproj
+++ b/src/FeatureManagement.Database.EntityFrameworkCore.Sqlite/FeatureManagement.Database.EntityFrameworkCore.Sqlite.csproj
@@ -1,7 +1,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
@@ -16,10 +16,14 @@
-
+
+
+
+
+
diff --git a/src/FeatureManagement.Database.EntityFrameworkCore/FeatureManagement.Database.EntityFrameworkCore.csproj b/src/FeatureManagement.Database.EntityFrameworkCore/FeatureManagement.Database.EntityFrameworkCore.csproj
index 0c31d5d..df07330 100644
--- a/src/FeatureManagement.Database.EntityFrameworkCore/FeatureManagement.Database.EntityFrameworkCore.csproj
+++ b/src/FeatureManagement.Database.EntityFrameworkCore/FeatureManagement.Database.EntityFrameworkCore.csproj
@@ -1,7 +1,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
@@ -16,10 +16,14 @@
-
+
+
+
+
+
diff --git a/src/FeatureManagement.Database.MongoDB/FeatureManagement.Database.MongoDB.csproj b/src/FeatureManagement.Database.MongoDB/FeatureManagement.Database.MongoDB.csproj
index 16601c6..f30cf71 100644
--- a/src/FeatureManagement.Database.MongoDB/FeatureManagement.Database.MongoDB.csproj
+++ b/src/FeatureManagement.Database.MongoDB/FeatureManagement.Database.MongoDB.csproj
@@ -1,7 +1,7 @@
- netstandard2.1;net6.0;net8.0
+ netstandard2.1;net6.0;net8.0;net9.0
diff --git a/src/FeatureManagement.Database.NHibernate/FeatureManagement.Database.NHibernate.csproj b/src/FeatureManagement.Database.NHibernate/FeatureManagement.Database.NHibernate.csproj
index 6dbb9b9..045780c 100644
--- a/src/FeatureManagement.Database.NHibernate/FeatureManagement.Database.NHibernate.csproj
+++ b/src/FeatureManagement.Database.NHibernate/FeatureManagement.Database.NHibernate.csproj
@@ -1,7 +1,7 @@
- netstandard2.1;net6.0;net8.0
+ netstandard2.1;net6.0;net8.0;net9.0
diff --git a/test.ps1 b/test.ps1
index beb510a..17e771e 100644
--- a/test.ps1
+++ b/test.ps1
@@ -5,7 +5,7 @@ param(
)
# Get all test project files
-$testProjects = Get-ChildItem -Path "tests" -Recurse -Filter "*.csproj"
+$testProjects = Get-ChildItem -Path "tests" -Recurse -Filter "*.Tests.csproj"
# Filter test projects based on the $TestType parameter, except for "All"
if ($TestType -ne "All") {
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
index 232068e..a4518a2 100644
--- a/tests/Directory.Build.props
+++ b/tests/Directory.Build.props
@@ -1,6 +1,6 @@
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props
index dfa4664..4d45470 100644
--- a/tests/Directory.Packages.props
+++ b/tests/Directory.Packages.props
@@ -3,24 +3,25 @@
true
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement.Database.Abstractions.Tests.csproj b/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement.Database.Abstractions.Tests.csproj
index 496d23f..c72cb74 100644
--- a/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement.Database.Abstractions.Tests.csproj
+++ b/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement.Database.Abstractions.Tests.csproj
@@ -3,14 +3,13 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
-
@@ -20,6 +19,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement/Database/Abstractions/Tests/CachedFeatureStoreTests.cs b/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement/Database/Abstractions/Tests/CachedFeatureStoreTests.cs
index 4b2a846..30e3d19 100644
--- a/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement/Database/Abstractions/Tests/CachedFeatureStoreTests.cs
+++ b/tests/FeatureManagement.Database.Abstractions.Tests/FeatureManagement/Database/Abstractions/Tests/CachedFeatureStoreTests.cs
@@ -1,12 +1,19 @@
// Copyright (c) Matteo Ciapparelli.
// Licensed under the MIT license.
-using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
-using System.Text.Json;
using static FeatureManagement.Database.Abstractions.Features;
+#if NET9_0_OR_GREATER
+using Microsoft.Extensions.Caching.Hybrid;
+#else
+
+using Microsoft.Extensions.Caching.Distributed;
+using System.Text.Json;
+
+#endif
+
namespace FeatureManagement.Database.Abstractions.Tests;
public class CachedFeatureStoreTests
@@ -22,11 +29,19 @@ public async Task GetFeatureFromCachedStore()
var serviceProvider = serviceCollection.BuildServiceProvider();
var cachedFeatureStore = serviceProvider.GetRequiredService();
+#if NET9_0_OR_GREATER
+ var cache = serviceProvider.GetRequiredService();
+#else
var cache = serviceProvider.GetRequiredService();
+#endif
// Act
var feature = await cachedFeatureStore.GetFeatureAsync(FirstFeature);
+#if NET9_0_OR_GREATER
+ var cachedFeature = await cache.GetOrCreateAsync(FeatureCacheOptions.CachePrefix + FirstFeature, static (_) => ValueTask.FromResult(default(Feature)));
+#else
var cachedFeature = JsonSerializer.Deserialize(await cache.GetAsync(FeatureCacheOptions.CachePrefix + FirstFeature));
+#endif
// Assert
Assert.True(feature is not null);
@@ -35,7 +50,7 @@ public async Task GetFeatureFromCachedStore()
Assert.Equal(FirstFeature, feature.Name);
Assert.Equal(feature.Name, cachedFeature.Name);
- Assert.True(feature.Settings.Any());
+ Assert.True(feature.Settings.Count > 0);
Assert.Equivalent(feature.Settings, cachedFeature.Settings);
Assert.Equal(FeatureFilterType.TimeWindow, feature.Settings.First().FilterType);
@@ -53,12 +68,20 @@ public async Task GetAllFeaturesFromCachedStore()
var serviceProvider = serviceCollection.BuildServiceProvider();
var cachedFeatureStore = serviceProvider.GetRequiredService();
+#if NET9_0_OR_GREATER
+ var cache = serviceProvider.GetRequiredService();
+#else
var cache = serviceProvider.GetRequiredService();
+#endif
var cacheOptions = serviceProvider.GetRequiredService>().Value;
// Act
var features = await cachedFeatureStore.GetFeaturesAsync();
+#if NET9_0_OR_GREATER
+ var cachedFeatures = await cache.GetOrCreateAsync(FeatureCacheOptions.CachePrefix + cacheOptions.KeyNames.AllFeatures, static (_) => ValueTask.FromResult(default(IReadOnlyCollection)));
+#else
var cachedFeatures = JsonSerializer.Deserialize>(await cache.GetAsync(FeatureCacheOptions.CachePrefix + cacheOptions.KeyNames.AllFeatures));
+#endif
// Assert
Assert.True(features is not null);
@@ -79,7 +102,7 @@ public async Task GetAllFeaturesFromCachedStore()
Assert.Equal(feature.Name, cachedFeature.Name);
- Assert.True(feature.Settings.Any());
+ Assert.True(feature.Settings.Count > 0);
Assert.Equivalent(feature.Settings, cachedFeature.Settings);
Assert.Equal(FeatureFilterType.TimeWindow, feature.Settings.First().FilterType);
@@ -98,19 +121,26 @@ public async Task GetFeatureFromCacheMustBeNullWhenCacheExpires()
var serviceProvider = serviceCollection.BuildServiceProvider();
var cachedFeatureStore = serviceProvider.GetRequiredService();
+#if NET9_0_OR_GREATER
+ var cache = serviceProvider.GetRequiredService();
+#else
var cache = serviceProvider.GetRequiredService();
+#endif
// Act
var feature = await cachedFeatureStore.GetFeatureAsync(FirstFeature);
await Task.Delay(1200);
+#if NET9_0_OR_GREATER
+ var featureCache = await cache.GetOrCreateAsync(FirstFeature, static (_) => ValueTask.FromResult(default(Feature)));
+#else
var featureCache = await cache.GetAsync(FirstFeature);
+#endif
// Assert
Assert.True(feature is not null);
Assert.True(featureCache is null);
}
-
-
+
[Fact]
public async Task GetAllFeaturesFromCachedStoreWhenFeaturesHaveCircularReference()
{
@@ -122,11 +152,19 @@ public async Task GetAllFeaturesFromCachedStoreWhenFeaturesHaveCircularReference
var serviceProvider = serviceCollection.BuildServiceProvider();
var cachedFeatureStore = serviceProvider.GetRequiredService();
+#if NET9_0_OR_GREATER
+ var cache = serviceProvider.GetRequiredService();
+#else
var cache = serviceProvider.GetRequiredService();
+#endif
// Act
var feature = await cachedFeatureStore.GetFeatureAsync(FirstFeature);
+#if NET9_0_OR_GREATER
+ var cachedFeature = await cache.GetOrCreateAsync(FeatureCacheOptions.CachePrefix + FirstFeature, static (_) => ValueTask.FromResult(default(Feature)));
+#else
var cachedFeature = JsonSerializer.Deserialize(await cache.GetAsync(FeatureCacheOptions.CachePrefix + FirstFeature));
+#endif
// Assert
Assert.True(feature is not null);
@@ -135,6 +173,6 @@ public async Task GetAllFeaturesFromCachedStoreWhenFeaturesHaveCircularReference
Assert.Equal(FirstFeature, feature.Name);
Assert.Equal(feature.Name, cachedFeature.Name);
- Assert.True(feature.Settings.Any());
+ Assert.True(feature.Settings.Count > 0);
}
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement.Database.CosmosDB.Tests.csproj b/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement.Database.CosmosDB.Tests.csproj
index 5918c37..ac99324 100644
--- a/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement.Database.CosmosDB.Tests.csproj
+++ b/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement.Database.CosmosDB.Tests.csproj
@@ -3,14 +3,13 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
-
@@ -19,18 +18,25 @@
-
-
+
+
+
-
+
+
+
+
+
+
+
diff --git a/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/IntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/IntegrationTestWebAppFactory.cs
index 5df6b6e..0210efd 100644
--- a/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/IntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/IntegrationTestWebAppFactory.cs
@@ -13,16 +13,24 @@ namespace FeatureManagement.Database.CosmosDB.Tests;
public sealed class IntegrationTestWebAppFactory : WebApplicationFactory, IAsyncLifetime
{
- private readonly CosmosDbContainer _cosmosDbContainer = new CosmosDbBuilder()
+ private readonly CosmosDbContainer _cosmosDbContainer;
+
+ internal string ConnectionString => _cosmosDbContainer.GetConnectionString();
+
+ public IntegrationTestWebAppFactory()
+ {
+ var containerName = GetUniqueContainerName("cosmosdb-test-container");
+
+ _cosmosDbContainer = new CosmosDbBuilder()
+ .WithName(containerName)
.WithImage("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest")
- .WithExposedPort(8081)
- .WithPortBinding(8081, true)
+ .WithExposedPort(CosmosDbBuilder.CosmosDbPort)
+ .WithPortBinding(CosmosDbBuilder.CosmosDbPort, assignRandomHostPort: true)
.WithEnvironment("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", "2")
.WithEnvironment("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(8081))
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(CosmosDbBuilder.CosmosDbPort))
.Build();
-
- internal string ConnectionString => _cosmosDbContainer.GetConnectionString();
+ }
public async Task InitializeAsync()
{
@@ -69,4 +77,9 @@ private void ConfigureServices(IServiceCollection services)
};
});
}
+
+ private static string GetUniqueContainerName(string baseName)
+ {
+ return $"{baseName}-{Guid.NewGuid().ToString("N")[..8]}";
+ }
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/Seed.cs b/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/Seed.cs
index 4f91343..43bcdf7 100644
--- a/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/Seed.cs
+++ b/tests/FeatureManagement.Database.CosmosDB.Tests/FeatureManagement/Database/CosmosDB/Tests/Seed.cs
@@ -21,53 +21,68 @@ internal static async Task SeedDataAsync(CosmosDBOptions cosmosDBOptions, ICosmo
var featuresContainer = cosmosDBConnectionFactory.GetFeaturesContainer();
var featureSettingsContainer = cosmosDBConnectionFactory.GetFeatureSettingsContainer();
+ // IDs and Partition Keys
+ const string Feature1Id = "7c81e846-dc77-4aff-bf03-8dd8bb2d3194";
+ const string Feature2Id = "d3c82992-2f12-4008-9376-da37695a2747";
+ const string FeatureSettingsId = "672dc1bd-9c5b-44ce-8461-234b262a8395";
+ var partitionKey1 = new PartitionKey(FirstFeature);
+ var partitionKey2 = new PartitionKey(SecondFeature);
+ var featureSettingsPartitionKey = new PartitionKey(Feature1Id);
+
try
{
- await featuresContainer.DeleteItemAsync("7c81e846-dc77-4aff-bf03-8dd8bb2d3194", new PartitionKey(FirstFeature));
- await featuresContainer.DeleteItemAsync("d3c82992-2f12-4008-9376-da37695a2747", new PartitionKey(SecondFeature));
+ // Delete existing items if they exist
+ await featuresContainer.DeleteItemAsync(Feature1Id, partitionKey1);
+ await featuresContainer.DeleteItemAsync(Feature2Id, partitionKey2);
if (cosmosDBOptions.UseSeparateContainers)
- await featureSettingsContainer.DeleteItemAsync("672dc1bd-9c5b-44ce-8461-234b262a8395", new PartitionKey("7c81e846-dc77-4aff-bf03-8dd8bb2d3194"));
+ await featureSettingsContainer.DeleteItemAsync(FeatureSettingsId, featureSettingsPartitionKey);
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
- // Nothing: I can't delete something that doesn't exist
+ // Item not found, nothing to delete
}
- List features =
- [
- new()
- {
- Id = Guid.Parse("7C81E846-DC77-4AFF-BF03-8DD8BB2D3194"),
+ // Define new features and settings
+ var features = new List
+ {
+ new() {
+ Id = Guid.Parse(Feature1Id),
Name = FirstFeature,
RequirementType = Microsoft.FeatureManagement.RequirementType.All,
},
- new()
- {
- Id = Guid.Parse("D3C82992-2F12-4008-9376-DA37695A2747"),
+ new() {
+ Id = Guid.Parse(Feature2Id),
Name = SecondFeature,
RequirementType = Microsoft.FeatureManagement.RequirementType.All,
}
- ];
+ };
- List settings =
- [
- new()
- {
- Id = Guid.Parse("672DC1BD-9C5B-44CE-8461-234B262A8395"),
+ var settings = new List
+ {
+ new() {
+ Id = Guid.Parse(FeatureSettingsId),
FeatureId = features[0].Id,
FilterType = FeatureFilterType.TimeWindow,
Parameters = """{"Start": "Mon, 01 May 2023 13:59:59 GMT", "End": "Sat, 01 July 2023 00:00:00 GMT"}"""
}
- ];
+ };
if (!cosmosDBOptions.UseSeparateContainers)
features[0].Settings = settings;
- await featuresContainer.CreateItemAsync(new { id = features[0].Id.ToString(), features[0].Name, features[0].RequirementType, features[0].Settings }, new PartitionKey(features[0].Name));
- await featuresContainer.CreateItemAsync(new { id = features[1].Id.ToString(), features[1].Name, features[1].RequirementType }, new PartitionKey(features[1].Name));
+ try
+ {
+ // Insert new items
+ await featuresContainer.CreateItemAsync(new { id = features[0].Id.ToString(), features[0].Name, features[0].RequirementType, features[0].Settings }, partitionKey1);
+ await featuresContainer.CreateItemAsync(new { id = features[1].Id.ToString(), features[1].Name, features[1].RequirementType }, partitionKey2);
- if (cosmosDBOptions.UseSeparateContainers)
- await featureSettingsContainer.CreateItemAsync(new { id = settings[0].Id.ToString(), settings[0].FeatureId, settings[0].FilterType, settings[0].CustomFilterTypeName, settings[0].Parameters }, new PartitionKey(settings[0].FeatureId.ToString()));
+ if (cosmosDBOptions.UseSeparateContainers)
+ await featureSettingsContainer.CreateItemAsync(new { id = settings[0].Id.ToString(), settings[0].FeatureId, settings[0].FilterType, settings[0].Parameters }, featureSettingsPartitionKey);
+ }
+ catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Conflict)
+ {
+ // Do nothing when the item already exists
+ }
}
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.Dapper.Tests/FeatureManagement.Database.Dapper.Tests.csproj b/tests/FeatureManagement.Database.Dapper.Tests/FeatureManagement.Database.Dapper.Tests.csproj
index 4562117..2934407 100644
--- a/tests/FeatureManagement.Database.Dapper.Tests/FeatureManagement.Database.Dapper.Tests.csproj
+++ b/tests/FeatureManagement.Database.Dapper.Tests/FeatureManagement.Database.Dapper.Tests.csproj
@@ -3,14 +3,13 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
-
@@ -19,8 +18,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests.csproj b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests.csproj
index df3644b..ca600a7 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests.csproj
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests.csproj
@@ -3,7 +3,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlFeatureStoreWithCacheTests.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlFeatureStoreWithCacheTests.cs
index 15687bd..eb2c7cb 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlFeatureStoreWithCacheTests.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlFeatureStoreWithCacheTests.cs
@@ -3,7 +3,7 @@
using FeatureManagement.Database.EntityFrameworkCore.Tests;
-namespace FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests;
+namespace FeatureManagement.Database.EntityFrameworkCore.MySql.Tests;
public sealed class MySqlFeatureStoreWithCacheTests(MySqlWithCacheIntegrationTestWebAppFactory factory)
: EFCoreFeatureStoreTests(factory)
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlIntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlIntegrationTestWebAppFactory.cs
index b1ddb9b..a7685f7 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlIntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlIntegrationTestWebAppFactory.cs
@@ -11,28 +11,28 @@ namespace FeatureManagement.Database.EntityFrameworkCore.MySql.Tests;
public sealed class MySqlIntegrationTestWebAppFactory : IntegrationTestWebAppFactory
{
- private readonly MySqlContainer _mySqlContainer = new MySqlBuilder()
- .WithName("mysql-test-container")
- .WithImage("mysql:latest")
- .WithDatabase("TestDb")
- .WithUsername("root")
- .WithPassword("mysqlpassword")
- .WithPrivileged(true)
- .WithPortBinding(MySqlBuilder.MySqlPort)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MySqlBuilder.MySqlPort))
- .Build();
-
public MySqlIntegrationTestWebAppFactory()
{
- _container = _mySqlContainer;
+ var containerName = GetUniqueContainerName("mysql-test-container");
+
+ _container = new MySqlBuilder()
+ .WithName(containerName)
+ .WithImage("mysql:latest")
+ .WithDatabase("TestDb")
+ .WithUsername("root")
+ .WithPassword("mysqlpassword")
+ .WithPrivileged(true)
+ .WithPortBinding(MySqlBuilder.MySqlPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MySqlBuilder.MySqlPort))
+ .Build();
}
protected override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.AddDatabaseFeatureManagement()
- .UseMySql(_mySqlContainer.GetConnectionString(),
+ .UseMySql(_container.GetConnectionString(),
options => options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName));
}
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlWithCacheIntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlWithCacheIntegrationTestWebAppFactory.cs
index c4f8ca3..73c329d 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlWithCacheIntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.MySql.Tests/FeatureManagement/Database/EntityFrameworkCore/MySql/Tests/MySqlWithCacheIntegrationTestWebAppFactory.cs
@@ -7,32 +7,32 @@
using System.Reflection;
using Testcontainers.MySql;
-namespace FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests;
+namespace FeatureManagement.Database.EntityFrameworkCore.MySql.Tests;
public sealed class MySqlWithCacheIntegrationTestWebAppFactory : IntegrationTestWebAppFactory
{
- private readonly MySqlContainer _mySqlContainer = new MySqlBuilder()
- .WithName("mysql-test-container-cache")
- .WithImage("mysql:latest")
- .WithDatabase("TestDb")
- .WithUsername("root")
- .WithPassword("mysqlpassword")
- .WithPrivileged(true)
- .WithPortBinding(MySqlBuilder.MySqlPort, assignRandomHostPort: true)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MySqlBuilder.MySqlPort))
- .Build();
-
public MySqlWithCacheIntegrationTestWebAppFactory()
{
- _container = _mySqlContainer;
+ var containerName = GetUniqueContainerName("mysql-test-container-cache");
+
+ _container = new MySqlBuilder()
+ .WithName(containerName)
+ .WithImage("mysql:latest")
+ .WithDatabase("TestDb")
+ .WithUsername("root")
+ .WithPassword("mysqlpassword")
+ .WithPrivileged(true)
+ .WithPortBinding(MySqlBuilder.MySqlPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MySqlBuilder.MySqlPort))
+ .Build();
}
protected override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.AddDatabaseFeatureManagement()
- .UseMySql(_mySqlContainer.GetConnectionString(),
+ .UseMySql(_container.GetConnectionString(),
options => options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName))
.WithCacheService();
}
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests.csproj b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests.csproj
index 7306a7d..bcf7e6f 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests.csproj
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests.csproj
@@ -3,7 +3,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlFeatureStoreWithCacheTests.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlFeatureStoreWithCacheTests.cs
index e2b45c3..6911a01 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlFeatureStoreWithCacheTests.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlFeatureStoreWithCacheTests.cs
@@ -3,7 +3,7 @@
using FeatureManagement.Database.EntityFrameworkCore.Tests;
-namespace FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests;
+namespace FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests;
public sealed class PostgreSqlFeatureStoreWithCacheTests(PostgreSqlWithCacheIntegrationTestWebAppFactory factory)
: EFCoreFeatureStoreTests(factory)
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlIntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlIntegrationTestWebAppFactory.cs
index f98c6f1..11bbb89 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlIntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlIntegrationTestWebAppFactory.cs
@@ -11,24 +11,24 @@ namespace FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests;
public sealed class PostgreSqlIntegrationTestWebAppFactory : IntegrationTestWebAppFactory
{
- private readonly PostgreSqlContainer _postgreSqlContainer = new PostgreSqlBuilder()
- .WithName("postgresql-test-container")
- .WithImage("postgres:latest")
- .WithPortBinding(PostgreSqlBuilder.PostgreSqlPort)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(PostgreSqlBuilder.PostgreSqlPort))
- .Build();
-
public PostgreSqlIntegrationTestWebAppFactory()
{
- _container = _postgreSqlContainer;
+ var containerName = GetUniqueContainerName("postgresql-test-container");
+
+ _container = new PostgreSqlBuilder()
+ .WithName(containerName)
+ .WithImage("postgres:latest")
+ .WithPortBinding(PostgreSqlBuilder.PostgreSqlPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(PostgreSqlBuilder.PostgreSqlPort))
+ .Build();
}
protected override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.AddDatabaseFeatureManagement()
- .UseNpgsql(_postgreSqlContainer.GetConnectionString(),
+ .UseNpgsql(_container.GetConnectionString(),
options => options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName));
}
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlWithCacheIntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlWithCacheIntegrationTestWebAppFactory.cs
index 21030bb..4aefe7e 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlWithCacheIntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests/FeatureManagement/Database/EntityFrameworkCore/PostgreSQL/Tests/PostgreSqlWithCacheIntegrationTestWebAppFactory.cs
@@ -7,28 +7,28 @@
using System.Reflection;
using Testcontainers.PostgreSql;
-namespace FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests;
+namespace FeatureManagement.Database.EntityFrameworkCore.PostgreSQL.Tests;
public sealed class PostgreSqlWithCacheIntegrationTestWebAppFactory : IntegrationTestWebAppFactory
{
- private readonly PostgreSqlContainer _postgreSqlContainer = new PostgreSqlBuilder()
- .WithName("postgresql-test-container-cache")
- .WithImage("postgres:latest")
- .WithPortBinding(PostgreSqlBuilder.PostgreSqlPort, assignRandomHostPort: true)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(PostgreSqlBuilder.PostgreSqlPort))
- .Build();
-
public PostgreSqlWithCacheIntegrationTestWebAppFactory()
{
- _container = _postgreSqlContainer;
+ var containerName = GetUniqueContainerName("postgresql-test-container-cache");
+
+ _container = new PostgreSqlBuilder()
+ .WithName(containerName)
+ .WithImage("postgres:latest")
+ .WithPortBinding(PostgreSqlBuilder.PostgreSqlPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(PostgreSqlBuilder.PostgreSqlPort))
+ .Build();
}
protected override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.AddDatabaseFeatureManagement()
- .UseNpgsql(_postgreSqlContainer.GetConnectionString(),
+ .UseNpgsql(_container.GetConnectionString(),
options => options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName))
.WithCacheService();
}
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests.csproj b/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests.csproj
index dfb7b9b..00d4a47 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests.csproj
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests.csproj
@@ -3,7 +3,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerIntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerIntegrationTestWebAppFactory.cs
index 62dadde..adb681f 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerIntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerIntegrationTestWebAppFactory.cs
@@ -11,25 +11,25 @@ namespace FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests;
public sealed class SqlServerIntegrationTestWebAppFactory : IntegrationTestWebAppFactory
{
- private readonly MsSqlContainer _sqlServerContainer = new MsSqlBuilder()
- .WithName("sqlserver-test-container")
- .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
- .WithEnvironment("ACCEPT_EULA", "Y")
- .WithPortBinding(MsSqlBuilder.MsSqlPort)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlBuilder.MsSqlPort))
- .Build();
-
public SqlServerIntegrationTestWebAppFactory()
{
- _container = _sqlServerContainer;
+ var containerName = GetUniqueContainerName("sqlserver-test-container");
+
+ _container = new MsSqlBuilder()
+ .WithName(containerName)
+ .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
+ .WithEnvironment("ACCEPT_EULA", "Y")
+ .WithPortBinding(MsSqlBuilder.MsSqlPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlBuilder.MsSqlPort))
+ .Build();
}
protected override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.AddDatabaseFeatureManagement()
- .UseSqlServer(_sqlServerContainer.GetConnectionString(),
+ .UseSqlServer(_container.GetConnectionString(),
options => options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName));
}
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerWithCacheIntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerWithCacheIntegrationTestWebAppFactory.cs
index 13f14d7..ac71142 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerWithCacheIntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests/FeatureManagement/Database/EntityFrameworkCore/SqlServer/Tests/SqlServerWithCacheIntegrationTestWebAppFactory.cs
@@ -11,25 +11,25 @@ namespace FeatureManagement.Database.EntityFrameworkCore.SqlServer.Tests;
public sealed class SqlServerWithCacheIntegrationTestWebAppFactory : IntegrationTestWebAppFactory
{
- private readonly MsSqlContainer _sqlServerContainer = new MsSqlBuilder()
- .WithName("sqlserver-test-container-with-cache")
- .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
- .WithEnvironment("ACCEPT_EULA", "Y")
- .WithPortBinding(MsSqlBuilder.MsSqlPort, assignRandomHostPort: true)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlBuilder.MsSqlPort))
- .Build();
-
public SqlServerWithCacheIntegrationTestWebAppFactory()
{
- _container = _sqlServerContainer;
+ var containerName = GetUniqueContainerName("sqlserver-test-container-with-cache");
+
+ _container = new MsSqlBuilder()
+ .WithName(containerName)
+ .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
+ .WithEnvironment("ACCEPT_EULA", "Y")
+ .WithPortBinding(MsSqlBuilder.MsSqlPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlBuilder.MsSqlPort))
+ .Build();
}
protected override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.AddDatabaseFeatureManagement()
- .UseSqlServer(_sqlServerContainer.GetConnectionString(),
+ .UseSqlServer(_container.GetConnectionString(),
options => options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName))
.WithCacheService();
}
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests.csproj b/tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests.csproj
index 7a4fd2b..4123652 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests.csproj
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests/FeatureManagement.Database.EntityFrameworkCore.Sqlite.Tests.csproj
@@ -3,7 +3,7 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement.Database.EntityFrameworkCore.Tests.csproj b/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement.Database.EntityFrameworkCore.Tests.csproj
index b9563d3..eb73d59 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement.Database.EntityFrameworkCore.Tests.csproj
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement.Database.EntityFrameworkCore.Tests.csproj
@@ -3,15 +3,13 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
-
-
@@ -26,13 +24,27 @@
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
@@ -42,6 +54,8 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement/Database/EntityFrameworkCore/Tests/IntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement/Database/EntityFrameworkCore/Tests/IntegrationTestWebAppFactory.cs
index 586d089..0c4844a 100644
--- a/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement/Database/EntityFrameworkCore/Tests/IntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.EntityFrameworkCore.Tests/FeatureManagement/Database/EntityFrameworkCore/Tests/IntegrationTestWebAppFactory.cs
@@ -37,6 +37,11 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
protected virtual void ConfigureServices(IServiceCollection services)
{
- services.RemoveAll(typeof(DbContextOptions));
+ services.RemoveAll>();
+ }
+
+ protected static string GetUniqueContainerName(string baseName)
+ {
+ return $"{baseName}-{Guid.NewGuid().ToString("N")[..8]}";
}
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement.Database.MongoDB.Tests.csproj b/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement.Database.MongoDB.Tests.csproj
index b612dbf..6386ab0 100644
--- a/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement.Database.MongoDB.Tests.csproj
+++ b/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement.Database.MongoDB.Tests.csproj
@@ -3,14 +3,13 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
-
@@ -23,12 +22,19 @@
-
+
+
+
+
+
+
+
+
diff --git a/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement/Database/MongoDB/Tests/IntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement/Database/MongoDB/Tests/IntegrationTestWebAppFactory.cs
index 551f29f..e5b5412 100644
--- a/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement/Database/MongoDB/Tests/IntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.MongoDB.Tests/FeatureManagement/Database/MongoDB/Tests/IntegrationTestWebAppFactory.cs
@@ -12,20 +12,26 @@ namespace FeatureManagement.Database.MongoDB.Tests;
public sealed class IntegrationTestWebAppFactory : WebApplicationFactory, IAsyncLifetime
{
- private readonly MongoDbContainer _mongoDBContainer = new MongoDbBuilder()
- .WithName("mongodb-test-container")
- .WithImage("mongo:latest")
- .WithUsername(null)
- .WithPassword(null)
- .WithPortBinding(MongoDbBuilder.MongoDbPort)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MongoDbBuilder.MongoDbPort))
- .Build();
-
+ private readonly MongoDbContainer _mongoDBContainer;
private const string _Database = "TestDb";
internal string ConnectionString => _mongoDBContainer.GetConnectionString();
+ public IntegrationTestWebAppFactory()
+ {
+ var containerName = GetUniqueContainerName("mongodb-test-container");
+
+ _mongoDBContainer = new MongoDbBuilder()
+ .WithName(containerName)
+ .WithImage("mongo:latest")
+ .WithUsername(null)
+ .WithPassword(null)
+ .WithPortBinding(MongoDbBuilder.MongoDbPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MongoDbBuilder.MongoDbPort))
+ .Build();
+ }
+
public async Task InitializeAsync()
{
await _mongoDBContainer.StartAsync();
@@ -50,4 +56,9 @@ private void ConfigureServices(IServiceCollection services)
services.AddDatabaseFeatureManagement()
.UseMongoDB(ConnectionString, _Database);
}
+
+ private static string GetUniqueContainerName(string baseName)
+ {
+ return $"{baseName}-{Guid.NewGuid().ToString("N")[..8]}";
+ }
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement.Database.NHibernate.Tests.csproj b/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement.Database.NHibernate.Tests.csproj
index 8654495..c6e15da 100644
--- a/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement.Database.NHibernate.Tests.csproj
+++ b/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement.Database.NHibernate.Tests.csproj
@@ -3,14 +3,13 @@
- net6.0;net8.0
+ net6.0;net8.0;net9.0
false
true
-
@@ -25,12 +24,19 @@
-
+
+
+
+
+
+
+
+
diff --git a/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/IntegrationTestWebAppFactory.cs b/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/IntegrationTestWebAppFactory.cs
index 5ad0b9a..b868892 100644
--- a/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/IntegrationTestWebAppFactory.cs
+++ b/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/IntegrationTestWebAppFactory.cs
@@ -12,14 +12,23 @@ namespace FeatureManagement.Database.NHibernate.Tests;
public class IntegrationTestWebAppFactory : WebApplicationFactory, IAsyncLifetime
{
- private readonly MsSqlContainer _sqlServerContainer = new MsSqlBuilder()
- .WithName("sqlserver-test-container")
- .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
- .WithEnvironment("ACCEPT_EULA", "Y")
- .WithPortBinding(MsSqlBuilder.MsSqlPort)
- .WithCleanUp(true)
- .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlBuilder.MsSqlPort))
- .Build();
+ private readonly MsSqlContainer _sqlServerContainer;
+
+ public IntegrationTestWebAppFactory()
+ {
+ var containerName = GetUniqueContainerName("nhibernate-sqlserver-test-container");
+
+ _sqlServerContainer = new MsSqlBuilder()
+ .WithName(containerName)
+ .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
+ .WithEnvironment("ACCEPT_EULA", "Y")
+ .WithEnvironment("SA_USERNAME", MsSqlBuilder.DefaultUsername)
+ .WithEnvironment("SA_PASSWORD", MsSqlBuilder.DefaultPassword)
+ .WithPortBinding(MsSqlBuilder.MsSqlPort, assignRandomHostPort: true)
+ .WithCleanUp(true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlBuilder.MsSqlPort))
+ .Build();
+ }
public async Task InitializeAsync()
{
@@ -46,4 +55,9 @@ protected virtual void ConfigureServices(IServiceCollection services)
services.AddDatabaseFeatureManagement()
.UseNHibernate(_sqlServerContainer.GetConnectionString(), new SqlServerConnectionFactory());
}
+
+ private static string GetUniqueContainerName(string baseName)
+ {
+ return $"{baseName}-{Guid.NewGuid().ToString("N")[..8]}";
+ }
}
\ No newline at end of file
diff --git a/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/Seed.cs b/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/Seed.cs
index 00323e3..c472d62 100644
--- a/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/Seed.cs
+++ b/tests/FeatureManagement.Database.NHibernate.Tests/FeatureManagement/Database/NHibernate/Tests/Seed.cs
@@ -40,7 +40,7 @@ internal static async Task SeedData(ISessionFactory sessionFactory)
};
// Add settings to the feature
- features[0].Settings = [ featureSetting ];
+ features[0].Settings = [featureSetting];
// Save entities to the database
await session.SaveAsync(featureSetting);