using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Yavsc; using Yavsc.Models; using Yavsc.Server.Services; using Yavsc.Services; using Yavsc.Tests.Shared; namespace Yavsc.Blogs.Tests; /// /// Test host for the Yavsc.Blogs API surface. Specialisation of /// that wires up only the bits the /// blog API actually depends on: /// /// /// An in-memory /// (the real one — no mock) so BlogSpotService.Index can run /// against an empty table and return an empty list. /// A trivial /// stub: the GET index path doesn't read the file system, so any /// implementation is fine. /// The default /// from Microsoft.AspNetCore.Authorization. /// The test auth bypass from /// Yavsc.Tests.Shared so the [Authorize("BlogScope")] /// attribute on BlogApiController is satisfied when the /// test sends the X-Test-Role header. /// /// /// No IdentityServer, no SMTP, no static assets — the Org fixture /// owns all of that and we don't need any of it for blog integration /// tests. /// public sealed class BlogsWebServerFixture : WebHostFixture { protected override WebApplication BuildApp(WebApplicationBuilder builder) { // Use the real ApplicationDbContext with an in-memory store. // BlogSpotService reads _context.BlogSpot directly, so any // attempt to mock it would be wasted work; the real service // against an empty table returns an empty list, which is // exactly what the first test wants to assert. builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("Yavsc.Blogs.Tests")); // Trivial file-system auth: the GET index path never calls // into it, but the DI container needs an instance. builder.Services.AddSingleton( new NoopFileSystemAuthManager()); // Real BlogSpotService — same instance the production host // builds (ApplicationDbContext, IAuthorizationService, // IFileSystemAuthManager). builder.Services.AddScoped(); // The BlogApiController is reached through MVC, so register // MVC + the BlogScope authorization policy. builder.Services.AddControllers(); builder.Services.AddAuthorization(opt => { // Mirror the production "BlogScope" policy: any // authenticated user. The TestAuthPolicyProvider we // register below short-circuits the role check via the // X-Test-Role header. opt.AddPolicy("BlogScope", p => p.RequireAssertion(_ => true)); }); // Test auth bypass — swapped in BEFORE the host builds the // service collection, so it overrides any production // policy provider registered by AddAuthorization above. builder.Services.AddSingleton(); return builder.Build(); } protected override async Task ConfigurePipelineAsync(WebApplication app) { app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); await Task.CompletedTask; return app; } /// Trivial stub. The /// blog API endpoints exercised by the first tests don't read the /// file system, so the implementation can be a no-op. private sealed class NoopFileSystemAuthManager : IFileSystemAuthManager { public FileAccessRight GetFilePathAccess(ClaimsPrincipal user, string fileRelativePath) => FileAccessRight.None; public void SetAccess(long circleId, string normalizedFullPath, FileAccessRight access) { } } }