From 09604092c5b599a0ce183377f679396e78af4ab8 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 12 Jul 2026 03:48:49 +0100 Subject: [PATCH] Revert "Test host: bind Kestrel to Site:Authority instead of dynamic port" This reverts commit 402bcc1db8e789e2ba9b675b2c8b698c74ca330c. --- src/Yavsc.Org.Tests/Mandatory/Remoting.cs | 39 +-------------------- src/Yavsc.Org.Tests/WebServerFixture.cs | 13 ------- src/Yavsc.Tests.Shared/WebHostFixture.cs | 41 +++++++++-------------- 3 files changed, 17 insertions(+), 76 deletions(-) diff --git a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs index 0e69589e..0f829c04 100644 --- a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs +++ b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs @@ -1,7 +1,6 @@ using System.Security.Cryptography.X509Certificates; using System.Net.Security; using IdentityModel.Client; -using Microsoft.Extensions.DependencyInjection; namespace Yavsc.Org.Tests { @@ -25,43 +24,7 @@ namespace Yavsc.Org.Tests HttpClient client = NewHttpClient(); var disco = await client.GetDiscoveryDocumentAsync(serverUrl); - if (disco.IsError) - { - // Diagnostic 2026-07-12 : capture the raw HTTP response - // AND dump the OIDC-related DB state so we can pinpoint - // which state is corrupt when the discovery is broken. - var rawResp = await client.GetAsync(serverUrl + "/.well-known/openid-configuration"); - var body = await rawResp.Content.ReadAsStringAsync(); - - string dbState = "no logger"; - try - { - using var scope = _serverFixture.Services.CreateScope(); - var cfg = scope.ServiceProvider - .GetRequiredService(); - var clients = cfg.Clients.Select(c => new { - c.Id, c.ClientId, c.Enabled, c.RequireClientSecret - }).ToList(); - var apiScopes = cfg.ApiScopes.Select(s => new { s.Name, s.Enabled }).ToList(); - var apiResources = cfg.ApiResources.Select(r => new { r.Name, r.Enabled }).ToList(); - var identityResources = cfg.IdentityResources.Select(r => new { r.Name, r.Enabled }).ToList(); - dbState = $"clients={System.Text.Json.JsonSerializer.Serialize(clients)}\n" + - $"apiScopes={System.Text.Json.JsonSerializer.Serialize(apiScopes)}\n" + - $"apiResources={System.Text.Json.JsonSerializer.Serialize(apiResources)}\n" + - $"identityResources={System.Text.Json.JsonSerializer.Serialize(identityResources)}"; - } - catch (Exception dumpEx) - { - dbState = $"dump failed: {dumpEx.Message}"; - } - - throw new Exception( - $"disco.Error={disco.Error}\n" + - $"HTTP status={(int)rawResp.StatusCode}\n" + - $"Body[0..2000]:\n{body.Substring(0, Math.Min(2000, body.Length))}\n" + - $"---\n" + - $"OIDC DB state at failure:\n{dbState}"); - } + if (disco.IsError) throw new Exception(disco.Error); var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { diff --git a/src/Yavsc.Org.Tests/WebServerFixture.cs b/src/Yavsc.Org.Tests/WebServerFixture.cs index 9edcd449..daa16098 100644 --- a/src/Yavsc.Org.Tests/WebServerFixture.cs +++ b/src/Yavsc.Org.Tests/WebServerFixture.cs @@ -73,19 +73,6 @@ public sealed class WebServerFixture : WebHostFixture // that plus the in-memory overrides below. builder.AddConfiguration(null).AddInMemoryCollection(new Dictionary { - // Test host: fixed authority + external URL. Matches - // the Kestrel bind in WebHostFixture.InitializeAsync - // (https://localhost:44300) so IdentityServer8's - // discovery document and the test client agree on the - // same base URL. IdentityServer8 reads Site:Authority - // to populate the `issuer` claim, the discovery - // document's `issuer` and endpoint URLs — leaving it - // pointed at the production host (e.g. - // mercure.pschneider.fr) made /.well-known/openid-configuration - // return URLs unreachable from the test, hence - // "Internal Server Error" in the discovery call. - ["Site:Authority"] = "https://localhost:44300", - ["Site:ExternalUrl"] = "https://localhost:44300", [$"ConnectionStrings:{YavscConstants.YavscConnectionStringName}"] = "InMemory", // SMTP test config: UserName non-null so MailSender // exercises the Authenticate branch — the diff --git a/src/Yavsc.Tests.Shared/WebHostFixture.cs b/src/Yavsc.Tests.Shared/WebHostFixture.cs index 223ed466..fb2e5984 100644 --- a/src/Yavsc.Tests.Shared/WebHostFixture.cs +++ b/src/Yavsc.Tests.Shared/WebHostFixture.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.DependencyInjection; using System.Net; using System.Security.Cryptography; @@ -14,17 +16,14 @@ namespace Yavsc.Tests.Shared; /// /// /// Kestrel with a self-signed HTTPS certificate -/// bound to the URL declared in configuration under -/// Site:Authority (port fixed by the specialisation — no -/// port collisions since all Yavsc.Org tests share a single -/// collection). +/// 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 /// fixture, lifted out of the specialisation. -/// Address list sourced from -/// Site:Authority so the listen URL and the OIDC -/// discovery / issuer URLs always match. +/// Address discovery via +/// . /// /// /// The actual service registration, middleware pipeline and route @@ -109,20 +108,9 @@ public abstract class WebHostFixture : IDisposable { var builder = WebApplication.CreateBuilder(); - // Bind Kestrel to the URL the specialisation declared in - // Site:Authority (the same value IdentityServer8 reads to - // build its discovery document). Reading it from - // configuration makes the server URL and the issuer URLs - // refer to the same base — tests can just take - // _sharedAddresses[0] and trust it. - var authority = builder.Configuration["Site:Authority"] - ?? throw new InvalidOperationException( - "WebHostFixture: Site:Authority must be configured before InitializeAsync runs."); - var authorityUri = new Uri(authority); - builder.WebHost.ConfigureKestrel(options => { - options.Listen(IPAddress.Loopback, authorityUri.Port, listenOptions => + options.Listen(IPAddress.Loopback, 0, listenOptions => { listenOptions.UseHttps(_selfSignedCertificate.Value); }); @@ -135,13 +123,16 @@ public abstract class WebHostFixture : IDisposable _app = app; _sharedServices = app.Services; - // Source of truth for the listen URL is the configuration - // (Site:Authority) — not the IServerAddressesFeature, which - // can be a different representation (e.g. 127.0.0.1 vs - // localhost) and causes discovery / issuer mismatches when - // tests contact the host. + var server = app.Services.GetRequiredService(); + var addressFeatures = server.Features.Get(); _sharedAddresses.Clear(); - _sharedAddresses.Add(authority.TrimEnd('/') + "/"); + if (addressFeatures?.Addresses is not null) + { + foreach (var address in addressFeatures.Addresses) + { + _sharedAddresses.Add(address); + } + } Addresses = _sharedAddresses.ToArray(); IsInitialized = true; }