Files
tiku-backend.net/Tiku.IntegrationTests/Api/BackofficeUiBootstrapTests.cs
xiong 2c4a0bad6c
Some checks failed
ci / release-gate (push) Has been cancelled
fix:修复部分数据库查询问题
2026-08-03 14:22:06 +08:00

197 lines
8.5 KiB
C#

using System.Net;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.Security;
using Tiku.Domain.Common;
using Tiku.Domain.Identity;
using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;
namespace Tiku.IntegrationTests.Api;
public sealed class BackofficeUiBootstrapTests
{
[Fact]
public async Task TenantUiBootstrap_ReturnsOnlyMenusAllowedByEffectivePermissions()
{
var commandRecorder = new RecordingDbCommandInterceptor();
await using var factory = new ApiTestFactory(dbCommandInterceptor: commandRecorder);
await factory.SeedBuiltinBackofficeCatalogAsync();
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var roleId = Guid.NewGuid();
var phone = "13710000000";
await factory.SeedAsync(
new Tenant
{
Id = tenantId,
Slug = tenantId.ToString("N"),
Name = "Scoped UI Tenant",
Status = TenantStatus.Active,
Metadata = JsonDefaults.Object()
},
new User
{
Id = userId,
Phone = phone,
Name = "Dashboard Operator"
}.WithTestPassword(),
new TenantMembership
{
TenantId = tenantId,
UserId = userId,
Role = TenantRole.Student,
Status = MembershipStatus.Active
},
new TenantBackendRole
{
Id = roleId,
TenantId = tenantId,
Code = "dashboard_operator",
Name = "Dashboard Operator",
Status = BackendRoleStatus.Active,
DataScope = JsonSerializer.SerializeToElement(new { mode = "self" })
},
new TenantBackendRolePermission
{
TenantId = tenantId,
RoleId = roleId,
PermissionCode = BackendPermissions.TenantDashboardView
},
new TenantBackendUserRole
{
TenantId = tenantId,
UserId = userId,
RoleId = roleId
});
using var client = factory.CreateClient();
client.UseAccessToken(await client.LoginAsTenantAsync(tenantId, phone));
commandRecorder.Reset();
using var response = await client.GetAsync("/api/tenant/access/ui-bootstrap");
using var bootstrap = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var firstRequestCommands = commandRecorder.Snapshot();
commandRecorder.Reset();
using var repeatedResponse = await client.GetAsync("/api/tenant/access/ui-bootstrap");
var repeatedRequestCommands = commandRecorder.Snapshot();
using var roleManagementResponse = await client.GetAsync("/api/tenant/access/bootstrap");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(HttpStatusCode.OK, repeatedResponse.StatusCode);
Assert.Equal(
[BackendPermissions.TenantDashboardView],
bootstrap.RootElement.GetProperty("permissionCodes").EnumerateArray().Select(item => item.GetString()));
Assert.Equal(
["tenant.dashboard"],
bootstrap.RootElement.GetProperty("menus").EnumerateArray()
.Select(item => item.GetProperty("code").GetString()));
Assert.True(
firstRequestCommands.Count is >= 1 and <= 10,
$"Expected at most 10 SQL commands, but captured {firstRequestCommands.Count}:{Environment.NewLine}{string.Join($"{Environment.NewLine}---{Environment.NewLine}", firstRequestCommands)}");
Assert.InRange(repeatedRequestCommands.Count, 1, firstRequestCommands.Count);
AssertNoPerCodeCatalogExistenceQueries(firstRequestCommands);
AssertNoPerCodeCatalogExistenceQueries(repeatedRequestCommands);
Assert.Equal(HttpStatusCode.Forbidden, roleManagementResponse.StatusCode);
}
[Fact]
public async Task PlatformUiBootstrap_DoesNotProbeCatalogPerCode()
{
var commandRecorder = new RecordingDbCommandInterceptor();
await using var factory = new ApiTestFactory(
configurationOverrides: new Dictionary<string, string?>
{
["Tenancy:Resolution:PlatformHosts:0"] = "localhost"
},
dbCommandInterceptor: commandRecorder);
await factory.SeedBuiltinBackofficeCatalogAsync();
var userId = Guid.NewGuid();
var roleId = Guid.NewGuid();
var email = $"platform-{Guid.NewGuid():N}@example.test";
await factory.SeedAsync(
new User
{
Id = userId,
Email = email,
NormalizedEmail = email.ToUpperInvariant(),
UserName = email,
NormalizedUserName = email.ToUpperInvariant(),
Name = "Platform UI Operator",
PrimaryRole = "platform_admin",
RawProfile = JsonDefaults.Object()
}.WithTestPassword(),
new PlatformBackendRole
{
Id = roleId,
Code = $"platform_ui_{Guid.NewGuid():N}",
Name = "Platform UI Operator",
Status = BackendRoleStatus.Active
},
new PlatformBackendRolePermission
{
RoleId = roleId,
PermissionCode = BackendPermissions.PlatformDashboardView
},
new PlatformBackendUserRole
{
UserId = userId,
RoleId = roleId
});
using var client = factory.CreateClient();
client.UseAccessToken(await client.LoginAsPlatformAsync(email));
commandRecorder.Reset();
using var response = await client.GetAsync("/api/platform/access/ui-bootstrap");
using var bootstrap = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var firstRequestCommands = commandRecorder.Snapshot();
commandRecorder.Reset();
using var repeatedResponse = await client.GetAsync("/api/platform/access/ui-bootstrap");
var repeatedRequestCommands = commandRecorder.Snapshot();
using (var scope = factory.CreateSystemScope("Revoke cached platform UI permission"))
{
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
var permission = await dbContext.PlatformBackendRolePermissions.SingleAsync(item =>
item.RoleId == roleId && item.PermissionCode == BackendPermissions.PlatformDashboardView);
dbContext.Remove(permission);
await dbContext.SaveChangesAsync();
}
using var revokedResponse = await client.GetAsync("/api/platform/access/ui-bootstrap");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(HttpStatusCode.OK, repeatedResponse.StatusCode);
Assert.Equal(HttpStatusCode.Unauthorized, revokedResponse.StatusCode);
Assert.Equal(
[BackendPermissions.PlatformDashboardView],
bootstrap.RootElement.GetProperty("permissionCodes").EnumerateArray().Select(item => item.GetString()));
Assert.Equal(
["platform.dashboard"],
bootstrap.RootElement.GetProperty("menus").EnumerateArray()
.Select(item => item.GetProperty("code").GetString()));
Assert.InRange(firstRequestCommands.Count, 1, 10);
Assert.Contains(firstRequestCommands, IsPlatformPermissionQuery);
Assert.DoesNotContain(repeatedRequestCommands, IsPlatformPermissionQuery);
AssertNoPerCodeCatalogExistenceQueries(firstRequestCommands);
AssertNoPerCodeCatalogExistenceQueries(repeatedRequestCommands);
}
private static void AssertNoPerCodeCatalogExistenceQueries(IEnumerable<string> commands)
{
string[] catalogTables = ["saas_features", "permission_modules", "backend_permissions", "backend_menus"];
Assert.DoesNotContain(commands, command =>
command.TrimStart().StartsWith("SELECT EXISTS", StringComparison.OrdinalIgnoreCase) &&
catalogTables.Any(table => command.Contains(table, StringComparison.OrdinalIgnoreCase)));
}
private static bool IsPlatformPermissionQuery(string command)
{
return command.Contains("platform_backend_role_permissions", StringComparison.OrdinalIgnoreCase) &&
command.Contains("SELECT DISTINCT", StringComparison.OrdinalIgnoreCase);
}
}