diff --git a/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs b/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs
index 2b8bf705..6e39fd00 100644
--- a/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs
+++ b/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs
@@ -48,8 +48,6 @@ namespace Yavsc.Blogs.Tests;
///
public sealed class BlogsWebServerFixture : WebHostFixture
{
- protected override int HttpsPort => 5103;
-
private InMemoryDatabaseRoot? _inMemoryRoot;
protected override WebApplication BuildApp(WebApplicationBuilder builder)
diff --git a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs
index 73f3da18..0f829c04 100644
--- a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs
+++ b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs
@@ -18,7 +18,7 @@ namespace Yavsc.Org.Tests
[Fact]
public async Task ObtainServiceToken()
{
- var serverUrl = _serverFixture.SiteSettings.Authority;
+ var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
if (string.IsNullOrEmpty(serverUrl))
throw new InvalidOperationException("No HTTPS server address found");
@@ -45,7 +45,7 @@ namespace Yavsc.Org.Tests
[Fact]
public async Task ObtainResourceOwnerPasswordToken()
{
- var serverUrl = _serverFixture.SiteSettings.Authority;
+ var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
if (string.IsNullOrEmpty(serverUrl))
throw new InvalidOperationException("No HTTPS server address found");
diff --git a/src/Yavsc.Org.Tests/WebServerFixture.cs b/src/Yavsc.Org.Tests/WebServerFixture.cs
index a623bc9e..daa16098 100644
--- a/src/Yavsc.Org.Tests/WebServerFixture.cs
+++ b/src/Yavsc.Org.Tests/WebServerFixture.cs
@@ -8,8 +8,6 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using System.Net;
-using System.Net.Sockets;
using Yavsc;
using Yavsc.Extensions;
using Yavsc.Interfaces;
@@ -41,10 +39,6 @@ namespace Yavsc.Org.Tests;
[CollectionDefinition("Yavsc Server")]
public sealed class WebServerFixture : WebHostFixture
{
- private static readonly int _httpsPort = GetAvailableLoopbackPort();
-
- protected override int HttpsPort => _httpsPort;
-
private static IConfiguration? _sharedConfiguration;
private static SiteSettings? _sharedSiteSettings;
private static ILogger? _sharedLogger;
@@ -70,8 +64,6 @@ public sealed class WebServerFixture : WebHostFixture
protected override WebApplication BuildApp(WebApplicationBuilder builder)
{
- var authority = $"https://localhost:{_httpsPort}";
-
// WebApplication.CreateBuilder defaults WebRootPath to
// {ContentRoot}/wwwroot. The test assembly runs from
// src/Yavsc.Org.Tests/bin/.../, which has no wwwroot of
@@ -89,7 +81,6 @@ public sealed class WebServerFixture : WebHostFixture
["Smtp:Port"] = "465",
["Smtp:UserName"] = "test-user",
["Smtp:Password"] = "test-pass",
- ["Site:Authority"] = authority
});
Configuration = builder.Configuration;
@@ -285,19 +276,4 @@ public sealed class WebServerFixture : WebHostFixture
TestingUser = dbContext.Users.FirstOrDefault(u => u.UserName == testingUserName);
}
}
-
- private static int GetAvailableLoopbackPort()
- {
- var listener = new TcpListener(IPAddress.Loopback, 0);
- listener.Start();
-
- try
- {
- return ((IPEndPoint)listener.LocalEndpoint).Port;
- }
- finally
- {
- listener.Stop();
- }
- }
}
diff --git a/src/Yavsc.Org.Tests/appsettings.json b/src/Yavsc.Org.Tests/appsettings.json
index 5f353cd8..bf8599e0 100644
--- a/src/Yavsc.Org.Tests/appsettings.json
+++ b/src/Yavsc.Org.Tests/appsettings.json
@@ -1,6 +1,6 @@
{
"Site": {
- "Authority": "https://localhost:5101",
+ "Authority": "https://mercure.pschneider.fr",
"Title": "Yavsc dev",
"Slogan": "Yavsc : WIP.",
"Banner": "/images/yavsc.png",
@@ -62,16 +62,6 @@
"UserName": "fakeuser",
"Password": "f/\\kePassw0rd"
}
- },
- "Kestrel": {
- "Endpoints": {
- "Http": {
- "Url": "http://localhost:5100"
- },
- "Https": {
- "Url": "https://localhost:5101"
- }
- }
}
-
+
}
diff --git a/src/Yavsc.Org/Migrations/20260711173717_EnforceBlogAuthorFKs.cs b/src/Yavsc.Org/Migrations/20260711173717_EnforceBlogAuthorFKs.cs
index 6e3e0f06..b13ef467 100644
--- a/src/Yavsc.Org/Migrations/20260711173717_EnforceBlogAuthorFKs.cs
+++ b/src/Yavsc.Org/Migrations/20260711173717_EnforceBlogAuthorFKs.cs
@@ -10,6 +10,29 @@ namespace Yavsc.Migrations
///
protected override void Up(MigrationBuilder migrationBuilder)
{
+ // Assainir les orphelins AVANT d'enforcer la FK Restrict.
+ // En prod (Postgres), la migration aurait sinon planté
+ // sur des billets/commentaires dont l'AuthorId pointe
+ // vers un user déjà supprimé. La logique métier refuse
+ // désormais l'orphelin (cf. BlogSpotService.Details) — on
+ // aligne l'état de la base avec ce contrat.
+ migrationBuilder.Sql(@"
+ DO $$
+ DECLARE n_comments int;
+ n_posts int;
+ BEGIN
+ DELETE FROM ""Comment""
+ WHERE ""AuthorId"" NOT IN (SELECT ""Id"" FROM ""AspNetUsers"");
+ GET DIAGNOSTICS n_comments = ROW_COUNT;
+
+ DELETE FROM ""BlogSpot""
+ WHERE ""AuthorId"" NOT IN (SELECT ""Id"" FROM ""AspNetUsers"");
+ GET DIAGNOSTICS n_posts = ROW_COUNT;
+
+ RAISE NOTICE 'EnforceBlogAuthorFKs: % orphaned comments deleted, % orphaned blog posts deleted',
+ n_comments, n_posts;
+ END $$;
+ ");
migrationBuilder.DropForeignKey(
name: "FK_BlogSpot_AspNetUsers_AuthorId",
diff --git a/src/Yavsc.Tests.Shared/WebHostFixture.cs b/src/Yavsc.Tests.Shared/WebHostFixture.cs
index 417d29d8..fb2e5984 100644
--- a/src/Yavsc.Tests.Shared/WebHostFixture.cs
+++ b/src/Yavsc.Tests.Shared/WebHostFixture.cs
@@ -16,8 +16,8 @@ namespace Yavsc.Tests.Shared;
///
///
/// - Kestrel with a self-signed HTTPS certificate
-/// on a fixture-defined fixed port for deterministic integration
-/// test endpoints.
+/// on a dynamically-allocated port (no port collisions between
+/// parallel xUnit test classes).
/// - A per-process single-instance host initialised
/// on first construction and torn down when the last fixture is
/// disposed — same lazy + lock + count pattern as the original Org
@@ -86,12 +86,11 @@ public abstract class WebHostFixture : IDisposable
protected virtual void CopySpecialisedSharedState() { }
/// Specialisations register their services and middleware
- /// here. The base class has already configured Kestrel HTTPS on
- /// the fixture-defined test port — do not bind additional
- /// listeners.
+ /// here. The base class has already configured Kestrel HTTPS on a
+ /// dynamic port — do not bind additional listeners.
/// The
- /// configured with Kestrel HTTPS on the fixture-defined test port
- /// and the shared self-signed certificate.
+ /// configured with Kestrel HTTPS on a dynamic port and the shared
+ /// self-signed certificate.
/// The fully built , ready
/// for ConfigurePipeline + StartAsync.
protected abstract WebApplication BuildApp(WebApplicationBuilder builder);
@@ -105,18 +104,13 @@ public abstract class WebHostFixture : IDisposable
return app;
}
- /// HTTPS port used by this fixture's Kestrel host.
- /// Override in derived fixtures when they must not share the same
- /// listen port.
- protected virtual int HttpsPort => 5101;
-
private async Task InitializeAsync()
{
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options =>
{
- options.Listen(IPAddress.Loopback, HttpsPort, listenOptions =>
+ options.Listen(IPAddress.Loopback, 0, listenOptions =>
{
listenOptions.UseHttps(_selfSignedCertificate.Value);
});