Files
PnvPanel/backend/tests/PnvPanel.Application.Tests/Admin/Support/ApproveRoleRequestCommandHandlerTests.cs
T
Leonid Pershin 285d8180c8
CI / Backend (build + test) (push) Successful in 1m22s
CI / Frontend (lint + typecheck + build) (push) Successful in 32s
Enhance role management by adding pricing fields and updating related logic
- Updated `CreateRoleCommand` and `UpdateRoleCommand` to include optional pricing fields: `PricePerConfigPerQuarter` and `PricePerConfigPerYear`.
- Modified `RoleEndpoints` to handle the new pricing parameters during role creation and updates.
- Enhanced validation logic in `CreateRoleCommandValidator` and `UpdateRoleCommandValidator` to ensure pricing fields are non-negative when provided.
- Updated `RoleDto` and `SelectableRoleDto` to include pricing information, ensuring proper data handling in API responses.
- Adjusted frontend components to support new pricing fields in role forms and display total costs based on configurations.
- Updated API documentation to reflect changes in role management endpoints and pricing structure.
2026-07-18 19:02:12 +03:00

203 lines
7.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using NSubstitute;
using PnvPanel.Application.Admin.Roles;
using PnvPanel.Application.Admin.Support;
using PnvPanel.Application.Common.Interfaces;
using PnvPanel.Application.Common.Models;
using PnvPanel.Application.Support;
using PnvPanel.Application.Tests.TestSupport;
using PnvPanel.Domain.Support;
using Xunit;
namespace PnvPanel.Application.Tests.Admin.Support;
public class ApproveRoleRequestCommandHandlerTests
{
private readonly IRoleService _roleService = Substitute.For<IRoleService>();
private readonly IRealtimeNotifier _notifier = Substitute.For<IRealtimeNotifier>();
private readonly ITelegramNotifier _telegramNotifier = Substitute.For<ITelegramNotifier>();
[Fact]
public async Task Handle_ForNewRoleRequest_CreatesRoleAssignsAndResolves()
{
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var ticket = SupportTicket.CreateRoleRequestForNewRole(userId, "premium", 10, 5);
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
var newRoleId = Guid.NewGuid();
_roleService
.CreateRoleAsync("premium", 10, 5, null, null, Arg.Any<CancellationToken>())
.Returns(Result.Success(new RoleDto(newRoleId, "premium", 10, 5, false)));
_roleService
.ChangeUserRoleAsync(userId, newRoleId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid(), "admin");
var handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new ApproveRoleRequestCommand(ticket.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TicketStatus.Resolved, ticket.Status);
await _roleService
.Received(1)
.CreateRoleAsync("premium", 10, 5, null, null, Arg.Any<CancellationToken>());
await _roleService
.Received(1)
.ChangeUserRoleAsync(userId, newRoleId, Arg.Any<CancellationToken>());
await _telegramNotifier
.Received(1)
.NotifyUserAsync(
userId,
Arg.Any<string>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
public async Task Handle_ForExistingRoleRequest_SkipsRoleCreation()
{
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var roleId = Guid.NewGuid();
var ticket = SupportTicket.CreateRoleRequestForExistingRole(userId, roleId);
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_roleService
.ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid());
var handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new ApproveRoleRequestCommand(ticket.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await _roleService
.DidNotReceive()
.CreateRoleAsync(
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<int?>(),
Arg.Any<int?>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
public async Task Handle_WhenNotRoleRequestType_ReturnsError()
{
using var dbContext = InMemoryDbContextFactory.Create();
var ticket = SupportTicket.CreateBugReport(Guid.NewGuid());
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid());
var handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new ApproveRoleRequestCommand(ticket.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.NotRoleRequest, result.Error);
}
[Fact]
public async Task Handle_WhenTicketIsOwnedByAdmin_StillApproves()
{
// Одобрить свою же заявку можно — единственный реальный риск (снять admin с последнего
// администратора) ловит RoleService.ChangeUserRoleAsync, а не этот хендлер (см. соседний тест).
using var dbContext = InMemoryDbContextFactory.Create();
var adminId = Guid.NewGuid();
var roleId = Guid.NewGuid();
var ticket = SupportTicket.CreateRoleRequestForExistingRole(adminId, roleId);
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_roleService
.ChangeUserRoleAsync(adminId, roleId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
var currentUser = FakeCurrentUser.Authenticated(adminId, "admin");
var handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new ApproveRoleRequestCommand(ticket.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TicketStatus.Resolved, ticket.Status);
}
[Fact]
public async Task Handle_WhenRoleServiceRefusesLastAdminDowngrade_PropagatesFailure()
{
using var dbContext = InMemoryDbContextFactory.Create();
var adminId = Guid.NewGuid();
var roleId = Guid.NewGuid();
var ticket = SupportTicket.CreateRoleRequestForExistingRole(adminId, roleId);
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_roleService
.ChangeUserRoleAsync(adminId, roleId, Arg.Any<CancellationToken>())
.Returns(Result.Failure(RoleErrors.CannotRemoveLastAdmin));
var currentUser = FakeCurrentUser.Authenticated(adminId, "admin");
var handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new ApproveRoleRequestCommand(ticket.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(RoleErrors.CannotRemoveLastAdmin, result.Error);
// Тикет остаётся Open — можно повторить попытку после назначения второго админа.
Assert.Equal(TicketStatus.Open, ticket.Status);
}
}