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