Refactor project files for improved readability and structure
CI / Backend (build + test) (push) Successful in 1m18s
CI / Frontend (lint + typecheck + build) (push) Successful in 31s

- Cleaned up whitespace in Directory.Build.props and Directory.Packages.props for consistency.
- Reformatted project file references in PnvPanel.Api.csproj for better clarity.
- Enhanced code readability in various endpoint files by adjusting line breaks and indentation.
- Standardized method signatures and improved formatting in ResultExtensions and multiple endpoint classes for better maintainability.
This commit is contained in:
Leonid Pershin
2026-07-14 07:24:13 +03:00
parent 9d5424bb9c
commit df137ca5a7
285 changed files with 6911 additions and 2063 deletions
@@ -27,15 +27,28 @@ public class ApproveActivationCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
_currentUser.UserId.Returns(adminId);
_identityService.ActivateUserAsync(request.UserId, adminId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_identityService
.ActivateUserAsync(request.UserId, adminId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
var handler = new ApproveActivationCommandHandler(dbContext, _identityService, _notifier, _telegramNotifier, _currentUser);
var handler = new ApproveActivationCommandHandler(
dbContext,
_identityService,
_notifier,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new ApproveActivationCommand(request.Id), CancellationToken.None);
var result = await handler.Handle(
new ApproveActivationCommand(request.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(ActivationStatus.Approved, request.Status);
await _notifier.Received(1).NotifyUserActivatedAsync(request.UserId, Arg.Any<CancellationToken>());
await _notifier
.Received(1)
.NotifyUserActivatedAsync(request.UserId, Arg.Any<CancellationToken>());
}
[Fact]
@@ -44,9 +57,18 @@ public class ApproveActivationCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
_currentUser.UserId.Returns(Guid.NewGuid());
var handler = new ApproveActivationCommandHandler(dbContext, _identityService, _notifier, _telegramNotifier, _currentUser);
var handler = new ApproveActivationCommandHandler(
dbContext,
_identityService,
_notifier,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new ApproveActivationCommand(Guid.NewGuid()), CancellationToken.None);
var result = await handler.Handle(
new ApproveActivationCommand(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ActivationErrors.NotFound, result.Error);
@@ -63,9 +85,18 @@ public class ApproveActivationCommandHandlerTests
_currentUser.UserId.Returns(Guid.NewGuid());
var handler = new ApproveActivationCommandHandler(dbContext, _identityService, _notifier, _telegramNotifier, _currentUser);
var handler = new ApproveActivationCommandHandler(
dbContext,
_identityService,
_notifier,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new ApproveActivationCommand(request.Id), CancellationToken.None);
var result = await handler.Handle(
new ApproveActivationCommand(request.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ActivationErrors.AlreadyDecided, result.Error);
@@ -82,14 +113,27 @@ public class ApproveActivationCommandHandlerTests
_currentUser.UserId.Returns(adminId);
var failure = Error.NotFound("User.NotFound", "Пользователь не найден.");
_identityService.ActivateUserAsync(request.UserId, adminId, Arg.Any<CancellationToken>()).Returns(Result.Failure(failure));
_identityService
.ActivateUserAsync(request.UserId, adminId, Arg.Any<CancellationToken>())
.Returns(Result.Failure(failure));
var handler = new ApproveActivationCommandHandler(dbContext, _identityService, _notifier, _telegramNotifier, _currentUser);
var handler = new ApproveActivationCommandHandler(
dbContext,
_identityService,
_notifier,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new ApproveActivationCommand(request.Id), CancellationToken.None);
var result = await handler.Handle(
new ApproveActivationCommand(request.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(failure, result.Error);
await _notifier.DidNotReceive().NotifyUserActivatedAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
await _notifier
.DidNotReceive()
.NotifyUserActivatedAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
}
}
@@ -25,7 +25,10 @@ public class RejectActivationCommandHandlerTests
var handler = new RejectActivationCommandHandler(dbContext, _currentUser);
var result = await handler.Handle(new RejectActivationCommand(request.Id, "недостаточно информации"), CancellationToken.None);
var result = await handler.Handle(
new RejectActivationCommand(request.Id, "недостаточно информации"),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(ActivationStatus.Rejected, request.Status);
@@ -40,7 +43,10 @@ public class RejectActivationCommandHandlerTests
var handler = new RejectActivationCommandHandler(dbContext, _currentUser);
var result = await handler.Handle(new RejectActivationCommand(Guid.NewGuid(), null), CancellationToken.None);
var result = await handler.Handle(
new RejectActivationCommand(Guid.NewGuid(), null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ActivationErrors.NotFound, result.Error);
@@ -59,7 +65,10 @@ public class RejectActivationCommandHandlerTests
var handler = new RejectActivationCommandHandler(dbContext, _currentUser);
var result = await handler.Handle(new RejectActivationCommand(request.Id, null), CancellationToken.None);
var result = await handler.Handle(
new RejectActivationCommand(request.Id, null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ActivationErrors.AlreadyDecided, result.Error);
@@ -22,18 +22,40 @@ public class RequestActivationCommandHandlerTests
_currentUser.UserId.Returns(userId);
_currentUser.UserName.Returns("alice");
var handler = new RequestActivationCommandHandler(dbContext, _notifier, _telegramNotifier, _currentUser);
var handler = new RequestActivationCommandHandler(
dbContext,
_notifier,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new RequestActivationCommand("Please activate"), CancellationToken.None);
var result = await handler.Handle(
new RequestActivationCommand("Please activate"),
CancellationToken.None
);
await dbContext.SaveChangesAsync(CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal("Please activate", result.Value.Comment);
Assert.Single(dbContext.ActivationRequests);
await _notifier.Received(1).NotifyActivationRequestedAsync(
Arg.Any<Guid>(), userId, "alice", "Please activate", Arg.Any<DateTimeOffset>(), Arg.Any<CancellationToken>());
await _telegramNotifier.Received(1).NotifyAdminsActivationRequestedAsync(
Arg.Any<Guid>(), "alice", "Please activate", Arg.Any<CancellationToken>());
await _notifier
.Received(1)
.NotifyActivationRequestedAsync(
Arg.Any<Guid>(),
userId,
"alice",
"Please activate",
Arg.Any<DateTimeOffset>(),
Arg.Any<CancellationToken>()
);
await _telegramNotifier
.Received(1)
.NotifyAdminsActivationRequestedAsync(
Arg.Any<Guid>(),
"alice",
"Please activate",
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -47,14 +69,30 @@ public class RequestActivationCommandHandlerTests
_currentUser.UserId.Returns(userId);
_currentUser.UserName.Returns("alice");
var handler = new RequestActivationCommandHandler(dbContext, _notifier, _telegramNotifier, _currentUser);
var handler = new RequestActivationCommandHandler(
dbContext,
_notifier,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new RequestActivationCommand(null), CancellationToken.None);
var result = await handler.Handle(
new RequestActivationCommand(null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ActivationErrors.AlreadyPending, result.Error);
await _notifier.DidNotReceive().NotifyActivationRequestedAsync(
Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<DateTimeOffset>(), Arg.Any<CancellationToken>());
await _notifier
.DidNotReceive()
.NotifyActivationRequestedAsync(
Arg.Any<Guid>(),
Arg.Any<Guid>(),
Arg.Any<string>(),
Arg.Any<string?>(),
Arg.Any<DateTimeOffset>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -63,9 +101,17 @@ public class RequestActivationCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
_currentUser.UserId.Returns((Guid?)null);
var handler = new RequestActivationCommandHandler(dbContext, _notifier, _telegramNotifier, _currentUser);
var handler = new RequestActivationCommandHandler(
dbContext,
_notifier,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new RequestActivationCommand(null), CancellationToken.None);
var result = await handler.Handle(
new RequestActivationCommand(null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.Unauthorized, result.Error);
@@ -19,7 +19,12 @@ public class ListAllConfigsQueryHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
inbound.Publish("Germany", [], null);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "my-config");
@@ -29,12 +34,16 @@ public class ListAllConfigsQueryHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetUserNamesAsync(Arg.Any<IReadOnlyCollection<Guid>>(), Arg.Any<CancellationToken>())
_identityService
.GetUserNamesAsync(Arg.Any<IReadOnlyCollection<Guid>>(), Arg.Any<CancellationToken>())
.Returns(new Dictionary<Guid, string> { [userId] = "alice" });
var handler = new ListAllConfigsQueryHandler(dbContext, _identityService);
var result = await handler.Handle(new ListAllConfigsQuery(1, 20, Search: null, Status: null), CancellationToken.None);
var result = await handler.Handle(
new ListAllConfigsQuery(1, 20, Search: null, Status: null),
CancellationToken.None
);
Assert.True(result.IsSuccess);
var item = Assert.Single(result.Value.Items);
@@ -52,7 +61,12 @@ public class ListAllConfigsQueryHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var matching = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "phone-config");
var other = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "laptop-config");
@@ -62,12 +76,16 @@ public class ListAllConfigsQueryHandlerTests
dbContext.VpnConfigs.AddRange(matching, other);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetUserNamesAsync(Arg.Any<IReadOnlyCollection<Guid>>(), Arg.Any<CancellationToken>())
_identityService
.GetUserNamesAsync(Arg.Any<IReadOnlyCollection<Guid>>(), Arg.Any<CancellationToken>())
.Returns(new Dictionary<Guid, string> { [userId] = "alice" });
var handler = new ListAllConfigsQueryHandler(dbContext, _identityService);
var result = await handler.Handle(new ListAllConfigsQuery(1, 20, Search: "phone", Status: null), CancellationToken.None);
var result = await handler.Handle(
new ListAllConfigsQuery(1, 20, Search: "phone", Status: null),
CancellationToken.None
);
Assert.True(result.IsSuccess);
var item = Assert.Single(result.Value.Items);
@@ -80,7 +98,12 @@ public class ListAllConfigsQueryHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var active = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "active-config");
var revoked = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "revoked-config");
@@ -91,12 +114,16 @@ public class ListAllConfigsQueryHandlerTests
dbContext.VpnConfigs.AddRange(active, revoked);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetUserNamesAsync(Arg.Any<IReadOnlyCollection<Guid>>(), Arg.Any<CancellationToken>())
_identityService
.GetUserNamesAsync(Arg.Any<IReadOnlyCollection<Guid>>(), Arg.Any<CancellationToken>())
.Returns(new Dictionary<Guid, string> { [userId] = "alice" });
var handler = new ListAllConfigsQueryHandler(dbContext, _identityService);
var result = await handler.Handle(new ListAllConfigsQuery(1, 20, Search: null, Status: ConfigStatus.Revoked), CancellationToken.None);
var result = await handler.Handle(
new ListAllConfigsQuery(1, 20, Search: null, Status: ConfigStatus.Revoked),
CancellationToken.None
);
Assert.True(result.IsSuccess);
var item = Assert.Single(result.Value.Items);
@@ -21,9 +21,20 @@ public class RegisterNodeCommandHandlerTests
_gateway.ValidateBaseAddress(Arg.Any<Uri>()).Returns(Result.Success());
_secretProtector.Protect("secret-password").Returns("protected-secret-password");
var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector, _currentUser);
var handler = new RegisterNodeCommandHandler(
dbContext,
_gateway,
_secretProtector,
_currentUser
);
var command = new RegisterNodeCommand("node-1", "https://node1.example.com", "admin", "secret-password", "eu-west");
var command = new RegisterNodeCommand(
"node-1",
"https://node1.example.com",
"admin",
"secret-password",
"eu-west"
);
var result = await handler.Handle(command, CancellationToken.None);
@@ -31,7 +42,10 @@ public class RegisterNodeCommandHandlerTests
Assert.Equal("node-1", result.Value.Name);
Assert.Equal("admin", result.Value.Username);
Assert.Single(dbContext.Nodes.Local);
Assert.Equal("protected-secret-password", dbContext.Nodes.Local.Single().Credentials.ProtectedPassword);
Assert.Equal(
"protected-secret-password",
dbContext.Nodes.Local.Single().Credentials.ProtectedPassword
);
}
[Fact]
@@ -39,9 +53,20 @@ public class RegisterNodeCommandHandlerTests
{
using var dbContext = InMemoryDbContextFactory.Create();
var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector, _currentUser);
var handler = new RegisterNodeCommandHandler(
dbContext,
_gateway,
_secretProtector,
_currentUser
);
var command = new RegisterNodeCommand("node-1", "not-a-uri", "admin", "secret-password", null);
var command = new RegisterNodeCommand(
"node-1",
"not-a-uri",
"admin",
"secret-password",
null
);
var result = await handler.Handle(command, CancellationToken.None);
@@ -59,9 +84,20 @@ public class RegisterNodeCommandHandlerTests
var error = Error.Validation("Nodes.SchemeNotAllowed", "Разрешён только HTTPS.");
_gateway.ValidateBaseAddress(Arg.Any<Uri>()).Returns(Result.Failure(error));
var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector, _currentUser);
var handler = new RegisterNodeCommandHandler(
dbContext,
_gateway,
_secretProtector,
_currentUser
);
var command = new RegisterNodeCommand("node-1", "http://node1.example.com", "admin", "secret-password", null);
var command = new RegisterNodeCommand(
"node-1",
"http://node1.example.com",
"admin",
"secret-password",
null
);
var result = await handler.Handle(command, CancellationToken.None);
@@ -25,20 +25,38 @@ public class ApproveRoleRequestCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
var newRoleId = Guid.NewGuid();
_roleService.CreateRoleAsync("premium", 10, 5, Arg.Any<CancellationToken>())
_roleService
.CreateRoleAsync("premium", 10, 5, Arg.Any<CancellationToken>())
.Returns(Result.Success(new RoleDto(newRoleId, "premium", 10, 5, false)));
_roleService.ChangeUserRoleAsync(userId, newRoleId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_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 handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(new ApproveRoleRequestCommand(ticket.Id), CancellationToken.None);
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, Arg.Any<CancellationToken>());
await _roleService.Received(1).ChangeUserRoleAsync(userId, newRoleId, Arg.Any<CancellationToken>());
await _telegramNotifier.Received(1).NotifyUserAsync(userId, Arg.Any<string>(), Arg.Any<CancellationToken>());
await _roleService
.Received(1)
.CreateRoleAsync("premium", 10, 5, Arg.Any<CancellationToken>());
await _roleService
.Received(1)
.ChangeUserRoleAsync(userId, newRoleId, Arg.Any<CancellationToken>());
await _telegramNotifier
.Received(1)
.NotifyUserAsync(userId, Arg.Any<string>(), Arg.Any<CancellationToken>());
}
[Fact]
@@ -51,16 +69,33 @@ public class ApproveRoleRequestCommandHandlerTests
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_roleService.ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_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 handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(new ApproveRoleRequestCommand(ticket.Id), CancellationToken.None);
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<CancellationToken>());
await _roleService
.DidNotReceive()
.CreateRoleAsync(
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<int>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -72,9 +107,18 @@ public class ApproveRoleRequestCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid());
var handler = new ApproveRoleRequestCommandHandler(dbContext, _roleService, _notifier, _telegramNotifier, currentUser);
var handler = new ApproveRoleRequestCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(new ApproveRoleRequestCommand(ticket.Id), CancellationToken.None);
var result = await handler.Handle(
new ApproveRoleRequestCommand(ticket.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.NotRoleRequest, result.Error);
@@ -18,7 +18,9 @@ public class BlockUserCommandHandlerTests
private readonly IRealtimeNotifier _notifier = Substitute.For<IRealtimeNotifier>();
private readonly ITelegramNotifier _telegramNotifier = Substitute.For<ITelegramNotifier>();
private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();
private readonly ILogger<BlockUserCommandHandler> _logger = Substitute.For<ILogger<BlockUserCommandHandler>>();
private readonly ILogger<BlockUserCommandHandler> _logger = Substitute.For<
ILogger<BlockUserCommandHandler>
>();
[Fact]
public async Task Handle_IdentityServiceFails_ReturnsFailureWithoutTouchingConfigs()
@@ -26,17 +28,35 @@ public class BlockUserCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var failure = UserErrors.NotFound;
_identityService.BlockUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Failure(failure));
_identityService
.BlockUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Failure(failure));
var handler = new BlockUserCommandHandler(dbContext, _identityService, _gateway, _notifier, _telegramNotifier, _currentUser, _logger);
var handler = new BlockUserCommandHandler(
dbContext,
_identityService,
_gateway,
_notifier,
_telegramNotifier,
_currentUser,
_logger
);
var result = await handler.Handle(new BlockUserCommand(userId), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Equal(failure, result.Error);
await _gateway.DidNotReceive().UpdateClientAsync(
Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(),
Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>());
await _gateway
.DidNotReceive()
.UpdateClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<string>(),
Arg.Any<bool>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -46,7 +66,12 @@ public class BlockUserCommandHandlerTests
var userId = Guid.NewGuid();
var adminId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "my-config");
@@ -55,24 +80,56 @@ public class BlockUserCommandHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.BlockUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_identityService
.BlockUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
_currentUser.UserId.Returns(adminId);
_gateway.UpdateClientAsync(
Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(),
Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
_gateway
.UpdateClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<string>(),
Arg.Any<bool>(),
Arg.Any<CancellationToken>()
)
.Returns(Result.Success());
var handler = new BlockUserCommandHandler(dbContext, _identityService, _gateway, _notifier, _telegramNotifier, _currentUser, _logger);
var handler = new BlockUserCommandHandler(
dbContext,
_identityService,
_gateway,
_notifier,
_telegramNotifier,
_currentUser,
_logger
);
var result = await handler.Handle(new BlockUserCommand(userId), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(ConfigStatus.Disabled, config.Status);
await _gateway.Received(1).UpdateClientAsync(
Arg.Is<Node>(n => n.Id == node.Id), inbound.RemoteInboundId, config.ClientExternalId, config.Protocol,
"my-config", enable: false, Arg.Any<CancellationToken>());
await _notifier.Received(1).NotifyConfigStatusChangedAsync(userId, config.Id, ConfigStatus.Disabled, Arg.Any<CancellationToken>());
await _gateway
.Received(1)
.UpdateClientAsync(
Arg.Is<Node>(n => n.Id == node.Id),
inbound.RemoteInboundId,
config.ClientExternalId,
config.Protocol,
"my-config",
enable: false,
Arg.Any<CancellationToken>()
);
await _notifier
.Received(1)
.NotifyConfigStatusChangedAsync(
userId,
config.Id,
ConfigStatus.Disabled,
Arg.Any<CancellationToken>()
);
var audit = Assert.Single(dbContext.AuditLogs.Local);
Assert.Equal("UserBlocked", audit.Action);
@@ -85,17 +142,35 @@ public class BlockUserCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
_identityService.BlockUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_identityService
.BlockUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
_currentUser.UserId.Returns(Guid.NewGuid());
var handler = new BlockUserCommandHandler(dbContext, _identityService, _gateway, _notifier, _telegramNotifier, _currentUser, _logger);
var handler = new BlockUserCommandHandler(
dbContext,
_identityService,
_gateway,
_notifier,
_telegramNotifier,
_currentUser,
_logger
);
var result = await handler.Handle(new BlockUserCommand(userId), CancellationToken.None);
Assert.True(result.IsSuccess);
await _gateway.DidNotReceive().UpdateClientAsync(
Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(),
Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>());
await _gateway
.DidNotReceive()
.UpdateClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<string>(),
Arg.Any<bool>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -105,7 +180,12 @@ public class BlockUserCommandHandlerTests
var userId = Guid.NewGuid();
var adminId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "my-config");
@@ -114,20 +194,43 @@ public class BlockUserCommandHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.BlockUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_identityService
.BlockUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
_currentUser.UserId.Returns(adminId);
_gateway.UpdateClientAsync(
Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(),
Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
_gateway
.UpdateClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<string>(),
Arg.Any<bool>(),
Arg.Any<CancellationToken>()
)
.Returns(Result.Failure(Error.Failure("Xui.UpdateClientFailed", "Нода недоступна.")));
var handler = new BlockUserCommandHandler(dbContext, _identityService, _gateway, _notifier, _telegramNotifier, _currentUser, _logger);
var handler = new BlockUserCommandHandler(
dbContext,
_identityService,
_gateway,
_notifier,
_telegramNotifier,
_currentUser,
_logger
);
var result = await handler.Handle(new BlockUserCommand(userId), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(ConfigStatus.Active, config.Status);
await _notifier.DidNotReceive().NotifyConfigStatusChangedAsync(
Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<ConfigStatus>(), Arg.Any<CancellationToken>());
await _notifier
.DidNotReceive()
.NotifyConfigStatusChangedAsync(
Arg.Any<Guid>(),
Arg.Any<Guid>(),
Arg.Any<ConfigStatus>(),
Arg.Any<CancellationToken>()
);
}
}
@@ -19,14 +19,21 @@ public class ChangeUserRoleCommandHandlerTests
var userId = Guid.NewGuid();
var roleId = Guid.NewGuid();
_roleService.ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_roleService
.ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
var handler = new ChangeUserRoleCommandHandler(_roleService, dbContext, _currentUser);
var result = await handler.Handle(new ChangeUserRoleCommand(userId, roleId), CancellationToken.None);
var result = await handler.Handle(
new ChangeUserRoleCommand(userId, roleId),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await _roleService.Received(1).ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>());
await _roleService
.Received(1)
.ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>());
Assert.Single(dbContext.AuditLogs.Local);
}
@@ -38,11 +45,16 @@ public class ChangeUserRoleCommandHandlerTests
var userId = Guid.NewGuid();
var roleId = Guid.NewGuid();
var error = UserErrors.NotFound;
_roleService.ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>()).Returns(Result.Failure(error));
_roleService
.ChangeUserRoleAsync(userId, roleId, Arg.Any<CancellationToken>())
.Returns(Result.Failure(error));
var handler = new ChangeUserRoleCommandHandler(_roleService, dbContext, _currentUser);
var result = await handler.Handle(new ChangeUserRoleCommand(userId, roleId), CancellationToken.None);
var result = await handler.Handle(
new ChangeUserRoleCommand(userId, roleId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(error, result.Error);
@@ -24,13 +24,21 @@ public class DeleteUserCommandHandlerTests
var adminId = Guid.NewGuid();
_currentUser.UserId.Returns(adminId);
var handler = new DeleteUserCommandHandler(dbContext, _identityService, _gateway, _telegramNotifier, _currentUser);
var handler = new DeleteUserCommandHandler(
dbContext,
_identityService,
_gateway,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new DeleteUserCommand(adminId), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Equal(UserErrors.CannotDeleteSelf, result.Error);
await _identityService.DidNotReceive().DeleteUserAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
await _identityService
.DidNotReceive()
.DeleteUserAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
}
[Fact]
@@ -40,7 +48,12 @@ public class DeleteUserCommandHandlerTests
var userId = Guid.NewGuid();
var adminId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "my-config");
config.AssignRemoteClient("external-id");
@@ -51,20 +64,44 @@ public class DeleteUserCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
_currentUser.UserId.Returns(adminId);
_gateway.RemoveClientAsync(Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(), Arg.Any<CancellationToken>())
_gateway
.RemoveClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<CancellationToken>()
)
.Returns(Result.Success());
_identityService
.DeleteUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
_identityService.DeleteUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
var handler = new DeleteUserCommandHandler(dbContext, _identityService, _gateway, _telegramNotifier, _currentUser);
var handler = new DeleteUserCommandHandler(
dbContext,
_identityService,
_gateway,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new DeleteUserCommand(userId), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(ConfigStatus.Revoked, config.Status);
await _gateway.Received(1).RemoveClientAsync(
Arg.Is<Node>(n => n.Id == node.Id), inbound.RemoteInboundId, "external-id", config.Protocol, Arg.Any<CancellationToken>());
await _telegramNotifier.Received(1).NotifyUserAsync(userId, Arg.Any<string>(), Arg.Any<CancellationToken>());
await _gateway
.Received(1)
.RemoveClientAsync(
Arg.Is<Node>(n => n.Id == node.Id),
inbound.RemoteInboundId,
"external-id",
config.Protocol,
Arg.Any<CancellationToken>()
);
await _telegramNotifier
.Received(1)
.NotifyUserAsync(userId, Arg.Any<string>(), Arg.Any<CancellationToken>());
await _identityService.Received(1).DeleteUserAsync(userId, Arg.Any<CancellationToken>());
var audit = Assert.Single(dbContext.AuditLogs.Local);
@@ -79,15 +116,30 @@ public class DeleteUserCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
_currentUser.UserId.Returns(Guid.NewGuid());
_identityService.DeleteUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_identityService
.DeleteUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
var handler = new DeleteUserCommandHandler(dbContext, _identityService, _gateway, _telegramNotifier, _currentUser);
var handler = new DeleteUserCommandHandler(
dbContext,
_identityService,
_gateway,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new DeleteUserCommand(userId), CancellationToken.None);
Assert.True(result.IsSuccess);
await _gateway.DidNotReceive().RemoveClientAsync(
Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(), Arg.Any<CancellationToken>());
await _gateway
.DidNotReceive()
.RemoveClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -96,9 +148,17 @@ public class DeleteUserCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
_currentUser.UserId.Returns(Guid.NewGuid());
_identityService.DeleteUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Failure(UserErrors.NotFound));
_identityService
.DeleteUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Failure(UserErrors.NotFound));
var handler = new DeleteUserCommandHandler(dbContext, _identityService, _gateway, _telegramNotifier, _currentUser);
var handler = new DeleteUserCommandHandler(
dbContext,
_identityService,
_gateway,
_telegramNotifier,
_currentUser
);
var result = await handler.Handle(new DeleteUserCommand(userId), CancellationToken.None);
@@ -17,7 +17,9 @@ public class UnblockUserCommandHandlerTests
private readonly IXuiPanelGateway _gateway = Substitute.For<IXuiPanelGateway>();
private readonly IRealtimeNotifier _notifier = Substitute.For<IRealtimeNotifier>();
private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();
private readonly ILogger<UnblockUserCommandHandler> _logger = Substitute.For<ILogger<UnblockUserCommandHandler>>();
private readonly ILogger<UnblockUserCommandHandler> _logger = Substitute.For<
ILogger<UnblockUserCommandHandler>
>();
[Fact]
public async Task Handle_WhenUnblockSucceeds_ReEnablesDisabledConfigsAndWritesAudit()
@@ -26,7 +28,12 @@ public class UnblockUserCommandHandlerTests
var userId = Guid.NewGuid();
var adminId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "my-config");
config.Disable();
@@ -37,22 +44,53 @@ public class UnblockUserCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
_currentUser.UserId.Returns(adminId);
_identityService.UnblockUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_gateway.UpdateClientAsync(
Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(),
Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
_identityService
.UnblockUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
_gateway
.UpdateClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<string>(),
Arg.Any<bool>(),
Arg.Any<CancellationToken>()
)
.Returns(Result.Success());
var handler = new UnblockUserCommandHandler(dbContext, _identityService, _gateway, _notifier, _currentUser, _logger);
var handler = new UnblockUserCommandHandler(
dbContext,
_identityService,
_gateway,
_notifier,
_currentUser,
_logger
);
var result = await handler.Handle(new UnblockUserCommand(userId), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(ConfigStatus.Active, config.Status);
await _gateway.Received(1).UpdateClientAsync(
node, inbound.RemoteInboundId, config.ClientExternalId, config.Protocol,
Arg.Any<string>(), true, Arg.Any<CancellationToken>());
await _notifier.Received(1).NotifyConfigStatusChangedAsync(userId, config.Id, ConfigStatus.Active, Arg.Any<CancellationToken>());
await _gateway
.Received(1)
.UpdateClientAsync(
node,
inbound.RemoteInboundId,
config.ClientExternalId,
config.Protocol,
Arg.Any<string>(),
true,
Arg.Any<CancellationToken>()
);
await _notifier
.Received(1)
.NotifyConfigStatusChangedAsync(
userId,
config.Id,
ConfigStatus.Active,
Arg.Any<CancellationToken>()
);
Assert.Single(dbContext.AuditLogs.Local);
Assert.Equal("UserUnblocked", dbContext.AuditLogs.Local.Single().Action);
}
@@ -64,9 +102,18 @@ public class UnblockUserCommandHandlerTests
var userId = Guid.NewGuid();
var error = UserErrors.NotFound;
_identityService.UnblockUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Failure(error));
_identityService
.UnblockUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Failure(error));
var handler = new UnblockUserCommandHandler(dbContext, _identityService, _gateway, _notifier, _currentUser, _logger);
var handler = new UnblockUserCommandHandler(
dbContext,
_identityService,
_gateway,
_notifier,
_currentUser,
_logger
);
var result = await handler.Handle(new UnblockUserCommand(userId), CancellationToken.None);
@@ -82,7 +129,12 @@ public class UnblockUserCommandHandlerTests
var userId = Guid.NewGuid();
var adminId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "my-config");
config.Disable();
@@ -93,19 +145,41 @@ public class UnblockUserCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
_currentUser.UserId.Returns(adminId);
_identityService.UnblockUserAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_gateway.UpdateClientAsync(
Arg.Any<Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(),
Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
_identityService
.UnblockUserAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
_gateway
.UpdateClientAsync(
Arg.Any<Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<string>(),
Arg.Any<bool>(),
Arg.Any<CancellationToken>()
)
.Returns(Result.Failure(Error.Failure("Xui.UpdateClientFailed", "Нода недоступна.")));
var handler = new UnblockUserCommandHandler(dbContext, _identityService, _gateway, _notifier, _currentUser, _logger);
var handler = new UnblockUserCommandHandler(
dbContext,
_identityService,
_gateway,
_notifier,
_currentUser,
_logger
);
var result = await handler.Handle(new UnblockUserCommand(userId), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(ConfigStatus.Disabled, config.Status);
await _notifier.DidNotReceive().NotifyConfigStatusChangedAsync(
Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<ConfigStatus>(), Arg.Any<CancellationToken>());
await _notifier
.DidNotReceive()
.NotifyConfigStatusChangedAsync(
Arg.Any<Guid>(),
Arg.Any<Guid>(),
Arg.Any<ConfigStatus>(),
Arg.Any<CancellationToken>()
);
}
}
@@ -15,29 +15,50 @@ public class ChangePasswordCommandHandlerTests
[Fact]
public async Task Handle_Unauthenticated_ReturnsUnauthorized()
{
var handler = new ChangePasswordCommandHandler(_identityService, FakeCurrentUser.Anonymous());
var handler = new ChangePasswordCommandHandler(
_identityService,
FakeCurrentUser.Anonymous()
);
var result = await handler.Handle(new ChangePasswordCommand("old", "new"), CancellationToken.None);
var result = await handler.Handle(
new ChangePasswordCommand("old", "new"),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.Unauthorized, result.Error);
await _identityService.DidNotReceive().ChangePasswordAsync(
Arg.Any<Guid>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<CancellationToken>());
await _identityService
.DidNotReceive()
.ChangePasswordAsync(
Arg.Any<Guid>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
public async Task Handle_Authenticated_DelegatesToIdentityService()
{
var userId = Guid.NewGuid();
_identityService.ChangePasswordAsync(userId, "old-pass", "new-pass", Arg.Any<CancellationToken>())
_identityService
.ChangePasswordAsync(userId, "old-pass", "new-pass", Arg.Any<CancellationToken>())
.Returns(Result.Success());
var handler = new ChangePasswordCommandHandler(_identityService, FakeCurrentUser.Authenticated(userId));
var handler = new ChangePasswordCommandHandler(
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new ChangePasswordCommand("old-pass", "new-pass"), CancellationToken.None);
var result = await handler.Handle(
new ChangePasswordCommand("old-pass", "new-pass"),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await _identityService.Received(1).ChangePasswordAsync(userId, "old-pass", "new-pass", Arg.Any<CancellationToken>());
await _identityService
.Received(1)
.ChangePasswordAsync(userId, "old-pass", "new-pass", Arg.Any<CancellationToken>());
}
[Fact]
@@ -45,12 +66,19 @@ public class ChangePasswordCommandHandlerTests
{
var userId = Guid.NewGuid();
var error = Error.Validation("Auth.WrongCurrentPassword", "Текущий пароль неверен.");
_identityService.ChangePasswordAsync(userId, "wrong", "new-pass", Arg.Any<CancellationToken>())
_identityService
.ChangePasswordAsync(userId, "wrong", "new-pass", Arg.Any<CancellationToken>())
.Returns(Result.Failure(error));
var handler = new ChangePasswordCommandHandler(_identityService, FakeCurrentUser.Authenticated(userId));
var handler = new ChangePasswordCommandHandler(
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new ChangePasswordCommand("wrong", "new-pass"), CancellationToken.None);
var result = await handler.Handle(
new ChangePasswordCommand("wrong", "new-pass"),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(error, result.Error);
@@ -27,9 +27,14 @@ public class GetCurrentUserQueryHandlerTests
public async Task Handle_ProfileMissing_ReturnsUnauthorized()
{
var userId = Guid.NewGuid();
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns((CurrentUserProfile?)null);
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns((CurrentUserProfile?)null);
var handler = new GetCurrentUserQueryHandler(_identityService, FakeCurrentUser.Authenticated(userId));
var handler = new GetCurrentUserQueryHandler(
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new GetCurrentUserQuery(), CancellationToken.None);
@@ -41,12 +46,26 @@ public class GetCurrentUserQueryHandlerTests
public async Task Handle_AuthenticatedWithProfile_ReturnsCurrentUserDto()
{
var userId = Guid.NewGuid();
var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 3, RoleQuota.Unlimited, "sub-token");
var profile = new CurrentUserProfile(
userId,
"alice",
Guid.NewGuid(),
"user",
true,
false,
3,
RoleQuota.Unlimited,
"sub-token"
);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(profile);
_identityService.GetTelegramLinkInfoAsync(userId, Arg.Any<CancellationToken>())
_identityService
.GetTelegramLinkInfoAsync(userId, Arg.Any<CancellationToken>())
.Returns(new TelegramLinkInfo(true, 42, "alice_tg"));
var handler = new GetCurrentUserQueryHandler(_identityService, FakeCurrentUser.Authenticated(userId, "alice"));
var handler = new GetCurrentUserQueryHandler(
_identityService,
FakeCurrentUser.Authenticated(userId, "alice")
);
var result = await handler.Handle(new GetCurrentUserQuery(), CancellationToken.None);
@@ -11,9 +11,11 @@ public class LoginCommandHandlerTests
{
private readonly IIdentityService _identityService = Substitute.For<IIdentityService>();
private readonly IJwtTokenService _jwtTokenService = Substitute.For<IJwtTokenService>();
private readonly IRefreshTokenService _refreshTokenService = Substitute.For<IRefreshTokenService>();
private readonly IRefreshTokenService _refreshTokenService =
Substitute.For<IRefreshTokenService>();
private LoginCommandHandler CreateHandler() => new(_identityService, _jwtTokenService, _refreshTokenService);
private LoginCommandHandler CreateHandler() =>
new(_identityService, _jwtTokenService, _refreshTokenService);
[Fact]
public async Task Handle_WithValidCredentials_ReturnsAuthResult()
@@ -21,20 +23,33 @@ public class LoginCommandHandlerTests
var userId = Guid.NewGuid();
var authUser = new AuthenticatedUser(userId, "alice", "user");
var profile = new CurrentUserProfile(
userId, "alice", Guid.NewGuid(), "user", IsActivated: true, IsBlocked: false, MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited, SubscriptionToken: "sub-token");
userId,
"alice",
Guid.NewGuid(),
"user",
IsActivated: true,
IsBlocked: false,
MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited,
SubscriptionToken: "sub-token"
);
_identityService.ValidateCredentialsAsync("alice", "P@ssw0rd", Arg.Any<CancellationToken>())
_identityService
.ValidateCredentialsAsync("alice", "P@ssw0rd", Arg.Any<CancellationToken>())
.Returns(Result.Success(authUser));
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(profile);
_identityService.GetTelegramLinkInfoAsync(userId, Arg.Any<CancellationToken>())
_identityService
.GetTelegramLinkInfoAsync(userId, Arg.Any<CancellationToken>())
.Returns(new TelegramLinkInfo(true, 123456, "alice_tg"));
_jwtTokenService.GenerateAccessToken(Arg.Any<AuthenticatedUser>())
_jwtTokenService
.GenerateAccessToken(Arg.Any<AuthenticatedUser>())
.Returns(("access-token", DateTimeOffset.UtcNow.AddMinutes(15)));
_refreshTokenService.IssueAsync(userId, Arg.Any<CancellationToken>())
_refreshTokenService
.IssueAsync(userId, Arg.Any<CancellationToken>())
.Returns(new IssuedRefreshToken("refresh-token", DateTimeOffset.UtcNow.AddDays(30)));
var result = await CreateHandler().Handle(new LoginCommand("alice", "P@ssw0rd"), CancellationToken.None);
var result = await CreateHandler()
.Handle(new LoginCommand("alice", "P@ssw0rd"), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal("access-token", result.Value.AccessToken);
@@ -46,14 +61,18 @@ public class LoginCommandHandlerTests
[Fact]
public async Task Handle_WithInvalidCredentials_ReturnsFailureWithoutIssuingTokens()
{
_identityService.ValidateCredentialsAsync("alice", "wrong", Arg.Any<CancellationToken>())
_identityService
.ValidateCredentialsAsync("alice", "wrong", Arg.Any<CancellationToken>())
.Returns(Result.Failure<AuthenticatedUser>(AuthErrors.InvalidCredentials));
var result = await CreateHandler().Handle(new LoginCommand("alice", "wrong"), CancellationToken.None);
var result = await CreateHandler()
.Handle(new LoginCommand("alice", "wrong"), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.InvalidCredentials, result.Error);
await _refreshTokenService.DidNotReceive().IssueAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
await _refreshTokenService
.DidNotReceive()
.IssueAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
}
[Fact]
@@ -62,11 +81,15 @@ public class LoginCommandHandlerTests
var userId = Guid.NewGuid();
var authUser = new AuthenticatedUser(userId, "alice", "user");
_identityService.ValidateCredentialsAsync("alice", "P@ssw0rd", Arg.Any<CancellationToken>())
_identityService
.ValidateCredentialsAsync("alice", "P@ssw0rd", Arg.Any<CancellationToken>())
.Returns(Result.Success(authUser));
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns((CurrentUserProfile?)null);
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns((CurrentUserProfile?)null);
var result = await CreateHandler().Handle(new LoginCommand("alice", "P@ssw0rd"), CancellationToken.None);
var result = await CreateHandler()
.Handle(new LoginCommand("alice", "P@ssw0rd"), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.InvalidCredentials, result.Error);
@@ -11,27 +11,46 @@ public class RefreshCommandHandlerTests
{
private readonly IIdentityService _identityService = Substitute.For<IIdentityService>();
private readonly IJwtTokenService _jwtTokenService = Substitute.For<IJwtTokenService>();
private readonly IRefreshTokenService _refreshTokenService = Substitute.For<IRefreshTokenService>();
private readonly IRefreshTokenService _refreshTokenService =
Substitute.For<IRefreshTokenService>();
private RefreshCommandHandler CreateHandler() => new(_identityService, _jwtTokenService, _refreshTokenService);
private RefreshCommandHandler CreateHandler() =>
new(_identityService, _jwtTokenService, _refreshTokenService);
[Fact]
public async Task Handle_WithValidToken_RotatesAndReturnsNewAuthResult()
{
var userId = Guid.NewGuid();
var profile = new CurrentUserProfile(
userId, "alice", Guid.NewGuid(), "user", IsActivated: true, IsBlocked: false, MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited, SubscriptionToken: "sub-token");
var rotated = new RotatedRefreshToken(userId, "new-refresh-token", DateTimeOffset.UtcNow.AddDays(30));
userId,
"alice",
Guid.NewGuid(),
"user",
IsActivated: true,
IsBlocked: false,
MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited,
SubscriptionToken: "sub-token"
);
var rotated = new RotatedRefreshToken(
userId,
"new-refresh-token",
DateTimeOffset.UtcNow.AddDays(30)
);
_refreshTokenService.RotateAsync("old-token", Arg.Any<CancellationToken>()).Returns(Result.Success(rotated));
_refreshTokenService
.RotateAsync("old-token", Arg.Any<CancellationToken>())
.Returns(Result.Success(rotated));
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(profile);
_identityService.GetTelegramLinkInfoAsync(userId, Arg.Any<CancellationToken>())
_identityService
.GetTelegramLinkInfoAsync(userId, Arg.Any<CancellationToken>())
.Returns(new TelegramLinkInfo(false, null, null));
_jwtTokenService.GenerateAccessToken(Arg.Any<AuthenticatedUser>())
_jwtTokenService
.GenerateAccessToken(Arg.Any<AuthenticatedUser>())
.Returns(("new-access-token", DateTimeOffset.UtcNow.AddMinutes(15)));
var result = await CreateHandler().Handle(new RefreshCommand("old-token"), CancellationToken.None);
var result = await CreateHandler()
.Handle(new RefreshCommand("old-token"), CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal("new-access-token", result.Value.AccessToken);
@@ -41,26 +60,39 @@ public class RefreshCommandHandlerTests
[Fact]
public async Task Handle_WithInvalidOrReusedToken_ReturnsFailure()
{
_refreshTokenService.RotateAsync("stolen-token", Arg.Any<CancellationToken>())
_refreshTokenService
.RotateAsync("stolen-token", Arg.Any<CancellationToken>())
.Returns(Result.Failure<RotatedRefreshToken>(AuthErrors.InvalidRefreshToken));
var result = await CreateHandler().Handle(new RefreshCommand("stolen-token"), CancellationToken.None);
var result = await CreateHandler()
.Handle(new RefreshCommand("stolen-token"), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.InvalidRefreshToken, result.Error);
await _identityService.DidNotReceive().GetProfileAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
await _identityService
.DidNotReceive()
.GetProfileAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
}
[Fact]
public async Task Handle_WhenProfileNoLongerExists_ReturnsInvalidRefreshToken()
{
var userId = Guid.NewGuid();
var rotated = new RotatedRefreshToken(userId, "new-refresh-token", DateTimeOffset.UtcNow.AddDays(30));
var rotated = new RotatedRefreshToken(
userId,
"new-refresh-token",
DateTimeOffset.UtcNow.AddDays(30)
);
_refreshTokenService.RotateAsync("old-token", Arg.Any<CancellationToken>()).Returns(Result.Success(rotated));
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns((CurrentUserProfile?)null);
_refreshTokenService
.RotateAsync("old-token", Arg.Any<CancellationToken>())
.Returns(Result.Success(rotated));
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns((CurrentUserProfile?)null);
var result = await CreateHandler().Handle(new RefreshCommand("old-token"), CancellationToken.None);
var result = await CreateHandler()
.Handle(new RefreshCommand("old-token"), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.InvalidRefreshToken, result.Error);
@@ -15,21 +15,37 @@ public class RequireActivationBehaviorTests
private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();
private readonly IIdentityService _identityService = Substitute.For<IIdentityService>();
private RequireActivationBehavior<DummyRequest, Result<string>> CreateBehavior() => new(_currentUser, _identityService);
private RequireActivationBehavior<DummyRequest, Result<string>> CreateBehavior() =>
new(_currentUser, _identityService);
private static CurrentUserProfile CreateProfile(Guid userId, bool isActivated) =>
new(userId, "alice", Guid.NewGuid(), "user", IsActivated: isActivated, IsBlocked: false, MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited, SubscriptionToken: "sub-token");
new(
userId,
"alice",
Guid.NewGuid(),
"user",
IsActivated: isActivated,
IsBlocked: false,
MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited,
SubscriptionToken: "sub-token"
);
[Fact]
public async Task Handle_WhenActivated_CallsNext()
{
var userId = Guid.NewGuid();
_currentUser.UserId.Returns(userId);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(CreateProfile(userId, isActivated: true));
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns(CreateProfile(userId, isActivated: true));
var result = await CreateBehavior().Handle(
new DummyRequest(), () => Task.FromResult(Result.Success("ok")), CancellationToken.None);
var result = await CreateBehavior()
.Handle(
new DummyRequest(),
() => Task.FromResult(Result.Success("ok")),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal("ok", result.Value);
@@ -40,17 +56,21 @@ public class RequireActivationBehaviorTests
{
var userId = Guid.NewGuid();
_currentUser.UserId.Returns(userId);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(CreateProfile(userId, isActivated: false));
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns(CreateProfile(userId, isActivated: false));
var nextCalled = false;
var result = await CreateBehavior().Handle(
new DummyRequest(),
() =>
{
nextCalled = true;
return Task.FromResult(Result.Success("ok"));
},
CancellationToken.None);
var result = await CreateBehavior()
.Handle(
new DummyRequest(),
() =>
{
nextCalled = true;
return Task.FromResult(Result.Success("ok"));
},
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.NotActivated, result.Error);
@@ -62,8 +82,12 @@ public class RequireActivationBehaviorTests
{
_currentUser.UserId.Returns((Guid?)null);
var result = await CreateBehavior().Handle(
new DummyRequest(), () => Task.FromResult(Result.Success("ok")), CancellationToken.None);
var result = await CreateBehavior()
.Handle(
new DummyRequest(),
() => Task.FromResult(Result.Success("ok")),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.Unauthorized, result.Error);
@@ -74,10 +98,16 @@ public class RequireActivationBehaviorTests
{
var userId = Guid.NewGuid();
_currentUser.UserId.Returns(userId);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns((CurrentUserProfile?)null);
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns((CurrentUserProfile?)null);
var result = await CreateBehavior().Handle(
new DummyRequest(), () => Task.FromResult(Result.Success("ok")), CancellationToken.None);
var result = await CreateBehavior()
.Handle(
new DummyRequest(),
() => Task.FromResult(Result.Success("ok")),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.Unauthorized, result.Error);
@@ -27,16 +27,35 @@ public class GetMyConfigsQueryHandlerTests
var activeConfig = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "Active");
var revokedConfig = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "Revoked");
revokedConfig.Revoke();
var otherUsersConfig = VpnConfig.Create(otherUserId, inbound.Id, VpnProtocol.Vless, "Other");
var otherUsersConfig = VpnConfig.Create(
otherUserId,
inbound.Id,
VpnProtocol.Vless,
"Other"
);
dbContext.Inbounds.Add(inbound);
dbContext.VpnConfigs.AddRange(activeConfig, revokedConfig, otherUsersConfig);
await dbContext.SaveChangesAsync(CancellationToken.None);
var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 5, RoleQuota.Unlimited, "sub-token");
var profile = new CurrentUserProfile(
userId,
"alice",
Guid.NewGuid(),
"user",
true,
false,
5,
RoleQuota.Unlimited,
"sub-token"
);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(profile);
var handler = new GetMyConfigsQueryHandler(dbContext, _identityService, FakeCurrentUser.Authenticated(userId));
var handler = new GetMyConfigsQueryHandler(
dbContext,
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new GetMyConfigsQuery(), CancellationToken.None);
@@ -51,7 +70,11 @@ public class GetMyConfigsQueryHandlerTests
{
using var dbContext = InMemoryDbContextFactory.Create();
var handler = new GetMyConfigsQueryHandler(dbContext, _identityService, FakeCurrentUser.Anonymous());
var handler = new GetMyConfigsQueryHandler(
dbContext,
_identityService,
FakeCurrentUser.Anonymous()
);
var result = await handler.Handle(new GetMyConfigsQuery(), CancellationToken.None);
@@ -64,9 +87,15 @@ public class GetMyConfigsQueryHandlerTests
{
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns((CurrentUserProfile?)null);
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns((CurrentUserProfile?)null);
var handler = new GetMyConfigsQueryHandler(dbContext, _identityService, FakeCurrentUser.Authenticated(userId));
var handler = new GetMyConfigsQueryHandler(
dbContext,
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new GetMyConfigsQuery(), CancellationToken.None);
@@ -21,7 +21,12 @@ public class RevokeVpnConfigCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, null);
config.AssignRemoteClient("external-id");
@@ -31,15 +36,37 @@ public class RevokeVpnConfigCommandHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
var handler = new RevokeVpnConfigCommandHandler(dbContext, _gateway, _notifier, FakeCurrentUser.Authenticated(userId));
var handler = new RevokeVpnConfigCommandHandler(
dbContext,
_gateway,
_notifier,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new RevokeVpnConfigCommand(config.Id), CancellationToken.None);
var result = await handler.Handle(
new RevokeVpnConfigCommand(config.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(ConfigStatus.Revoked, config.Status);
await _gateway.Received(1).RemoveClientAsync(
Arg.Any<Node>(), inbound.RemoteInboundId, "external-id", config.Protocol, Arg.Any<CancellationToken>());
await _notifier.Received(1).NotifyConfigStatusChangedAsync(userId, config.Id, ConfigStatus.Revoked, Arg.Any<CancellationToken>());
await _gateway
.Received(1)
.RemoveClientAsync(
Arg.Any<Node>(),
inbound.RemoteInboundId,
"external-id",
config.Protocol,
Arg.Any<CancellationToken>()
);
await _notifier
.Received(1)
.NotifyConfigStatusChangedAsync(
userId,
config.Id,
ConfigStatus.Revoked,
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -56,13 +83,28 @@ public class RevokeVpnConfigCommandHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
var handler = new RevokeVpnConfigCommandHandler(dbContext, _gateway, _notifier, FakeCurrentUser.Authenticated(userId));
var handler = new RevokeVpnConfigCommandHandler(
dbContext,
_gateway,
_notifier,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new RevokeVpnConfigCommand(config.Id), CancellationToken.None);
var result = await handler.Handle(
new RevokeVpnConfigCommand(config.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await _gateway.DidNotReceive().RemoveClientAsync(
Arg.Any<PnvPanel.Domain.Nodes.Node>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<VpnProtocol>(), Arg.Any<CancellationToken>());
await _gateway
.DidNotReceive()
.RemoveClientAsync(
Arg.Any<PnvPanel.Domain.Nodes.Node>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<VpnProtocol>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -71,9 +113,17 @@ public class RevokeVpnConfigCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var handler = new RevokeVpnConfigCommandHandler(dbContext, _gateway, _notifier, FakeCurrentUser.Authenticated(userId));
var handler = new RevokeVpnConfigCommandHandler(
dbContext,
_gateway,
_notifier,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new RevokeVpnConfigCommand(Guid.NewGuid()), CancellationToken.None);
var result = await handler.Handle(
new RevokeVpnConfigCommand(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ConfigErrors.NotFound, result.Error);
@@ -84,9 +134,17 @@ public class RevokeVpnConfigCommandHandlerTests
{
using var dbContext = InMemoryDbContextFactory.Create();
var handler = new RevokeVpnConfigCommandHandler(dbContext, _gateway, _notifier, FakeCurrentUser.Anonymous());
var handler = new RevokeVpnConfigCommandHandler(
dbContext,
_gateway,
_notifier,
FakeCurrentUser.Anonymous()
);
var result = await handler.Handle(new RevokeVpnConfigCommand(Guid.NewGuid()), CancellationToken.None);
var result = await handler.Handle(
new RevokeVpnConfigCommand(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(PnvPanel.Application.Auth.AuthErrors.Unauthorized, result.Error);
@@ -18,16 +18,33 @@ public class RotateVpnConfigCommandHandlerTests
private readonly IIdentityService _identityService = Substitute.For<IIdentityService>();
private static CurrentUserProfile MakeProfile(Guid userId) =>
new(userId, "alice", Guid.NewGuid(), "user", true, false, 3, RoleQuota.Unlimited, "sub-token");
new(
userId,
"alice",
Guid.NewGuid(),
"user",
true,
false,
3,
RoleQuota.Unlimited,
"sub-token"
);
[Fact]
public async Task Handle_WhenActiveConfigOwnedByUser_RotatesAndAddsNewClient()
{
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(MakeProfile(userId));
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns(MakeProfile(userId));
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, "my-config");
config.AssignRemoteClient("old-external-id");
@@ -37,19 +54,41 @@ public class RotateVpnConfigCommandHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
_gateway.AddClientAsync(
Arg.Any<Node>(), inbound.RemoteInboundId, config.Protocol, Arg.Any<string>(),
Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
_gateway
.AddClientAsync(
Arg.Any<Node>(),
inbound.RemoteInboundId,
config.Protocol,
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<CancellationToken>()
)
.Returns(Result.Success("new-external-id"));
var handler = new RotateVpnConfigCommandHandler(dbContext, _gateway, _identityService, FakeCurrentUser.Authenticated(userId));
var handler = new RotateVpnConfigCommandHandler(
dbContext,
_gateway,
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new RotateVpnConfigCommand(config.Id), CancellationToken.None);
var result = await handler.Handle(
new RotateVpnConfigCommand(config.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal("new-external-id", config.ClientExternalId);
await _gateway.Received(1).RemoveClientAsync(
Arg.Any<Node>(), inbound.RemoteInboundId, "old-external-id", config.Protocol, Arg.Any<CancellationToken>());
await _gateway
.Received(1)
.RemoveClientAsync(
Arg.Any<Node>(),
inbound.RemoteInboundId,
"old-external-id",
config.Protocol,
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -58,9 +97,17 @@ public class RotateVpnConfigCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var handler = new RotateVpnConfigCommandHandler(dbContext, _gateway, _identityService, FakeCurrentUser.Authenticated(userId));
var handler = new RotateVpnConfigCommandHandler(
dbContext,
_gateway,
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new RotateVpnConfigCommand(Guid.NewGuid()), CancellationToken.None);
var result = await handler.Handle(
new RotateVpnConfigCommand(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ConfigErrors.NotFound, result.Error);
@@ -80,9 +127,17 @@ public class RotateVpnConfigCommandHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
var handler = new RotateVpnConfigCommandHandler(dbContext, _gateway, _identityService, FakeCurrentUser.Authenticated(otherUserId));
var handler = new RotateVpnConfigCommandHandler(
dbContext,
_gateway,
_identityService,
FakeCurrentUser.Authenticated(otherUserId)
);
var result = await handler.Handle(new RotateVpnConfigCommand(config.Id), CancellationToken.None);
var result = await handler.Handle(
new RotateVpnConfigCommand(config.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ConfigErrors.NotFound, result.Error);
@@ -102,9 +157,17 @@ public class RotateVpnConfigCommandHandlerTests
dbContext.VpnConfigs.Add(config);
await dbContext.SaveChangesAsync(CancellationToken.None);
var handler = new RotateVpnConfigCommandHandler(dbContext, _gateway, _identityService, FakeCurrentUser.Authenticated(userId));
var handler = new RotateVpnConfigCommandHandler(
dbContext,
_gateway,
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new RotateVpnConfigCommand(config.Id), CancellationToken.None);
var result = await handler.Handle(
new RotateVpnConfigCommand(config.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(ConfigErrors.NotFound, result.Error);
@@ -115,9 +178,16 @@ public class RotateVpnConfigCommandHandlerTests
{
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(MakeProfile(userId));
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns(MakeProfile(userId));
var node = Node.Register("node-1", new Uri("https://node1.example.com"), new NodeCredentials("admin", "protected"), null);
var node = Node.Register(
"node-1",
new Uri("https://node1.example.com"),
new NodeCredentials("admin", "protected"),
null
);
var inbound = Inbound.FromRemote(node.Id, "1", VpnProtocol.Vless, "remark", 443);
var config = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, null);
config.AssignRemoteClient("old-external-id");
@@ -128,14 +198,29 @@ public class RotateVpnConfigCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
var gatewayError = Error.Failure("Xui.Unreachable", "Панель недоступна.");
_gateway.AddClientAsync(
Arg.Any<Node>(), inbound.RemoteInboundId, config.Protocol, Arg.Any<string>(),
Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
_gateway
.AddClientAsync(
Arg.Any<Node>(),
inbound.RemoteInboundId,
config.Protocol,
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<int>(),
Arg.Any<CancellationToken>()
)
.Returns(Result.Failure<string>(gatewayError));
var handler = new RotateVpnConfigCommandHandler(dbContext, _gateway, _identityService, FakeCurrentUser.Authenticated(userId));
var handler = new RotateVpnConfigCommandHandler(
dbContext,
_gateway,
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new RotateVpnConfigCommand(config.Id), CancellationToken.None);
var result = await handler.Handle(
new RotateVpnConfigCommand(config.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(gatewayError, result.Error);
@@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
@@ -24,5 +23,4 @@
<ProjectReference Include="..\..\src\PnvPanel.Application\PnvPanel.Application.csproj" />
<ProjectReference Include="..\..\src\PnvPanel.Infrastructure\PnvPanel.Infrastructure.csproj" />
</ItemGroup>
</Project>
@@ -16,8 +16,17 @@ public class AddTicketCommentCommandHandlerTests
private readonly IRealtimeNotifier _notifier = Substitute.For<IRealtimeNotifier>();
private static CurrentUserProfile Profile(Guid userId, string role) =>
new(userId, "user", Guid.NewGuid(), role, IsActivated: true, IsBlocked: false, MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited, SubscriptionToken: "token");
new(
userId,
"user",
Guid.NewGuid(),
role,
IsActivated: true,
IsBlocked: false,
MaxConfigs: 3,
MaxIpLimit: RoleQuota.Unlimited,
SubscriptionToken: "token"
);
[Fact]
public async Task Handle_WhenOwnerComments_AddsCommentAndDoesNotNotifySelf()
@@ -28,16 +37,33 @@ public class AddTicketCommentCommandHandlerTests
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(Profile(userId, "user"));
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns(Profile(userId, "user"));
var currentUser = FakeCurrentUser.Authenticated(userId, "alice");
var handler = new AddTicketCommentCommandHandler(dbContext, _identityService, _fileStorage, _notifier, currentUser);
var handler = new AddTicketCommentCommandHandler(
dbContext,
_identityService,
_fileStorage,
_notifier,
currentUser
);
var result = await handler.Handle(new AddTicketCommentCommand(ticket.Id, "апдейт", []), CancellationToken.None);
var result = await handler.Handle(
new AddTicketCommentCommand(ticket.Id, "апдейт", []),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal("апдейт", result.Value.Body);
await _notifier.DidNotReceive().NotifyTicketUpdatedAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<CancellationToken>());
await _notifier
.DidNotReceive()
.NotifyTicketUpdatedAsync(
Arg.Any<Guid>(),
Arg.Any<Guid>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -50,15 +76,28 @@ public class AddTicketCommentCommandHandlerTests
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetProfileAsync(adminId, Arg.Any<CancellationToken>()).Returns(Profile(adminId, "admin"));
_identityService
.GetProfileAsync(adminId, Arg.Any<CancellationToken>())
.Returns(Profile(adminId, "admin"));
var currentUser = FakeCurrentUser.Authenticated(adminId, "admin");
var handler = new AddTicketCommentCommandHandler(dbContext, _identityService, _fileStorage, _notifier, currentUser);
var handler = new AddTicketCommentCommandHandler(
dbContext,
_identityService,
_fileStorage,
_notifier,
currentUser
);
var result = await handler.Handle(new AddTicketCommentCommand(ticket.Id, "ответ админа", []), CancellationToken.None);
var result = await handler.Handle(
new AddTicketCommentCommand(ticket.Id, "ответ админа", []),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await _notifier.Received(1).NotifyTicketUpdatedAsync(ticket.Id, ownerId, Arg.Any<CancellationToken>());
await _notifier
.Received(1)
.NotifyTicketUpdatedAsync(ticket.Id, ownerId, Arg.Any<CancellationToken>());
}
[Fact]
@@ -71,12 +110,23 @@ public class AddTicketCommentCommandHandlerTests
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetProfileAsync(strangerId, Arg.Any<CancellationToken>()).Returns(Profile(strangerId, "user"));
_identityService
.GetProfileAsync(strangerId, Arg.Any<CancellationToken>())
.Returns(Profile(strangerId, "user"));
var currentUser = FakeCurrentUser.Authenticated(strangerId);
var handler = new AddTicketCommentCommandHandler(dbContext, _identityService, _fileStorage, _notifier, currentUser);
var handler = new AddTicketCommentCommandHandler(
dbContext,
_identityService,
_fileStorage,
_notifier,
currentUser
);
var result = await handler.Handle(new AddTicketCommentCommand(ticket.Id, "текст", []), CancellationToken.None);
var result = await handler.Handle(
new AddTicketCommentCommand(ticket.Id, "текст", []),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.NotFound, result.Error);
@@ -92,12 +142,23 @@ public class AddTicketCommentCommandHandlerTests
dbContext.SupportTickets.Add(ticket);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(Profile(userId, "user"));
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns(Profile(userId, "user"));
var currentUser = FakeCurrentUser.Authenticated(userId);
var handler = new AddTicketCommentCommandHandler(dbContext, _identityService, _fileStorage, _notifier, currentUser);
var handler = new AddTicketCommentCommandHandler(
dbContext,
_identityService,
_fileStorage,
_notifier,
currentUser
);
var result = await handler.Handle(new AddTicketCommentCommand(ticket.Id, "текст", []), CancellationToken.None);
var result = await handler.Handle(
new AddTicketCommentCommand(ticket.Id, "текст", []),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.TicketClosed, result.Error);
@@ -21,9 +21,18 @@ public class CreateBugReportTicketCommandHandlerTests
var userId = Guid.NewGuid();
var currentUser = FakeCurrentUser.Authenticated(userId, "alice");
var handler = new CreateBugReportTicketCommandHandler(dbContext, _fileStorage, _notifier, _telegramNotifier, currentUser);
var handler = new CreateBugReportTicketCommandHandler(
dbContext,
_fileStorage,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(new CreateBugReportTicketCommand("Что-то сломалось", []), CancellationToken.None);
var result = await handler.Handle(
new CreateBugReportTicketCommand("Что-то сломалось", []),
CancellationToken.None
);
await dbContext.SaveChangesAsync(CancellationToken.None);
Assert.True(result.IsSuccess);
@@ -33,8 +42,14 @@ public class CreateBugReportTicketCommandHandlerTests
Assert.Equal("Что-то сломалось", result.Value.Comments[0].Body);
Assert.Single(dbContext.SupportTickets);
Assert.Single(dbContext.TicketComments);
await _telegramNotifier.Received(1).NotifyAdminsBugReportCreatedAsync(
Arg.Any<Guid>(), "alice", "Что-то сломалось", Arg.Any<CancellationToken>());
await _telegramNotifier
.Received(1)
.NotifyAdminsBugReportCreatedAsync(
Arg.Any<Guid>(),
"alice",
"Что-то сломалось",
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -42,13 +57,23 @@ public class CreateBugReportTicketCommandHandlerTests
{
using var dbContext = InMemoryDbContextFactory.Create();
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid());
var attachments = Enumerable.Range(0, 6)
var attachments = Enumerable
.Range(0, 6)
.Select(_ => new TicketAttachmentUpload(new MemoryStream(), "a.png", "image/png", 10))
.ToList();
var handler = new CreateBugReportTicketCommandHandler(dbContext, _fileStorage, _notifier, _telegramNotifier, currentUser);
var handler = new CreateBugReportTicketCommandHandler(
dbContext,
_fileStorage,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(new CreateBugReportTicketCommand("текст", attachments), CancellationToken.None);
var result = await handler.Handle(
new CreateBugReportTicketCommand("текст", attachments),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.TooManyAttachments, result.Error);
@@ -59,11 +84,23 @@ public class CreateBugReportTicketCommandHandlerTests
{
using var dbContext = InMemoryDbContextFactory.Create();
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid());
var attachments = new[] { new TicketAttachmentUpload(new MemoryStream(), "a.exe", "application/x-msdownload", 10) };
var attachments = new[]
{
new TicketAttachmentUpload(new MemoryStream(), "a.exe", "application/x-msdownload", 10),
};
var handler = new CreateBugReportTicketCommandHandler(dbContext, _fileStorage, _notifier, _telegramNotifier, currentUser);
var handler = new CreateBugReportTicketCommandHandler(
dbContext,
_fileStorage,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(new CreateBugReportTicketCommand("текст", attachments), CancellationToken.None);
var result = await handler.Handle(
new CreateBugReportTicketCommand("текст", attachments),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.UnsupportedAttachmentType, result.Error);
@@ -21,20 +21,36 @@ public class CreateRoleRequestTicketCommandHandlerTests
var userId = Guid.NewGuid();
var roleId = Guid.NewGuid();
var currentUser = FakeCurrentUser.Authenticated(userId, "alice");
_roleService.ListRolesAsync(Arg.Any<CancellationToken>())
_roleService
.ListRolesAsync(Arg.Any<CancellationToken>())
.Returns(new List<RoleDto> { new(roleId, "premium", 5, 2, false) });
var handler = new CreateRoleRequestTicketCommandHandler(dbContext, _roleService, _notifier, _telegramNotifier, currentUser);
var handler = new CreateRoleRequestTicketCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new CreateRoleRequestTicketCommand(roleId, null, null, null, "нужно больше конфигов"), CancellationToken.None);
new CreateRoleRequestTicketCommand(roleId, null, null, null, "нужно больше конфигов"),
CancellationToken.None
);
await dbContext.SaveChangesAsync(CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(roleId, result.Value.RequestedRoleId);
Assert.Equal("premium", result.Value.RequestedRoleName);
await _telegramNotifier.Received(1).NotifyAdminsRoleRequestCreatedAsync(
Arg.Any<Guid>(), "alice", "premium", "нужно больше конфигов", Arg.Any<CancellationToken>());
await _telegramNotifier
.Received(1)
.NotifyAdminsRoleRequestCreatedAsync(
Arg.Any<Guid>(),
"alice",
"premium",
"нужно больше конфигов",
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -43,13 +59,22 @@ public class CreateRoleRequestTicketCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var roleId = Guid.NewGuid();
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid());
_roleService.ListRolesAsync(Arg.Any<CancellationToken>())
_roleService
.ListRolesAsync(Arg.Any<CancellationToken>())
.Returns(new List<RoleDto> { new(roleId, "admin", -1, -1, true) });
var handler = new CreateRoleRequestTicketCommandHandler(dbContext, _roleService, _notifier, _telegramNotifier, currentUser);
var handler = new CreateRoleRequestTicketCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new CreateRoleRequestTicketCommand(roleId, null, null, null, "хочу быть админом"), CancellationToken.None);
new CreateRoleRequestTicketCommand(roleId, null, null, null, "хочу быть админом"),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.CannotRequestAdminRole, result.Error);
@@ -61,10 +86,18 @@ public class CreateRoleRequestTicketCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid(), "bob");
var handler = new CreateRoleRequestTicketCommandHandler(dbContext, _roleService, _notifier, _telegramNotifier, currentUser);
var handler = new CreateRoleRequestTicketCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new CreateRoleRequestTicketCommand(null, "custom", 10, 4, "нужна кастомная роль"), CancellationToken.None);
new CreateRoleRequestTicketCommand(null, "custom", 10, 4, "нужна кастомная роль"),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal("custom", result.Value.ProposedRoleName);
@@ -81,10 +114,18 @@ public class CreateRoleRequestTicketCommandHandlerTests
await dbContext.SaveChangesAsync(CancellationToken.None);
var currentUser = FakeCurrentUser.Authenticated(userId);
var handler = new CreateRoleRequestTicketCommandHandler(dbContext, _roleService, _notifier, _telegramNotifier, currentUser);
var handler = new CreateRoleRequestTicketCommandHandler(
dbContext,
_roleService,
_notifier,
_telegramNotifier,
currentUser
);
var result = await handler.Handle(
new CreateRoleRequestTicketCommand(null, "y", 2, 2, "ещё заявка"), CancellationToken.None);
new CreateRoleRequestTicketCommand(null, "y", 2, 2, "ещё заявка"),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.RoleRequestAlreadyPending, result.Error);
@@ -25,12 +25,21 @@ public class ReopenTicketCommandHandlerTests
var currentUser = FakeCurrentUser.Authenticated(userId);
var handler = new ReopenTicketCommandHandler(dbContext, _telegramNotifier, currentUser);
var result = await handler.Handle(new ReopenTicketCommand(ticket.Id), CancellationToken.None);
var result = await handler.Handle(
new ReopenTicketCommand(ticket.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TicketStatus.Open, ticket.Status);
await _telegramNotifier.Received(1).NotifyAdminsTicketReopenedAsync(
ticket.Id, Arg.Any<string>(), TicketType.BugReport, Arg.Any<CancellationToken>());
await _telegramNotifier
.Received(1)
.NotifyAdminsTicketReopenedAsync(
ticket.Id,
Arg.Any<string>(),
TicketType.BugReport,
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -45,7 +54,10 @@ public class ReopenTicketCommandHandlerTests
var currentUser = FakeCurrentUser.Authenticated(userId);
var handler = new ReopenTicketCommandHandler(dbContext, _telegramNotifier, currentUser);
var result = await handler.Handle(new ReopenTicketCommand(ticket.Id), CancellationToken.None);
var result = await handler.Handle(
new ReopenTicketCommand(ticket.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.NotResolved, result.Error);
@@ -63,7 +75,10 @@ public class ReopenTicketCommandHandlerTests
var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid());
var handler = new ReopenTicketCommandHandler(dbContext, _telegramNotifier, currentUser);
var result = await handler.Handle(new ReopenTicketCommand(ticket.Id), CancellationToken.None);
var result = await handler.Handle(
new ReopenTicketCommand(ticket.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(SupportErrors.NotFound, result.Error);
@@ -23,11 +23,16 @@ public class ApproveTelegramLoginCommandHandlerTests
dbContext.TelegramLoginRequests.Add(request);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>()).Returns(userId);
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns(userId);
var handler = new ApproveTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new ApproveTelegramLoginCommand(request.Id, telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new ApproveTelegramLoginCommand(request.Id, telegramUserId),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TelegramLoginStatus.Approved, request.Status);
@@ -40,12 +45,16 @@ public class ApproveTelegramLoginCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
const long telegramUserId = 123456L;
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns((Guid?)null);
var handler = new ApproveTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new ApproveTelegramLoginCommand(Guid.NewGuid(), telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new ApproveTelegramLoginCommand(Guid.NewGuid(), telegramUserId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramErrors.NotLinked, result.Error);
@@ -58,11 +67,16 @@ public class ApproveTelegramLoginCommandHandlerTests
var userId = Guid.NewGuid();
const long telegramUserId = 123456L;
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>()).Returns(userId);
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns(userId);
var handler = new ApproveTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new ApproveTelegramLoginCommand(Guid.NewGuid(), telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new ApproveTelegramLoginCommand(Guid.NewGuid(), telegramUserId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramErrors.LoginRequestNotFound, result.Error);
@@ -80,11 +94,16 @@ public class ApproveTelegramLoginCommandHandlerTests
dbContext.TelegramLoginRequests.Add(request);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>()).Returns(userId);
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns(userId);
var handler = new ApproveTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new ApproveTelegramLoginCommand(request.Id, telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new ApproveTelegramLoginCommand(request.Id, telegramUserId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal("Telegram.LoginRequestInvalid", result.Error.Code);
@@ -101,11 +120,16 @@ public class ApproveTelegramLoginCommandHandlerTests
dbContext.TelegramLoginRequests.Add(request);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>()).Returns(userId);
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns(userId);
var handler = new ApproveTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new ApproveTelegramLoginCommand(request.Id, telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new ApproveTelegramLoginCommand(request.Id, telegramUserId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal("Telegram.LoginRequestInvalid", result.Error.Code);
@@ -22,12 +22,16 @@ public class LinkTelegramCommandHandlerTests
dbContext.TelegramLinkTokens.Add(token);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.LinkTelegramAsync(userId, 123456L, "alice_tg", Arg.Any<CancellationToken>())
_identityService
.LinkTelegramAsync(userId, 123456L, "alice_tg", Arg.Any<CancellationToken>())
.Returns(Result.Success());
var handler = new LinkTelegramCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new LinkTelegramCommand(token.Token, 123456L, "alice_tg"), CancellationToken.None);
var result = await handler.Handle(
new LinkTelegramCommand(token.Token, 123456L, "alice_tg"),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(userId, result.Value);
@@ -41,7 +45,10 @@ public class LinkTelegramCommandHandlerTests
var handler = new LinkTelegramCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new LinkTelegramCommand("missing-token", 123456L, null), CancellationToken.None);
var result = await handler.Handle(
new LinkTelegramCommand("missing-token", 123456L, null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramErrors.LinkTokenNotFound, result.Error);
@@ -59,12 +66,21 @@ public class LinkTelegramCommandHandlerTests
var handler = new LinkTelegramCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new LinkTelegramCommand(token.Token, 123456L, null), CancellationToken.None);
var result = await handler.Handle(
new LinkTelegramCommand(token.Token, 123456L, null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramErrors.LinkTokenNotFound, result.Error);
await _identityService.DidNotReceive().LinkTelegramAsync(
Arg.Any<Guid>(), Arg.Any<long>(), Arg.Any<string?>(), Arg.Any<CancellationToken>());
await _identityService
.DidNotReceive()
.LinkTelegramAsync(
Arg.Any<Guid>(),
Arg.Any<long>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>()
);
}
[Fact]
@@ -78,7 +94,10 @@ public class LinkTelegramCommandHandlerTests
var handler = new LinkTelegramCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new LinkTelegramCommand(token.Token, 123456L, null), CancellationToken.None);
var result = await handler.Handle(
new LinkTelegramCommand(token.Token, 123456L, null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramErrors.LinkTokenNotFound, result.Error);
@@ -93,13 +112,20 @@ public class LinkTelegramCommandHandlerTests
dbContext.TelegramLinkTokens.Add(token);
await dbContext.SaveChangesAsync(CancellationToken.None);
var error = Error.Conflict("Telegram.AlreadyLinked", "Этот Telegram уже привязан к другому аккаунту.");
_identityService.LinkTelegramAsync(userId, 123456L, null, Arg.Any<CancellationToken>())
var error = Error.Conflict(
"Telegram.AlreadyLinked",
"Этот Telegram уже привязан к другому аккаунту."
);
_identityService
.LinkTelegramAsync(userId, 123456L, null, Arg.Any<CancellationToken>())
.Returns(Result.Failure(error));
var handler = new LinkTelegramCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new LinkTelegramCommand(token.Token, 123456L, null), CancellationToken.None);
var result = await handler.Handle(
new LinkTelegramCommand(token.Token, 123456L, null),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(error, result.Error);
@@ -23,11 +23,16 @@ public class RejectTelegramLoginCommandHandlerTests
dbContext.TelegramLoginRequests.Add(request);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>()).Returns(userId);
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns(userId);
var handler = new RejectTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new RejectTelegramLoginCommand(request.Id, telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new RejectTelegramLoginCommand(request.Id, telegramUserId),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TelegramLoginStatus.Rejected, request.Status);
@@ -39,12 +44,16 @@ public class RejectTelegramLoginCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
const long telegramUserId = 123456L;
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns((Guid?)null);
var handler = new RejectTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new RejectTelegramLoginCommand(Guid.NewGuid(), telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new RejectTelegramLoginCommand(Guid.NewGuid(), telegramUserId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramErrors.NotLinked, result.Error);
@@ -57,11 +66,16 @@ public class RejectTelegramLoginCommandHandlerTests
var userId = Guid.NewGuid();
const long telegramUserId = 123456L;
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>()).Returns(userId);
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns(userId);
var handler = new RejectTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new RejectTelegramLoginCommand(Guid.NewGuid(), telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new RejectTelegramLoginCommand(Guid.NewGuid(), telegramUserId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramErrors.LoginRequestNotFound, result.Error);
@@ -79,11 +93,16 @@ public class RejectTelegramLoginCommandHandlerTests
dbContext.TelegramLoginRequests.Add(request);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>()).Returns(userId);
_identityService
.FindUserIdByTelegramUserIdAsync(telegramUserId, Arg.Any<CancellationToken>())
.Returns(userId);
var handler = new RejectTelegramLoginCommandHandler(dbContext, _identityService);
var result = await handler.Handle(new RejectTelegramLoginCommand(request.Id, telegramUserId), CancellationToken.None);
var result = await handler.Handle(
new RejectTelegramLoginCommand(request.Id, telegramUserId),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal("Telegram.LoginRequestInvalid", result.Error.Code);
@@ -13,7 +13,10 @@ public class CreateLinkTokenCommandHandlerTests
using var dbContext = InMemoryDbContextFactory.Create();
var userId = Guid.NewGuid();
var handler = new CreateLinkTokenCommandHandler(dbContext, FakeCurrentUser.Authenticated(userId));
var handler = new CreateLinkTokenCommandHandler(
dbContext,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new CreateLinkTokenCommand(), CancellationToken.None);
@@ -13,10 +13,12 @@ public class GetLoginRequestStatusQueryHandlerTests
{
private readonly IIdentityService _identityService = Substitute.For<IIdentityService>();
private readonly IJwtTokenService _jwtTokenService = Substitute.For<IJwtTokenService>();
private readonly IRefreshTokenService _refreshTokenService = Substitute.For<IRefreshTokenService>();
private readonly IRefreshTokenService _refreshTokenService =
Substitute.For<IRefreshTokenService>();
private TelegramNs.GetLoginRequestStatusQueryHandler CreateHandler(PnvPanel.Infrastructure.Persistence.AppDbContext dbContext)
=> new(dbContext, _identityService, _jwtTokenService, _refreshTokenService);
private TelegramNs.GetLoginRequestStatusQueryHandler CreateHandler(
PnvPanel.Infrastructure.Persistence.AppDbContext dbContext
) => new(dbContext, _identityService, _jwtTokenService, _refreshTokenService);
[Fact]
public async Task Handle_WhenRequestNotFound_ReturnsNotFound()
@@ -25,7 +27,10 @@ public class GetLoginRequestStatusQueryHandlerTests
var handler = CreateHandler(dbContext);
var result = await handler.Handle(new TelegramNs.GetLoginRequestStatusQuery(Guid.NewGuid()), CancellationToken.None);
var result = await handler.Handle(
new TelegramNs.GetLoginRequestStatusQuery(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(TelegramNs.TelegramErrors.LoginRequestNotFound, result.Error);
@@ -41,12 +46,17 @@ public class GetLoginRequestStatusQueryHandlerTests
var handler = CreateHandler(dbContext);
var result = await handler.Handle(new TelegramNs.GetLoginRequestStatusQuery(request.Id), CancellationToken.None);
var result = await handler.Handle(
new TelegramNs.GetLoginRequestStatusQuery(request.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TelegramLoginStatus.Expired, result.Value.Status);
Assert.Null(result.Value.Auth);
await _refreshTokenService.DidNotReceive().IssueAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
await _refreshTokenService
.DidNotReceive()
.IssueAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
}
[Fact]
@@ -59,7 +69,10 @@ public class GetLoginRequestStatusQueryHandlerTests
var handler = CreateHandler(dbContext);
var result = await handler.Handle(new TelegramNs.GetLoginRequestStatusQuery(request.Id), CancellationToken.None);
var result = await handler.Handle(
new TelegramNs.GetLoginRequestStatusQuery(request.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TelegramLoginStatus.Pending, result.Value.Status);
@@ -76,16 +89,31 @@ public class GetLoginRequestStatusQueryHandlerTests
dbContext.TelegramLoginRequests.Add(request);
await dbContext.SaveChangesAsync(CancellationToken.None);
var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 3, RoleQuota.Unlimited, "sub-token");
var profile = new CurrentUserProfile(
userId,
"alice",
Guid.NewGuid(),
"user",
true,
false,
3,
RoleQuota.Unlimited,
"sub-token"
);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns(profile);
_jwtTokenService.GenerateAccessToken(Arg.Any<AuthenticatedUser>())
_jwtTokenService
.GenerateAccessToken(Arg.Any<AuthenticatedUser>())
.Returns(("access-token", DateTimeOffset.UtcNow.AddMinutes(15)));
_refreshTokenService.IssueAsync(userId, Arg.Any<CancellationToken>())
_refreshTokenService
.IssueAsync(userId, Arg.Any<CancellationToken>())
.Returns(new IssuedRefreshToken("refresh-token", DateTimeOffset.UtcNow.AddDays(30)));
var handler = CreateHandler(dbContext);
var result = await handler.Handle(new TelegramNs.GetLoginRequestStatusQuery(request.Id), CancellationToken.None);
var result = await handler.Handle(
new TelegramNs.GetLoginRequestStatusQuery(request.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(TelegramLoginStatus.Approved, result.Value.Status);
@@ -105,11 +133,16 @@ public class GetLoginRequestStatusQueryHandlerTests
dbContext.TelegramLoginRequests.Add(request);
await dbContext.SaveChangesAsync(CancellationToken.None);
_identityService.GetProfileAsync(userId, Arg.Any<CancellationToken>()).Returns((CurrentUserProfile?)null);
_identityService
.GetProfileAsync(userId, Arg.Any<CancellationToken>())
.Returns((CurrentUserProfile?)null);
var handler = CreateHandler(dbContext);
var result = await handler.Handle(new TelegramNs.GetLoginRequestStatusQuery(request.Id), CancellationToken.None);
var result = await handler.Handle(
new TelegramNs.GetLoginRequestStatusQuery(request.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.Unauthorized, result.Error);
@@ -16,26 +16,38 @@ public class UnlinkTelegramCommandHandlerTests
public async Task Handle_WhenAuthenticated_DelegatesToIdentityService()
{
var userId = Guid.NewGuid();
_identityService.UnlinkTelegramAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Success());
_identityService
.UnlinkTelegramAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Success());
var handler = new UnlinkTelegramCommandHandler(_identityService, FakeCurrentUser.Authenticated(userId));
var handler = new UnlinkTelegramCommandHandler(
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new UnlinkTelegramCommand(), CancellationToken.None);
Assert.True(result.IsSuccess);
await _identityService.Received(1).UnlinkTelegramAsync(userId, Arg.Any<CancellationToken>());
await _identityService
.Received(1)
.UnlinkTelegramAsync(userId, Arg.Any<CancellationToken>());
}
[Fact]
public async Task Handle_WhenNotAuthenticated_ReturnsUnauthorized()
{
var handler = new UnlinkTelegramCommandHandler(_identityService, FakeCurrentUser.Anonymous());
var handler = new UnlinkTelegramCommandHandler(
_identityService,
FakeCurrentUser.Anonymous()
);
var result = await handler.Handle(new UnlinkTelegramCommand(), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Equal(AuthErrors.Unauthorized, result.Error);
await _identityService.DidNotReceive().UnlinkTelegramAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
await _identityService
.DidNotReceive()
.UnlinkTelegramAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
}
[Fact]
@@ -43,9 +55,14 @@ public class UnlinkTelegramCommandHandlerTests
{
var userId = Guid.NewGuid();
var error = TelegramErrors.NotLinked;
_identityService.UnlinkTelegramAsync(userId, Arg.Any<CancellationToken>()).Returns(Result.Failure(error));
_identityService
.UnlinkTelegramAsync(userId, Arg.Any<CancellationToken>())
.Returns(Result.Failure(error));
var handler = new UnlinkTelegramCommandHandler(_identityService, FakeCurrentUser.Authenticated(userId));
var handler = new UnlinkTelegramCommandHandler(
_identityService,
FakeCurrentUser.Authenticated(userId)
);
var result = await handler.Handle(new UnlinkTelegramCommand(), CancellationToken.None);
@@ -10,8 +10,8 @@ public sealed class FakeCurrentUser : ICurrentUser
public bool IsAuthenticated => UserId is not null;
public static FakeCurrentUser Authenticated(Guid userId, string userName = "testuser")
=> new() { UserId = userId, UserName = userName };
public static FakeCurrentUser Authenticated(Guid userId, string userName = "testuser") =>
new() { UserId = userId, UserName = userName };
public static FakeCurrentUser Anonymous() => new();
}