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); }; } }