elsa-core/test/unit/Elsa.Identity.UnitTests/Services/RoleManagerTests.cs
Sipke Schoorstra 815d7b7f70
Fix MemoryRoleStore global role ID uniqueness (#8127)
* fix(identity): enforce global role ID uniqueness

* fix(identity): preserve role tenant on updates

* test(identity): cover role tenant rehoming
2026-09-13 23:28:38 +02:00

112 lines
4.3 KiB
C#

using Elsa.Common.Multitenancy;
using Elsa.Testing.Shared.Multitenancy;
using Elsa.Common.Services;
using Elsa.Identity.Entities;
using Elsa.Identity.Providers;
using Elsa.Identity.Services;
namespace Elsa.Identity.UnitTests.Services;
public class RoleManagerTests
{
private readonly TestTenantAccessor _tenantAccessor;
private readonly MemoryRoleStore _roleStore;
private readonly RoleManager _manager;
public RoleManagerTests()
{
_tenantAccessor = new TestTenantAccessor("tenant-a");
_roleStore = new MemoryRoleStore(new MemoryStore<Role>(), _tenantAccessor);
_manager = new RoleManager(_roleStore, new StoreBasedRoleProvider(_roleStore), _tenantAccessor);
}
[Fact]
public async Task CreateListUpdateAndDeleteAreIsolatedForRolesWithTheSameNameAcrossTenants()
{
var roleA = await _manager.CreateRoleAsync("Operators", ["tenant-a:permission"], "operators-a");
Assert.Equal("tenant-a", roleA.Role.TenantId);
Assert.Single(await _roleStore.FindManyAsync(new() { TenantId = "tenant-a" }));
var roleBId = string.Empty;
using (_tenantAccessor.PushContext(new Tenant { Id = "tenant-b", Name = "Tenant B" }))
{
var roleB = await _manager.CreateRoleAsync("Operators", ["tenant-b:permission"], "operators-b");
roleBId = roleB.Role.Id;
Assert.NotEqual(roleA.Role.Id, roleB.Role.Id);
Assert.Equal("tenant-b", roleB.Role.TenantId);
Assert.Equal(["tenant-b:permission"], roleB.Role.Permissions);
var tenantBRoles = await _roleStore.FindManyAsync(new() { TenantId = "tenant-b" });
Assert.Single(tenantBRoles);
Assert.Equal(roleB.Role.Id, tenantBRoles.Single().Id);
tenantBRoles.Single().Name = "Operators B";
await _roleStore.SaveAsync(tenantBRoles.Single());
Assert.Equal("Operators B", (await _roleStore.FindAsync(new() { Id = roleB.Role.Id }))!.Name);
}
var tenantARole = await _roleStore.FindAsync(new() { Id = roleA.Role.Id });
Assert.NotNull(tenantARole);
Assert.Equal("Operators", tenantARole.Name);
Assert.Equal(["tenant-a:permission"], tenantARole.Permissions);
tenantARole.Name = "Operators A";
await _roleStore.SaveAsync(tenantARole);
Assert.Equal("Operators A", (await _roleStore.FindAsync(new() { Id = roleA.Role.Id }))!.Name);
await _roleStore.DeleteAsync(new() { Id = roleA.Role.Id });
Assert.Empty(await _roleStore.FindManyAsync(new() { TenantId = "tenant-a" }));
using (_tenantAccessor.PushContext(new Tenant { Id = "tenant-b", Name = "Tenant B" }))
{
var remainingTenantBRole = await _roleStore.FindAsync(new() { Id = roleBId });
Assert.NotNull(remainingTenantBRole);
Assert.Equal("Operators B", remainingTenantBRole.Name);
}
}
[Fact]
public async Task DefaultTenantListsLegacyRolesWithoutATenantId()
{
var tenantAccessor = new TestTenantAccessor();
var roleStore = new MemoryRoleStore(new MemoryStore<Role>(), tenantAccessor);
await roleStore.SaveAsync(new Role { Id = "legacy", Name = "Legacy", Permissions = [] });
var roles = await roleStore.FindManyAsync(new() { TenantId = Tenant.DefaultTenantId });
Assert.Single(roles);
Assert.Equal("legacy", roles.Single().Id);
}
[Fact]
public async Task CreateRoleRejectsExistingRoleId()
{
await _roleStore.SaveAsync(new Role
{
Id = "admin",
Name = "Admin",
TenantId = "tenant-a",
Permissions = [PermissionNames.All]
});
await Assert.ThrowsAsync<InvalidOperationException>(() => _manager.CreateRoleAsync("Replacement", [], "admin"));
var role = await _roleStore.FindAsync(new() { Id = "admin" });
Assert.NotNull(role);
Assert.Equal("Admin", role.Name);
Assert.Equal([PermissionNames.All], role.Permissions);
}
[Fact]
public async Task CreateRoleRejectsProvidedAdminRoleIdCollision()
{
var manager = new RoleManager(_roleStore, new AdminRoleProvider(), _tenantAccessor);
await Assert.ThrowsAsync<InvalidOperationException>(() => manager.CreateRoleAsync("Replacement", [], "admin"));
}
}