using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Yavsc.Org.Tests;
///
/// WebApplicationFactory-based fixture for integration tests that need
/// to override services registered by the production Program.
/// Uses the in-memory so tests can hit real
/// HTTP endpoints without sockets or self-signed certificates.
///
/// Currently overrides so that
/// [Authorize("AdministratorOnly")] (and any other policy
/// requiring a role) is satisfied by sending an
/// X-Test-Role: Administrator header, without a real login.
/// Also injects a middleware that promotes the same header into a
/// real on HttpContext.User so
/// that user code reading User.GetUserId() sees a logged-in
/// identity.
///
public class TestWebApplicationFactory : WebApplicationFactory
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// UseDevelopmentEnvironment triggers the dev signing credential
// path in the production startup, so we don't need a real cert
// to satisfy IdentityServer at boot.
builder.UseEnvironment("Development");
builder.ConfigureTestServices(services =>
{
// Replace the production IAuthorizationPolicyProvider with
// the test one. The default registered by AddAuthorization
// becomes irrelevant: any GetPolicyAsync call is routed here.
services.AddSingleton();
});
// Promote the X-Test-Role header to an authenticated identity
// on the request, so anything that reads User.GetUserId() (or
// any other claim-based helper) downstream sees a logged-in
// user. The policy provider above only short-circuits
// [Authorize(...)] checks; it does not touch HttpContext.User.
builder.Configure(app =>
{
app.Use(InjectTestUser);
});
}
private static RequestDelegate InjectTestUser(RequestDelegate next)
{
return async ctx =>
{
var role = ctx.Request.Headers[TestAuthPolicyProvider.HeaderName].ToString();
if (!string.IsNullOrEmpty(role) &&
(ctx.User.Identity is null || !ctx.User.Identity.IsAuthenticated))
{
var identity = new ClaimsIdentity(
new[]
{
new Claim(
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
role),
new Claim(ClaimTypes.NameIdentifier, "test-user"),
},
authenticationType: "TestAuth");
ctx.User = new ClaimsPrincipal(identity);
}
await next(ctx);
};
}
}