mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-06 04:12:14 +00:00
fix(team-members): keep full-name round-trip stable on update
Storing the submitted full name in FirstName while leaving a seeded LastName intact made an unchanged save project a duplicated name (e.g. "Legacy Manager Manager"). Leave FirstName/LastName untouched when the submitted name already matches the stored first+last projection, and otherwise split the submitted name across FirstName/LastName so renames round-trip cleanly.
This commit is contained in:
parent
bdfe666b8d
commit
ba8907fad0
2 changed files with 70 additions and 1 deletions
|
|
@ -216,6 +216,57 @@ public sealed class TeamMemberServiceTests
|
||||||
overrides.Verify(data => data.SetOverridesAsync(user.Id, It.Is<IReadOnlyDictionary<string, UserPermissionState>>(value => value["createSites"] == UserPermissionState.Deny), It.IsAny<CancellationToken>()), Times.Once);
|
overrides.Verify(data => data.SetOverridesAsync(user.Id, It.Is<IReadOnlyDictionary<string, UserPermissionState>>(value => value["createSites"] == UserPermissionState.Deny), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Update_SavesUnchangedNameWithoutDuplicatingRoleLikeLastName()
|
||||||
|
{
|
||||||
|
var service = NewService(
|
||||||
|
out var userManager,
|
||||||
|
out var roleManager,
|
||||||
|
out var areas,
|
||||||
|
out _,
|
||||||
|
out var userData,
|
||||||
|
out var permissions);
|
||||||
|
var user = new ApplicationUser
|
||||||
|
{
|
||||||
|
Id = "admin",
|
||||||
|
FirstName = "Legacy",
|
||||||
|
LastName = "Manager",
|
||||||
|
Email = "legacy@example.com",
|
||||||
|
Color = "#F59E0B",
|
||||||
|
UniqueName = "Active"
|
||||||
|
};
|
||||||
|
userManager.Setup(manager => manager.FindByIdAsync(user.Id)).ReturnsAsync(user);
|
||||||
|
userManager.Setup(manager => manager.GetRolesAsync(user)).ReturnsAsync(new List<string> { "Admin" });
|
||||||
|
userManager.Setup(manager => manager.FindByEmailAsync(user.Email!)).ReturnsAsync(user);
|
||||||
|
roleManager.Setup(manager => manager.RoleExistsAsync("Admin")).ReturnsAsync(true);
|
||||||
|
areas.Setup(data => data.GetForUserAsync(user.Id, It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(Array.Empty<string>());
|
||||||
|
permissions.Setup(data => data.GetProfileAsync(user.Id, It.IsAny<ClaimsPrincipal>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(TeamPermissionResult<TeamPermissionProfileDTO>.Success(new TeamPermissionProfileDTO
|
||||||
|
{
|
||||||
|
UserId = user.Id,
|
||||||
|
Permissions = Array.Empty<TeamPermissionValueDTO>()
|
||||||
|
}));
|
||||||
|
|
||||||
|
var result = await service.UpdateAsync(
|
||||||
|
user.Id,
|
||||||
|
new UpdateTeamMemberRequestDTO
|
||||||
|
{
|
||||||
|
Name = "Legacy Manager",
|
||||||
|
Role = "Admin",
|
||||||
|
Color = "#F59E0B",
|
||||||
|
Email = user.Email
|
||||||
|
},
|
||||||
|
Admin(),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
result.Success.Should().BeTrue();
|
||||||
|
user.FirstName.Should().Be("Legacy");
|
||||||
|
user.LastName.Should().Be("Manager");
|
||||||
|
result.Member!.Name.Should().Be("Legacy Manager");
|
||||||
|
userData.Verify(data => data.UpdateUserAsync(user, It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Update_LeavesOmittedOverridesUnchanged()
|
public async Task Update_LeavesOmittedOverridesUnchanged()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ public sealed class TeamMemberService : ITeamMemberService
|
||||||
if (roleError is not null)
|
if (roleError is not null)
|
||||||
return OperationFailure(roleError);
|
return OperationFailure(roleError);
|
||||||
|
|
||||||
user.FirstName = candidate.Name!.Trim();
|
ApplyName(user, candidate.Name!.Trim());
|
||||||
user.Contact = candidate.Phone?.Trim();
|
user.Contact = candidate.Phone?.Trim();
|
||||||
user.PhoneNumber = candidate.Phone?.Trim();
|
user.PhoneNumber = candidate.Phone?.Trim();
|
||||||
user.Color = color;
|
user.Color = color;
|
||||||
|
|
@ -215,6 +215,24 @@ public sealed class TeamMemberService : ITeamMemberService
|
||||||
return await OperationSuccessAsync(user, caller, cancellationToken);
|
return await OperationSuccessAsync(user, caller, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ApplyName(ApplicationUser user, string fullName)
|
||||||
|
{
|
||||||
|
var current = $"{user.FirstName ?? ""} {user.LastName ?? ""}".Trim();
|
||||||
|
if (string.Equals(fullName, current, StringComparison.Ordinal))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var separator = fullName.IndexOf(' ');
|
||||||
|
if (separator < 0)
|
||||||
|
{
|
||||||
|
user.FirstName = fullName;
|
||||||
|
user.LastName = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.FirstName = fullName[..separator].Trim();
|
||||||
|
user.LastName = fullName[(separator + 1)..].Trim();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string?> EnsureRoleAsync(string role)
|
private async Task<string?> EnsureRoleAsync(string role)
|
||||||
{
|
{
|
||||||
if (await _roleManager.RoleExistsAsync(role))
|
if (await _roleManager.RoleExistsAsync(role))
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue