d3ro-voice/apps/api-server.Tests/AdminAuthorizationE2ETests.cs
2026-08-29 18:33:45 +09:00

119 lines
4.7 KiB
C#

using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Xunit;
namespace D3ROVoice.Api.Tests;
[CollectionDefinition("Api server integration", DisableParallelization = true)]
public sealed class ApiServerIntegrationCollection;
[Collection("Api server integration")]
public sealed class AdminAuthorizationE2ETests : IClassFixture<AdminAuthorizationE2ETests.ApiFactory>
{
private const string Secret = "admin-e2e-jwt-secret-0123456789-abcdef";
private const string Issuer = "https://admin-e2e.test";
private const string Audience = "d3ro-admin-e2e";
private readonly ApiFactory _factory;
public AdminAuthorizationE2ETests(ApiFactory factory)
{
_factory = factory;
}
[Fact]
public async Task AdminReadEndpointsRejectAnonymousAndNonManager()
{
using var client = _factory.CreateClient();
Assert.Equal(HttpStatusCode.Unauthorized, (await client.GetAsync("/api/admin/stats")).StatusCode);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("User"));
Assert.Equal(HttpStatusCode.Forbidden, (await client.GetAsync("/api/admin/stats")).StatusCode);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("manager"));
Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/api/admin/stats")).StatusCode);
}
[Fact]
public async Task AdminMutationRejectsManagerAndAcceptsNormalizedSuperAdmin()
{
using var client = _factory.CreateClient();
using var emptyPayload = new StringContent("{}", Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("Manager"));
Assert.Equal(HttpStatusCode.Forbidden,
(await client.PostAsync("/api/admin/endpoints", emptyPayload)).StatusCode);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("super_admin"));
using var secondPayload = new StringContent("{}", Encoding.UTF8, "application/json");
Assert.Equal(HttpStatusCode.BadRequest,
(await client.PostAsync("/api/admin/endpoints", secondPayload)).StatusCode);
}
private static string Token(string role)
{
var credentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret)),
SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: Issuer,
audience: Audience,
claims:
[
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim(ClaimTypes.Email, "admin@example.com"),
new Claim(ClaimTypes.Role, role)
],
expires: DateTime.UtcNow.AddMinutes(5),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public sealed class ApiFactory : WebApplicationFactory<Program>
{
private readonly string _databasePath = Path.Combine(
Path.GetTempPath(), $"d3ro-admin-auth-e2e-{Guid.NewGuid():N}.db");
private readonly Dictionary<string, string?> _previousEnvironment = new();
public ApiFactory()
{
SetEnvironment("JWT_SECRET", Secret);
SetEnvironment("JWT_ISSUER", Issuer);
SetEnvironment("JWT_AUDIENCE", Audience);
SetEnvironment("D3RO_API_TOKEN", "fixture-internal-gateway-token-32-bytes-minimum");
SetEnvironment("DB_PATH", _databasePath);
SetEnvironment("CORS_ALLOWED_ORIGINS", "http://localhost:3001");
SetEnvironment("ALLOWED_HOSTS", "localhost;127.0.0.1");
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development");
builder.ConfigureLogging(logging => logging.ClearProviders());
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
SqliteConnection.ClearAllPools();
if (File.Exists(_databasePath)) File.Delete(_databasePath);
foreach (var (name, value) in _previousEnvironment)
{
Environment.SetEnvironmentVariable(name, value);
}
}
private void SetEnvironment(string name, string value)
{
_previousEnvironment[name] = Environment.GetEnvironmentVariable(name);
Environment.SetEnvironmentVariable(name, value);
}
}
}