fix/testing #6
6 changed files with 53 additions and 34 deletions
|
|
@ -48,6 +48,8 @@ namespace Yavsc.Blogs.Tests;
|
|||
/// </summary>
|
||||
public sealed class BlogsWebServerFixture : WebHostFixture
|
||||
{
|
||||
protected override int HttpsPort => 5103;
|
||||
|
||||
private InMemoryDatabaseRoot? _inMemoryRoot;
|
||||
|
||||
protected override WebApplication BuildApp(WebApplicationBuilder builder)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Yavsc.Org.Tests
|
|||
[Fact]
|
||||
public async Task ObtainServiceToken()
|
||||
{
|
||||
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
||||
var serverUrl = _serverFixture.SiteSettings.Authority;
|
||||
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.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
||||
var serverUrl = _serverFixture.SiteSettings.Authority;
|
||||
if (string.IsNullOrEmpty(serverUrl))
|
||||
throw new InvalidOperationException("No HTTPS server address found");
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ 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;
|
||||
|
|
@ -39,6 +41,10 @@ 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;
|
||||
|
|
@ -64,6 +70,8 @@ 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
|
||||
|
|
@ -81,6 +89,7 @@ public sealed class WebServerFixture : WebHostFixture
|
|||
["Smtp:Port"] = "465",
|
||||
["Smtp:UserName"] = "test-user",
|
||||
["Smtp:Password"] = "test-pass",
|
||||
["Site:Authority"] = authority
|
||||
});
|
||||
|
||||
Configuration = builder.Configuration;
|
||||
|
|
@ -276,4 +285,19 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"Site": {
|
||||
"Authority": "https://mercure.pschneider.fr",
|
||||
"Authority": "https://localhost:5101",
|
||||
"Title": "Yavsc dev",
|
||||
"Slogan": "Yavsc : WIP.",
|
||||
"Banner": "/images/yavsc.png",
|
||||
|
|
@ -62,6 +62,16 @@
|
|||
"UserName": "fakeuser",
|
||||
"Password": "f/\\kePassw0rd"
|
||||
}
|
||||
},
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
"Http": {
|
||||
"Url": "http://localhost:5100"
|
||||
},
|
||||
"Https": {
|
||||
"Url": "https://localhost:5101"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,29 +10,6 @@ namespace Yavsc.Migrations
|
|||
/// <inheritdoc />
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ namespace Yavsc.Tests.Shared;
|
|||
///
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Kestrel with a self-signed HTTPS certificate
|
||||
/// on a dynamically-allocated port (no port collisions between
|
||||
/// parallel xUnit test classes).</description></item>
|
||||
/// on a fixture-defined fixed port for deterministic integration
|
||||
/// test endpoints.</description></item>
|
||||
/// <item><description>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,11 +86,12 @@ public abstract class WebHostFixture : IDisposable
|
|||
protected virtual void CopySpecialisedSharedState() { }
|
||||
|
||||
/// <summary>Specialisations register their services and middleware
|
||||
/// here. The base class has already configured Kestrel HTTPS on a
|
||||
/// dynamic port — do not bind additional listeners.</summary>
|
||||
/// here. The base class has already configured Kestrel HTTPS on
|
||||
/// the fixture-defined test port — do not bind additional
|
||||
/// listeners.</summary>
|
||||
/// <param name="builder">The <see cref="WebApplicationBuilder"/>
|
||||
/// configured with Kestrel HTTPS on a dynamic port and the shared
|
||||
/// self-signed certificate.</param>
|
||||
/// configured with Kestrel HTTPS on the fixture-defined test port
|
||||
/// and the shared self-signed certificate.</param>
|
||||
/// <returns>The fully built <see cref="WebApplication"/>, ready
|
||||
/// for <c>ConfigurePipeline</c> + <c>StartAsync</c>.</returns>
|
||||
protected abstract WebApplication BuildApp(WebApplicationBuilder builder);
|
||||
|
|
@ -104,13 +105,18 @@ public abstract class WebHostFixture : IDisposable
|
|||
return app;
|
||||
}
|
||||
|
||||
/// <summary>HTTPS port used by this fixture's Kestrel host.
|
||||
/// Override in derived fixtures when they must not share the same
|
||||
/// listen port.</summary>
|
||||
protected virtual int HttpsPort => 5101;
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||
options.Listen(IPAddress.Loopback, HttpsPort, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps(_selfSignedCertificate.Value);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue