Test host: bind Kestrel to Site:Authority instead of dynamic port
The Yavsc.Org integration tests were failing 'Internal Server Error' on
the OIDC discovery document when run as part of the full test suite.
Root cause: WebHostFixture bound Kestrel to IPAddress.Loopback on a
dynamically-allocated port and exposed it via IServerAddressesFeature.
But the OIDC issuer URLs (and the issuer claim) come from Site:Authority,
which was left at the production value (mercure.pschneider.fr). So
IdentityServer8's discovery document advertised URLs unreachable from
the test process, and the discovery call returned a 500.
Fix:
- WebServerFixture now overrides Site:Authority and Site:ExternalUrl
in AddInMemoryCollection to 'https://localhost:44300' (the ASP.NET
Core dev HTTPS convention).
- WebHostFixture reads Site:Authority from configuration and binds
Kestrel to that fixed URL. The exposed Addresses list is sourced
from the same configuration value instead of the
IServerAddressesFeature, so the listen URL and the OIDC issuer
URLs always match.
Remoting.cs (Mandatory/Remoting.cs): add 'using
Microsoft.Extensions.DependencyInjection;' so the existing OIDC/DB
diagnostic block (capture raw HTTP response + dump OIDC-related DB
state on discovery failure) compiles. The diagnostic itself is left
in place — it's what surfaced the 500 in the first place.
This commit is contained in:
parent
becd233593
commit
402bcc1db8
3 changed files with 76 additions and 17 deletions
|
|
@ -1,7 +1,5 @@
|
|||
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;
|
||||
|
|
@ -16,14 +14,17 @@ 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>
|
||||
/// bound to the URL declared in configuration under
|
||||
/// <c>Site:Authority</c> (port fixed by the specialisation — no
|
||||
/// port collisions since all Yavsc.Org tests share a single
|
||||
/// collection).</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
|
||||
/// fixture, lifted out of the specialisation.</description></item>
|
||||
/// <item><description>Address discovery via
|
||||
/// <see cref="IServerAddressesFeature"/>.</description></item>
|
||||
/// <item><description>Address list sourced from
|
||||
/// <c>Site:Authority</c> so the listen URL and the OIDC
|
||||
/// discovery / <c>issuer</c> URLs always match.</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// The actual service registration, middleware pipeline and route
|
||||
|
|
@ -108,9 +109,20 @@ 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, 0, listenOptions =>
|
||||
options.Listen(IPAddress.Loopback, authorityUri.Port, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps(_selfSignedCertificate.Value);
|
||||
});
|
||||
|
|
@ -123,16 +135,13 @@ public abstract class WebHostFixture : IDisposable
|
|||
_app = app;
|
||||
_sharedServices = app.Services;
|
||||
|
||||
var server = app.Services.GetRequiredService<IServer>();
|
||||
var addressFeatures = server.Features.Get<IServerAddressesFeature>();
|
||||
// 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.
|
||||
_sharedAddresses.Clear();
|
||||
if (addressFeatures?.Addresses is not null)
|
||||
{
|
||||
foreach (var address in addressFeatures.Addresses)
|
||||
{
|
||||
_sharedAddresses.Add(address);
|
||||
}
|
||||
}
|
||||
_sharedAddresses.Add(authority.TrimEnd('/') + "/");
|
||||
Addresses = _sharedAddresses.ToArray();
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue