Refactor project files for improved readability and structure
- 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:
+56
-12
@@ -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>());
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -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);
|
||||
|
||||
+58
-12
@@ -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);
|
||||
|
||||
+36
-9
@@ -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);
|
||||
|
||||
+43
-7
@@ -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);
|
||||
|
||||
|
||||
+58
-14
@@ -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);
|
||||
|
||||
+132
-29
@@ -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>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-5
@@ -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);
|
||||
|
||||
+75
-15
@@ -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);
|
||||
|
||||
|
||||
+95
-21
@@ -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>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+39
-11
@@ -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);
|
||||
|
||||
+50
-20
@@ -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);
|
||||
|
||||
+35
-6
@@ -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);
|
||||
|
||||
|
||||
+72
-14
@@ -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);
|
||||
|
||||
+108
-23
@@ -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>
|
||||
|
||||
+77
-16
@@ -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);
|
||||
|
||||
+47
-10
@@ -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);
|
||||
|
||||
+53
-12
@@ -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);
|
||||
|
||||
+20
-5
@@ -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);
|
||||
|
||||
+34
-10
@@ -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);
|
||||
|
||||
+36
-10
@@ -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);
|
||||
|
||||
+27
-8
@@ -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);
|
||||
|
||||
+4
-1
@@ -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);
|
||||
|
||||
|
||||
+46
-13
@@ -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);
|
||||
|
||||
+24
-7
@@ -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();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,14 @@ public class ClientAppTests
|
||||
[Fact]
|
||||
public void Create_SetsEnabledByDefault()
|
||||
{
|
||||
var app = ClientApp.Create("v2rayNG", new Uri("https://play.google.com/store/apps/details?id=x"), OsPlatform.Android, "desc", null, 1);
|
||||
var app = ClientApp.Create(
|
||||
"v2rayNG",
|
||||
new Uri("https://play.google.com/store/apps/details?id=x"),
|
||||
OsPlatform.Android,
|
||||
"desc",
|
||||
null,
|
||||
1
|
||||
);
|
||||
|
||||
Assert.Equal("v2rayNG", app.Name);
|
||||
Assert.Equal(OsPlatform.Android, app.OperatingSystem);
|
||||
@@ -19,9 +26,24 @@ public class ClientAppTests
|
||||
[Fact]
|
||||
public void Update_ReplacesAllMutableFields()
|
||||
{
|
||||
var app = ClientApp.Create("Old", new Uri("https://old.example.com"), OsPlatform.IOS, "old", "old-icon", 1);
|
||||
var app = ClientApp.Create(
|
||||
"Old",
|
||||
new Uri("https://old.example.com"),
|
||||
OsPlatform.IOS,
|
||||
"old",
|
||||
"old-icon",
|
||||
1
|
||||
);
|
||||
|
||||
app.Update("New", new Uri("https://new.example.com"), OsPlatform.MacOS, "new", "new-icon", 2, isEnabled: false);
|
||||
app.Update(
|
||||
"New",
|
||||
new Uri("https://new.example.com"),
|
||||
OsPlatform.MacOS,
|
||||
"new",
|
||||
"new-icon",
|
||||
2,
|
||||
isEnabled: false
|
||||
);
|
||||
|
||||
Assert.Equal("New", app.Name);
|
||||
Assert.Equal(new Uri("https://new.example.com"), app.DownloadUrl);
|
||||
|
||||
@@ -10,7 +10,14 @@ public class AuditLogTests
|
||||
{
|
||||
var actorId = Guid.NewGuid();
|
||||
|
||||
var log = AuditLog.Create(actorId, "user.blocked", "AppUser", actorId.ToString(), "{\"reason\":\"abuse\"}", AuditSource.Web);
|
||||
var log = AuditLog.Create(
|
||||
actorId,
|
||||
"user.blocked",
|
||||
"AppUser",
|
||||
actorId.ToString(),
|
||||
"{\"reason\":\"abuse\"}",
|
||||
AuditSource.Web
|
||||
);
|
||||
|
||||
Assert.Equal(actorId, log.ActorId);
|
||||
Assert.Equal("user.blocked", log.Action);
|
||||
@@ -24,7 +31,14 @@ public class AuditLogTests
|
||||
[Fact]
|
||||
public void Create_AllowsNullActorForSystemActions()
|
||||
{
|
||||
var log = AuditLog.Create(null, "node.healthcheck", "Node", Guid.NewGuid().ToString(), null, AuditSource.System);
|
||||
var log = AuditLog.Create(
|
||||
null,
|
||||
"node.healthcheck",
|
||||
"Node",
|
||||
Guid.NewGuid().ToString(),
|
||||
null,
|
||||
AuditSource.System
|
||||
);
|
||||
|
||||
Assert.Null(log.ActorId);
|
||||
Assert.Equal(AuditSource.System, log.Source);
|
||||
|
||||
@@ -11,7 +11,12 @@ public class NodeTests
|
||||
[Fact]
|
||||
public void Register_WithAbsoluteUri_CreatesEnabledUnknownStatusNode()
|
||||
{
|
||||
var node = Node.Register("Germany-1", new Uri("https://de1.example.com:2053"), Credentials, "Germany");
|
||||
var node = Node.Register(
|
||||
"Germany-1",
|
||||
new Uri("https://de1.example.com:2053"),
|
||||
Credentials,
|
||||
"Germany"
|
||||
);
|
||||
|
||||
Assert.Equal("Germany-1", node.Name);
|
||||
Assert.Equal("Germany", node.Location);
|
||||
@@ -25,13 +30,20 @@ public class NodeTests
|
||||
{
|
||||
var relativeUri = new Uri("de1.example.com", UriKind.Relative);
|
||||
|
||||
Assert.Throws<DomainException>(() => Node.Register("Germany-1", relativeUri, Credentials, null));
|
||||
Assert.Throws<DomainException>(() =>
|
||||
Node.Register("Germany-1", relativeUri, Credentials, null)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateDetails_ChangesNameAndLocation()
|
||||
{
|
||||
var node = Node.Register("Old", new Uri("https://example.com"), Credentials, "Old location");
|
||||
var node = Node.Register(
|
||||
"Old",
|
||||
new Uri("https://example.com"),
|
||||
Credentials,
|
||||
"Old location"
|
||||
);
|
||||
|
||||
node.UpdateDetails("New", "New location");
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -23,5 +22,4 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\PnvPanel.Domain\PnvPanel.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -8,7 +8,11 @@ namespace PnvPanel.IntegrationTests.Activation;
|
||||
[Collection(IntegrationTestCollection.Name)]
|
||||
public class ActivationFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
{
|
||||
private sealed record ActivationRequestResponse(Guid Id, string? Comment, DateTimeOffset CreatedAt);
|
||||
private sealed record ActivationRequestResponse(
|
||||
Guid Id,
|
||||
string? Comment,
|
||||
DateTimeOffset CreatedAt
|
||||
);
|
||||
|
||||
[Fact]
|
||||
public async Task RequestThenAdminApprove_ActivatesUser()
|
||||
@@ -18,7 +22,10 @@ public class ActivationFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
var (userId, userToken) = await RegisterAndLoginAsync(userClient, userName, "P@ssw0rd123");
|
||||
userClient.UseBearerToken(userToken);
|
||||
|
||||
var requestResponse = await userClient.PostJsonAsync("/api/activation/request", new { comment = "Please activate me" });
|
||||
var requestResponse = await userClient.PostJsonAsync(
|
||||
"/api/activation/request",
|
||||
new { comment = "Please activate me" }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, requestResponse.StatusCode);
|
||||
var request = await requestResponse.ReadAsAsync<ActivationRequestResponse>();
|
||||
Assert.NotNull(request);
|
||||
@@ -27,7 +34,10 @@ public class ActivationFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
var adminToken = await LoginAsAdminAsync(adminClient);
|
||||
adminClient.UseBearerToken(adminToken);
|
||||
|
||||
var approveResponse = await adminClient.PostAsync($"/api/admin/activation-requests/{request!.Id}/approve", content: null);
|
||||
var approveResponse = await adminClient.PostAsync(
|
||||
$"/api/admin/activation-requests/{request!.Id}/approve",
|
||||
content: null
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.NoContent, approveResponse.StatusCode);
|
||||
|
||||
var meResponse = await userClient.GetAsync("/api/auth/me");
|
||||
@@ -44,14 +54,20 @@ public class ActivationFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
var (_, userToken) = await RegisterAndLoginAsync(userClient, userName, "P@ssw0rd123");
|
||||
userClient.UseBearerToken(userToken);
|
||||
|
||||
var requestResponse = await userClient.PostJsonAsync("/api/activation/request", new { comment = (string?)null });
|
||||
var requestResponse = await userClient.PostJsonAsync(
|
||||
"/api/activation/request",
|
||||
new { comment = (string?)null }
|
||||
);
|
||||
var request = await requestResponse.ReadAsAsync<ActivationRequestResponse>();
|
||||
|
||||
using var adminClient = factory.CreateClient();
|
||||
var adminToken = await LoginAsAdminAsync(adminClient);
|
||||
adminClient.UseBearerToken(adminToken);
|
||||
|
||||
var rejectResponse = await adminClient.PostJsonAsync($"/api/admin/activation-requests/{request!.Id}/reject", new { reason = "not enough info" });
|
||||
var rejectResponse = await adminClient.PostJsonAsync(
|
||||
$"/api/admin/activation-requests/{request!.Id}/reject",
|
||||
new { reason = "not enough info" }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.NoContent, rejectResponse.StatusCode);
|
||||
|
||||
var meResponse = await userClient.GetAsync("/api/auth/me");
|
||||
@@ -67,10 +83,16 @@ public class ActivationFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
var (_, userToken) = await RegisterAndLoginAsync(userClient, userName, "P@ssw0rd123");
|
||||
userClient.UseBearerToken(userToken);
|
||||
|
||||
var first = await userClient.PostJsonAsync("/api/activation/request", new { comment = (string?)null });
|
||||
var first = await userClient.PostJsonAsync(
|
||||
"/api/activation/request",
|
||||
new { comment = (string?)null }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, first.StatusCode);
|
||||
|
||||
var second = await userClient.PostJsonAsync("/api/activation/request", new { comment = (string?)null });
|
||||
var second = await userClient.PostJsonAsync(
|
||||
"/api/activation/request",
|
||||
new { comment = (string?)null }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.Conflict, second.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,28 @@ namespace PnvPanel.IntegrationTests.Admin;
|
||||
[Collection(IntegrationTestCollection.Name)]
|
||||
public class NodeInboundCrudTests(PnvPanelWebApplicationFactory factory)
|
||||
{
|
||||
private sealed record NodeResponse(Guid Id, string Name, string BaseAddress, string Username, string? Location, string Status, bool IsEnabled);
|
||||
private sealed record NodeResponse(
|
||||
Guid Id,
|
||||
string Name,
|
||||
string BaseAddress,
|
||||
string Username,
|
||||
string? Location,
|
||||
string Status,
|
||||
bool IsEnabled
|
||||
);
|
||||
|
||||
private sealed record InboundResponse(
|
||||
Guid Id, Guid NodeId, string RemoteInboundId, string Protocol, string Remark, int Port,
|
||||
bool IsPublished, string? DisplayName, int? MaxClients, IReadOnlyList<Guid> AllowedRoleIds);
|
||||
Guid Id,
|
||||
Guid NodeId,
|
||||
string RemoteInboundId,
|
||||
string Protocol,
|
||||
string Remark,
|
||||
int Port,
|
||||
bool IsPublished,
|
||||
string? DisplayName,
|
||||
int? MaxClients,
|
||||
IReadOnlyList<Guid> AllowedRoleIds
|
||||
);
|
||||
|
||||
private sealed record SyncNodeResponse(int InboundsSynced, string Status);
|
||||
|
||||
@@ -23,14 +40,17 @@ public class NodeInboundCrudTests(PnvPanelWebApplicationFactory factory)
|
||||
var adminToken = await LoginAsAdminAsync(adminClient);
|
||||
adminClient.UseBearerToken(adminToken);
|
||||
|
||||
var registerResponse = await adminClient.PostJsonAsync("/api/admin/nodes", new
|
||||
{
|
||||
name = $"Node-{Guid.NewGuid():N}"[..20],
|
||||
baseAddress = "https://node.example.com:2053",
|
||||
username = "admin",
|
||||
password = "node-panel-password",
|
||||
location = "Germany",
|
||||
});
|
||||
var registerResponse = await adminClient.PostJsonAsync(
|
||||
"/api/admin/nodes",
|
||||
new
|
||||
{
|
||||
name = $"Node-{Guid.NewGuid():N}"[..20],
|
||||
baseAddress = "https://node.example.com:2053",
|
||||
username = "admin",
|
||||
password = "node-panel-password",
|
||||
location = "Germany",
|
||||
}
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, registerResponse.StatusCode);
|
||||
var node = await registerResponse.ReadAsAsync<NodeResponse>();
|
||||
Assert.NotNull(node);
|
||||
@@ -40,24 +60,32 @@ public class NodeInboundCrudTests(PnvPanelWebApplicationFactory factory)
|
||||
var nodes = await listNodesResponse.ReadAsAsync<List<NodeResponse>>();
|
||||
Assert.Contains(nodes!, n => n.Id == node!.Id);
|
||||
|
||||
var syncResponse = await adminClient.PostAsync($"/api/admin/nodes/{node!.Id}/sync", content: null);
|
||||
var syncResponse = await adminClient.PostAsync(
|
||||
$"/api/admin/nodes/{node!.Id}/sync",
|
||||
content: null
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, syncResponse.StatusCode);
|
||||
var sync = await syncResponse.ReadAsAsync<SyncNodeResponse>();
|
||||
Assert.Equal(1, sync!.InboundsSynced);
|
||||
|
||||
var listInboundsResponse = await adminClient.GetAsync($"/api/admin/inbounds?nodeId={node.Id}");
|
||||
var listInboundsResponse = await adminClient.GetAsync(
|
||||
$"/api/admin/inbounds?nodeId={node.Id}"
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, listInboundsResponse.StatusCode);
|
||||
var inbounds = await listInboundsResponse.ReadAsAsync<List<InboundResponse>>();
|
||||
var inbound = Assert.Single(inbounds!);
|
||||
Assert.False(inbound.IsPublished);
|
||||
|
||||
var publishResponse = await adminClient.SendPutJsonAsync($"/api/admin/inbounds/{inbound.Id}/publish", new
|
||||
{
|
||||
isPublished = true,
|
||||
displayName = "Germany (VLESS)",
|
||||
allowedRoleIds = Array.Empty<Guid>(),
|
||||
maxClients = (int?)null,
|
||||
});
|
||||
var publishResponse = await adminClient.SendPutJsonAsync(
|
||||
$"/api/admin/inbounds/{inbound.Id}/publish",
|
||||
new
|
||||
{
|
||||
isPublished = true,
|
||||
displayName = "Germany (VLESS)",
|
||||
allowedRoleIds = Array.Empty<Guid>(),
|
||||
maxClients = (int?)null,
|
||||
}
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, publishResponse.StatusCode);
|
||||
var published = await publishResponse.ReadAsAsync<InboundResponse>();
|
||||
Assert.True(published!.IsPublished);
|
||||
@@ -72,10 +100,17 @@ public class NodeInboundCrudTests(PnvPanelWebApplicationFactory factory)
|
||||
var (_, userToken) = await RegisterAndLoginAsync(userClient, userName, "P@ssw0rd123");
|
||||
userClient.UseBearerToken(userToken);
|
||||
|
||||
var response = await userClient.PostJsonAsync("/api/admin/nodes", new
|
||||
{
|
||||
name = "Node", baseAddress = "https://node.example.com", username = "admin", password = "pw", location = (string?)null,
|
||||
});
|
||||
var response = await userClient.PostJsonAsync(
|
||||
"/api/admin/nodes",
|
||||
new
|
||||
{
|
||||
name = "Node",
|
||||
baseAddress = "https://node.example.com",
|
||||
username = "admin",
|
||||
password = "pw",
|
||||
location = (string?)null,
|
||||
}
|
||||
);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
}
|
||||
|
||||
@@ -10,9 +10,19 @@ public class AuthFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
{
|
||||
private sealed record RegisterResponse(Guid Id, string UserName);
|
||||
|
||||
private sealed record CurrentUserResponse(Guid Id, string UserName, string Role, bool IsActivated, bool TelegramLinked);
|
||||
private sealed record CurrentUserResponse(
|
||||
Guid Id,
|
||||
string UserName,
|
||||
string Role,
|
||||
bool IsActivated,
|
||||
bool TelegramLinked
|
||||
);
|
||||
|
||||
private sealed record LoginResponse(string AccessToken, DateTimeOffset ExpiresAt, CurrentUserResponse User);
|
||||
private sealed record LoginResponse(
|
||||
string AccessToken,
|
||||
DateTimeOffset ExpiresAt,
|
||||
CurrentUserResponse User
|
||||
);
|
||||
|
||||
[Fact]
|
||||
public async Task RegisterLoginMeRefreshLogout_FullFlow_Succeeds()
|
||||
@@ -21,13 +31,19 @@ public class AuthFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
var userName = $"alice_{Guid.NewGuid():N}"[..20];
|
||||
const string password = "P@ssw0rd123";
|
||||
|
||||
var registerResponse = await client.PostJsonAsync("/api/auth/register", new { userName, password });
|
||||
var registerResponse = await client.PostJsonAsync(
|
||||
"/api/auth/register",
|
||||
new { userName, password }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, registerResponse.StatusCode);
|
||||
var registered = await registerResponse.ReadAsAsync<RegisterResponse>();
|
||||
Assert.NotNull(registered);
|
||||
Assert.Equal(userName, registered!.UserName);
|
||||
|
||||
var loginResponse = await client.PostJsonAsync("/api/auth/login", new { userName, password });
|
||||
var loginResponse = await client.PostJsonAsync(
|
||||
"/api/auth/login",
|
||||
new { userName, password }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, loginResponse.StatusCode);
|
||||
var login = await loginResponse.ReadAsAsync<LoginResponse>();
|
||||
Assert.NotNull(login);
|
||||
@@ -61,9 +77,15 @@ public class AuthFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
using var client = factory.CreateClient();
|
||||
var userName = $"bob_{Guid.NewGuid():N}"[..20];
|
||||
|
||||
await client.PostJsonAsync("/api/auth/register", new { userName, password = "CorrectPassword123" });
|
||||
await client.PostJsonAsync(
|
||||
"/api/auth/register",
|
||||
new { userName, password = "CorrectPassword123" }
|
||||
);
|
||||
|
||||
var loginResponse = await client.PostJsonAsync("/api/auth/login", new { userName, password = "WrongPassword123" });
|
||||
var loginResponse = await client.PostJsonAsync(
|
||||
"/api/auth/login",
|
||||
new { userName, password = "WrongPassword123" }
|
||||
);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, loginResponse.StatusCode);
|
||||
}
|
||||
@@ -74,10 +96,16 @@ public class AuthFlowTests(PnvPanelWebApplicationFactory factory)
|
||||
using var client = factory.CreateClient();
|
||||
var userName = $"carol_{Guid.NewGuid():N}"[..20];
|
||||
|
||||
var first = await client.PostJsonAsync("/api/auth/register", new { userName, password = "P@ssw0rd123" });
|
||||
var first = await client.PostJsonAsync(
|
||||
"/api/auth/register",
|
||||
new { userName, password = "P@ssw0rd123" }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, first.StatusCode);
|
||||
|
||||
var second = await client.PostJsonAsync("/api/auth/register", new { userName, password = "AnotherPass123!" });
|
||||
var second = await client.PostJsonAsync(
|
||||
"/api/auth/register",
|
||||
new { userName, password = "AnotherPass123!" }
|
||||
);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Conflict, second.StatusCode);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,21 @@ public class ConfigQuotaTests(PnvPanelWebApplicationFactory factory)
|
||||
|
||||
private sealed record SyncNodeResponse(int InboundsSynced, string Status);
|
||||
|
||||
private sealed record InboundResponse(Guid Id, Guid NodeId, string RemoteInboundId, string Protocol, string Remark, int Port, bool IsPublished);
|
||||
private sealed record InboundResponse(
|
||||
Guid Id,
|
||||
Guid NodeId,
|
||||
string RemoteInboundId,
|
||||
string Protocol,
|
||||
string Remark,
|
||||
int Port,
|
||||
bool IsPublished
|
||||
);
|
||||
|
||||
private sealed record ActivationRequestResponse(Guid Id, string? Comment, DateTimeOffset CreatedAt);
|
||||
private sealed record ActivationRequestResponse(
|
||||
Guid Id,
|
||||
string? Comment,
|
||||
DateTimeOffset CreatedAt
|
||||
);
|
||||
|
||||
private sealed record MyConfigsResponse(List<object> Configs, int MaxConfigs);
|
||||
|
||||
@@ -41,33 +53,44 @@ public class ConfigQuotaTests(PnvPanelWebApplicationFactory factory)
|
||||
var userRole = roles!.Single(r => r.Name == "user");
|
||||
|
||||
var updateRoleResponse = await adminClient.SendPutJsonAsync(
|
||||
$"/api/admin/roles/{userRole.Id}", new { maxConfigs = Quota, maxIpLimit = -1 });
|
||||
$"/api/admin/roles/{userRole.Id}",
|
||||
new { maxConfigs = Quota, maxIpLimit = -1 }
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, updateRoleResponse.StatusCode);
|
||||
|
||||
var registerNodeResponse = await adminClient.PostJsonAsync("/api/admin/nodes", new
|
||||
{
|
||||
name = $"QuotaNode-{Guid.NewGuid():N}"[..24],
|
||||
baseAddress = "https://quota-node.example.com",
|
||||
username = "admin",
|
||||
password = "node-panel-password",
|
||||
location = (string?)null,
|
||||
});
|
||||
var registerNodeResponse = await adminClient.PostJsonAsync(
|
||||
"/api/admin/nodes",
|
||||
new
|
||||
{
|
||||
name = $"QuotaNode-{Guid.NewGuid():N}"[..24],
|
||||
baseAddress = "https://quota-node.example.com",
|
||||
username = "admin",
|
||||
password = "node-panel-password",
|
||||
location = (string?)null,
|
||||
}
|
||||
);
|
||||
var node = await registerNodeResponse.ReadAsAsync<NodeResponse>();
|
||||
|
||||
var syncResponse = await adminClient.PostAsync($"/api/admin/nodes/{node!.Id}/sync", content: null);
|
||||
var syncResponse = await adminClient.PostAsync(
|
||||
$"/api/admin/nodes/{node!.Id}/sync",
|
||||
content: null
|
||||
);
|
||||
var sync = await syncResponse.ReadAsAsync<SyncNodeResponse>();
|
||||
Assert.Equal(1, sync!.InboundsSynced);
|
||||
|
||||
var inboundsResponse = await adminClient.GetAsync($"/api/admin/inbounds?nodeId={node.Id}");
|
||||
var inbound = (await inboundsResponse.ReadAsAsync<List<InboundResponse>>())!.Single();
|
||||
|
||||
var publishResponse = await adminClient.SendPutJsonAsync($"/api/admin/inbounds/{inbound.Id}/publish", new
|
||||
{
|
||||
isPublished = true,
|
||||
displayName = "Quota inbound",
|
||||
allowedRoleIds = new[] { userRole.Id },
|
||||
maxClients = (int?)null,
|
||||
});
|
||||
var publishResponse = await adminClient.SendPutJsonAsync(
|
||||
$"/api/admin/inbounds/{inbound.Id}/publish",
|
||||
new
|
||||
{
|
||||
isPublished = true,
|
||||
displayName = "Quota inbound",
|
||||
allowedRoleIds = new[] { userRole.Id },
|
||||
maxClients = (int?)null,
|
||||
}
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.OK, publishResponse.StatusCode);
|
||||
|
||||
using var userClient = factory.CreateClient();
|
||||
@@ -75,21 +98,29 @@ public class ConfigQuotaTests(PnvPanelWebApplicationFactory factory)
|
||||
var (_, userToken) = await RegisterAndLoginAsync(userClient, userName, "P@ssw0rd123");
|
||||
userClient.UseBearerToken(userToken);
|
||||
|
||||
var activationRequestResponse = await userClient.PostJsonAsync("/api/activation/request", new { comment = (string?)null });
|
||||
var activationRequest = await activationRequestResponse.ReadAsAsync<ActivationRequestResponse>();
|
||||
var approveResponse = await adminClient.PostAsync($"/api/admin/activation-requests/{activationRequest!.Id}/approve", content: null);
|
||||
var activationRequestResponse = await userClient.PostJsonAsync(
|
||||
"/api/activation/request",
|
||||
new { comment = (string?)null }
|
||||
);
|
||||
var activationRequest =
|
||||
await activationRequestResponse.ReadAsAsync<ActivationRequestResponse>();
|
||||
var approveResponse = await adminClient.PostAsync(
|
||||
$"/api/admin/activation-requests/{activationRequest!.Id}/approve",
|
||||
content: null
|
||||
);
|
||||
Assert.Equal(HttpStatusCode.NoContent, approveResponse.StatusCode);
|
||||
|
||||
var tasks = Enumerable.Range(0, ConcurrentAttempts).Select(async i =>
|
||||
{
|
||||
using var attemptClient = factory.CreateClient();
|
||||
attemptClient.UseBearerToken(userToken);
|
||||
return await attemptClient.PostJsonAsync("/api/configs", new
|
||||
var tasks = Enumerable
|
||||
.Range(0, ConcurrentAttempts)
|
||||
.Select(async i =>
|
||||
{
|
||||
inboundId = inbound.Id,
|
||||
label = $"device-{i}",
|
||||
using var attemptClient = factory.CreateClient();
|
||||
attemptClient.UseBearerToken(userToken);
|
||||
return await attemptClient.PostJsonAsync(
|
||||
"/api/configs",
|
||||
new { inboundId = inbound.Id, label = $"device-{i}" }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
var responses = await Task.WhenAll(tasks);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -24,5 +23,4 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\PnvPanel.Api\PnvPanel.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -4,22 +4,46 @@ namespace PnvPanel.IntegrationTests.TestSupport;
|
||||
|
||||
public static class AuthTestHelper
|
||||
{
|
||||
public sealed record CurrentUserResponse(Guid Id, string UserName, string Role, bool IsActivated, bool TelegramLinked);
|
||||
public sealed record CurrentUserResponse(
|
||||
Guid Id,
|
||||
string UserName,
|
||||
string Role,
|
||||
bool IsActivated,
|
||||
bool TelegramLinked
|
||||
);
|
||||
|
||||
public sealed record LoginResponse(string AccessToken, DateTimeOffset ExpiresAt, CurrentUserResponse User);
|
||||
public sealed record LoginResponse(
|
||||
string AccessToken,
|
||||
DateTimeOffset ExpiresAt,
|
||||
CurrentUserResponse User
|
||||
);
|
||||
|
||||
public static async Task<(Guid Id, string AccessToken)> RegisterAndLoginAsync(HttpClient client, string userName, string password)
|
||||
public static async Task<(Guid Id, string AccessToken)> RegisterAndLoginAsync(
|
||||
HttpClient client,
|
||||
string userName,
|
||||
string password
|
||||
)
|
||||
{
|
||||
var registerResponse = await client.PostJsonAsync("/api/auth/register", new { userName, password });
|
||||
var registerResponse = await client.PostJsonAsync(
|
||||
"/api/auth/register",
|
||||
new { userName, password }
|
||||
);
|
||||
registerResponse.EnsureSuccessStatusCode();
|
||||
|
||||
var (id, accessToken) = await LoginAsync(client, userName, password);
|
||||
return (id, accessToken);
|
||||
}
|
||||
|
||||
public static async Task<(Guid Id, string AccessToken)> LoginAsync(HttpClient client, string userName, string password)
|
||||
public static async Task<(Guid Id, string AccessToken)> LoginAsync(
|
||||
HttpClient client,
|
||||
string userName,
|
||||
string password
|
||||
)
|
||||
{
|
||||
var loginResponse = await client.PostJsonAsync("/api/auth/login", new { userName, password });
|
||||
var loginResponse = await client.PostJsonAsync(
|
||||
"/api/auth/login",
|
||||
new { userName, password }
|
||||
);
|
||||
loginResponse.EnsureSuccessStatusCode();
|
||||
var login = await loginResponse.ReadAsAsync<LoginResponse>();
|
||||
return (login!.User.Id, login.AccessToken);
|
||||
@@ -27,7 +51,11 @@ public static class AuthTestHelper
|
||||
|
||||
public static async Task<string> LoginAsAdminAsync(HttpClient client)
|
||||
{
|
||||
var (_, accessToken) = await LoginAsync(client, PnvPanelWebApplicationFactory.AdminUserName, PnvPanelWebApplicationFactory.AdminPassword);
|
||||
var (_, accessToken) = await LoginAsync(
|
||||
client,
|
||||
PnvPanelWebApplicationFactory.AdminUserName,
|
||||
PnvPanelWebApplicationFactory.AdminPassword
|
||||
);
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,13 @@ public sealed class FakeXuiPanelGateway : IXuiPanelGateway
|
||||
{
|
||||
public Result ValidateBaseAddress(Uri baseAddress) => Result.Success();
|
||||
|
||||
public Task<NodeProbeResult> ProbeAsync(Node node, CancellationToken cancellationToken)
|
||||
=> Task.FromResult(new NodeProbeResult(true, null));
|
||||
public Task<NodeProbeResult> ProbeAsync(Node node, CancellationToken cancellationToken) =>
|
||||
Task.FromResult(new NodeProbeResult(true, null));
|
||||
|
||||
public Task<Result<IReadOnlyList<RemoteInboundInfo>>> ListInboundsAsync(Node node, CancellationToken cancellationToken)
|
||||
public Task<Result<IReadOnlyList<RemoteInboundInfo>>> ListInboundsAsync(
|
||||
Node node,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
IReadOnlyList<RemoteInboundInfo> inbounds =
|
||||
[
|
||||
@@ -26,32 +29,53 @@ public sealed class FakeXuiPanelGateway : IXuiPanelGateway
|
||||
return Task.FromResult(Result.Success(inbounds));
|
||||
}
|
||||
|
||||
public void InvalidateClient(Guid nodeId)
|
||||
{
|
||||
}
|
||||
public void InvalidateClient(Guid nodeId) { }
|
||||
|
||||
public Task<Result<string>> AddClientAsync(
|
||||
Node node, string inboundRemoteId, VpnProtocol protocol, string clientEmail, string clientName, int limitIp,
|
||||
CancellationToken cancellationToken)
|
||||
=> Task.FromResult(Result.Success(Guid.NewGuid().ToString()));
|
||||
Node node,
|
||||
string inboundRemoteId,
|
||||
VpnProtocol protocol,
|
||||
string clientEmail,
|
||||
string clientName,
|
||||
int limitIp,
|
||||
CancellationToken cancellationToken
|
||||
) => Task.FromResult(Result.Success(Guid.NewGuid().ToString()));
|
||||
|
||||
public Task<Result> RemoveClientAsync(
|
||||
Node node, string inboundRemoteId, string clientExternalId, VpnProtocol protocol, CancellationToken cancellationToken)
|
||||
=> Task.FromResult(Result.Success());
|
||||
Node node,
|
||||
string inboundRemoteId,
|
||||
string clientExternalId,
|
||||
VpnProtocol protocol,
|
||||
CancellationToken cancellationToken
|
||||
) => Task.FromResult(Result.Success());
|
||||
|
||||
public Task<Result> UpdateClientAsync(
|
||||
Node node, string inboundRemoteId, string clientExternalId, VpnProtocol protocol,
|
||||
string name, bool enable, CancellationToken cancellationToken)
|
||||
=> Task.FromResult(Result.Success());
|
||||
Node node,
|
||||
string inboundRemoteId,
|
||||
string clientExternalId,
|
||||
VpnProtocol protocol,
|
||||
string name,
|
||||
bool enable,
|
||||
CancellationToken cancellationToken
|
||||
) => Task.FromResult(Result.Success());
|
||||
|
||||
public Task<Result<string>> BuildConnectionStringAsync(
|
||||
Node node, Inbound inbound, string clientExternalId, string clientName, string publicHost, CancellationToken cancellationToken)
|
||||
=> Task.FromResult(Result.Success("vless://fake-connection-string"));
|
||||
Node node,
|
||||
Inbound inbound,
|
||||
string clientExternalId,
|
||||
string clientName,
|
||||
string publicHost,
|
||||
CancellationToken cancellationToken
|
||||
) => Task.FromResult(Result.Success("vless://fake-connection-string"));
|
||||
|
||||
public Task<Result<IReadOnlyDictionary<string, ClientTrafficInfo>>> GetClientTrafficAsync(
|
||||
Node node, string inboundRemoteId, CancellationToken cancellationToken)
|
||||
Node node,
|
||||
string inboundRemoteId,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
IReadOnlyDictionary<string, ClientTrafficInfo> traffic = new Dictionary<string, ClientTrafficInfo>();
|
||||
IReadOnlyDictionary<string, ClientTrafficInfo> traffic =
|
||||
new Dictionary<string, ClientTrafficInfo>();
|
||||
return Task.FromResult(Result.Success(traffic));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,24 @@ public static class HttpClientJsonExtensions
|
||||
{
|
||||
public static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||||
|
||||
public static void UseBearerToken(this HttpClient client, string accessToken)
|
||||
=> client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
||||
public static void UseBearerToken(this HttpClient client, string accessToken) =>
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
|
||||
"Bearer",
|
||||
accessToken
|
||||
);
|
||||
|
||||
public static async Task<T?> ReadAsAsync<T>(this HttpResponseMessage response)
|
||||
=> await response.Content.ReadFromJsonAsync<T>(JsonOptions);
|
||||
public static async Task<T?> ReadAsAsync<T>(this HttpResponseMessage response) =>
|
||||
await response.Content.ReadFromJsonAsync<T>(JsonOptions);
|
||||
|
||||
public static Task<HttpResponseMessage> PostJsonAsync(this HttpClient client, string url, object body)
|
||||
=> client.PostAsJsonAsync(url, body, JsonOptions);
|
||||
public static Task<HttpResponseMessage> PostJsonAsync(
|
||||
this HttpClient client,
|
||||
string url,
|
||||
object body
|
||||
) => client.PostAsJsonAsync(url, body, JsonOptions);
|
||||
|
||||
public static Task<HttpResponseMessage> SendPutJsonAsync(this HttpClient client, string url, object body)
|
||||
=> client.PutAsJsonAsync(url, body, JsonOptions);
|
||||
public static Task<HttpResponseMessage> SendPutJsonAsync(
|
||||
this HttpClient client,
|
||||
string url,
|
||||
object body
|
||||
) => client.PutAsJsonAsync(url, body, JsonOptions);
|
||||
}
|
||||
|
||||
+17
-13
@@ -42,20 +42,24 @@ public sealed class PnvPanelWebApplicationFactory : WebApplicationFactory<Progra
|
||||
{
|
||||
builder.UseEnvironment("Development");
|
||||
|
||||
builder.ConfigureAppConfiguration((_, config) =>
|
||||
{
|
||||
config.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
builder.ConfigureAppConfiguration(
|
||||
(_, config) =>
|
||||
{
|
||||
["ConnectionStrings:Default"] = _postgres.GetConnectionString(),
|
||||
["AdminSeed:Username"] = AdminUserName,
|
||||
["AdminSeed:Password"] = AdminPassword,
|
||||
// Пусто — TelegramBotHostedService при пустом токене не стартует (см. Api/Telegram/TelegramBotHostedService.cs).
|
||||
["Telegram:BotToken"] = "",
|
||||
// Весь collection делит один TestServer/host — все запросы идут от одного "клиента",
|
||||
// дефолтный лимит 20/мин быстро исчерпывается. Поднимаем для тестового окружения.
|
||||
["RateLimiting:AuthPermitLimit"] = "10000",
|
||||
});
|
||||
});
|
||||
config.AddInMemoryCollection(
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
["ConnectionStrings:Default"] = _postgres.GetConnectionString(),
|
||||
["AdminSeed:Username"] = AdminUserName,
|
||||
["AdminSeed:Password"] = AdminPassword,
|
||||
// Пусто — TelegramBotHostedService при пустом токене не стартует (см. Api/Telegram/TelegramBotHostedService.cs).
|
||||
["Telegram:BotToken"] = "",
|
||||
// Весь collection делит один TestServer/host — все запросы идут от одного "клиента",
|
||||
// дефолтный лимит 20/мин быстро исчерпывается. Поднимаем для тестового окружения.
|
||||
["RateLimiting:AuthPermitLimit"] = "10000",
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user