mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 19:54:55 +00:00
Updeted Session Model and Session Opener Service
renamed RefreshToken in model to RefreshTokenHash - now we have a hash of token, not a token
This commit is contained in:
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Govor.Application.Interfaces.Authentication;
|
||||
using Govor.Application.Services.Authentication;
|
||||
using Govor.Data.Repositories.Exceptions;
|
||||
|
||||
namespace Govor.Application.Tests.Services.UserSessions;
|
||||
|
||||
@@ -14,138 +15,141 @@ public class UserSessionOpenerTests
|
||||
{
|
||||
private Mock<IUserSessionsRepository> _repositoryMock;
|
||||
private Mock<IJwtService> _jwtServiceMock;
|
||||
private Mock<ILogger<UserSessionOpener>> _loggerMock;
|
||||
private IOptions<JwtRefreshOption> _options;
|
||||
private Mock<IJwtTokenHasher> _jwtTokenHasherMock;
|
||||
private UserSessionOpener _service;
|
||||
private User _user;
|
||||
private Guid _sessionId;
|
||||
|
||||
private const string DeviceInfo = "Chrome on Windows";
|
||||
private const string GeneratedToken = "new-refresh-token";
|
||||
private const string NewAccessToken = "new-access-token";
|
||||
private const string RefreshToken = "new-refresh-token";
|
||||
private const string AccessToken = "new-access-token";
|
||||
private const string TokenHash = "hashed-refresh-token";
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_repositoryMock = new Mock<IUserSessionsRepository>();
|
||||
_jwtServiceMock = new Mock<IJwtService>();
|
||||
_loggerMock = new Mock<ILogger<UserSessionOpener>>();
|
||||
_options = Options.Create(new JwtRefreshOption { RefreshTokenLifetimeDays = 30 });
|
||||
|
||||
_sessionId = Guid.NewGuid();
|
||||
var loggerMock = new Mock<ILogger<UserSessionOpener>>();
|
||||
_jwtTokenHasherMock = new Mock<IJwtTokenHasher>();
|
||||
var options = Options.Create(new JwtRefreshOption { RefreshTokenLifetimeDays = 30 });
|
||||
|
||||
_user = new User
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Username = "test",
|
||||
PasswordHash = "hashed",
|
||||
Description = "some description",
|
||||
PasswordHash = "hashed-password",
|
||||
IconId = Guid.NewGuid(),
|
||||
CreatedOn = DateOnly.FromDateTime(DateTime.UtcNow),
|
||||
WasOnline = DateTime.UtcNow,
|
||||
InviteId = Guid.NewGuid()
|
||||
CreatedOn = DateOnly.FromDateTime(DateTime.Now),
|
||||
WasOnline = DateTime.Now,
|
||||
InviteId = Guid.NewGuid(),
|
||||
// ...
|
||||
};
|
||||
|
||||
_jwtServiceMock.Setup(j => j.GenerateRefreshTokenAsync(_user)).ReturnsAsync(RefreshToken);
|
||||
_jwtTokenHasherMock.Setup(h => h.HashToken(RefreshToken)).Returns(TokenHash);
|
||||
|
||||
_jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user, It.IsAny<Guid>())).ReturnsAsync(AccessToken);
|
||||
|
||||
_jwtServiceMock.Setup(j => j.GenerateRefreshTokenAsync(_user)).ReturnsAsync(GeneratedToken);
|
||||
_jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user, _sessionId)).ReturnsAsync(NewAccessToken);
|
||||
|
||||
_service = new UserSessionOpener(
|
||||
_repositoryMock.Object,
|
||||
_jwtServiceMock.Object,
|
||||
_options,
|
||||
_loggerMock.Object
|
||||
_jwtTokenHasherMock.Object,
|
||||
options,
|
||||
loggerMock.Object
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// UpdateExistingSessionAsync
|
||||
[Test]
|
||||
public async Task OpenSessionAsync_ShouldReturnExistingToken_IfSessionValid()
|
||||
public async Task OpenSessionAsync_ShouldUpdateExistingSession_WhenFoundByDeviceInfo()
|
||||
{
|
||||
// Arrange
|
||||
var session = new UserSession
|
||||
var existingSessionId = Guid.NewGuid();
|
||||
var existingSession = new UserSession
|
||||
{
|
||||
Id = _sessionId,
|
||||
Id = existingSessionId,
|
||||
UserId = _user.Id,
|
||||
DeviceInfo = DeviceInfo,
|
||||
RefreshToken = "valid-token",
|
||||
RefreshTokenHash = "old-hash",
|
||||
CreatedAt = DateTime.UtcNow.AddDays(-10),
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(10),
|
||||
IsRevoked = false
|
||||
IsRevoked = true
|
||||
};
|
||||
|
||||
_repositoryMock.Setup(r => r.GetByUserIdAsync(_user.Id)).ReturnsAsync(new List<UserSession> { session });
|
||||
|
||||
// Act
|
||||
var result = await _service.OpenSessionAsync(_user, DeviceInfo);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.refreshToken, Is.EqualTo(GeneratedToken));
|
||||
Assert.That(result.accessToken, Is.EqualTo(NewAccessToken));
|
||||
_repositoryMock.Verify(r => r.UpdateAsync(It.Is<UserSession>(s =>
|
||||
s.RefreshToken == GeneratedToken &&
|
||||
s.IsRevoked == false)), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task OpenSessionAsync_ShouldUpdateSession_IfExpiredOrRevoked()
|
||||
{
|
||||
// Arrange
|
||||
var session = new UserSession
|
||||
{
|
||||
Id = _sessionId,
|
||||
UserId = _user.Id,
|
||||
DeviceInfo = DeviceInfo,
|
||||
RefreshToken = "old-token",
|
||||
CreatedAt = DateTime.UtcNow.AddDays(-40),
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(-1),
|
||||
IsRevoked = false
|
||||
};
|
||||
|
||||
_repositoryMock.Setup(r => r.GetByUserIdAsync(_user.Id)).ReturnsAsync(new List<UserSession> { session });
|
||||
|
||||
// Act
|
||||
var result = await _service.OpenSessionAsync(_user, DeviceInfo);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.refreshToken, Is.EqualTo(GeneratedToken));
|
||||
Assert.That(result.accessToken, Is.EqualTo(NewAccessToken));
|
||||
_repositoryMock.Verify(r => r.UpdateAsync(It.Is<UserSession>(s =>
|
||||
s.RefreshToken == GeneratedToken &&
|
||||
s.IsRevoked == false)), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task OpenSessionAsync_ShouldCreateNewSession_IfNoneExists()
|
||||
{
|
||||
// Arrange
|
||||
_repositoryMock.Setup(r => r.GetByUserIdAsync(_user.Id)).ReturnsAsync(new List<UserSession>());
|
||||
_jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user, It.IsAny<Guid>())).ReturnsAsync(NewAccessToken);
|
||||
_repositoryMock.Setup(r => r.GetByUserIdAsync(_user.Id)).ReturnsAsync(new List<UserSession> { existingSession });
|
||||
|
||||
// Act
|
||||
var result = await _service.OpenSessionAsync(_user, DeviceInfo);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.refreshToken, Is.EqualTo(GeneratedToken));
|
||||
Assert.That(result.accessToken, Is.EqualTo(NewAccessToken));
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result.refreshToken, Is.EqualTo(RefreshToken), "Должен быть возвращен новый RefreshToken.");
|
||||
Assert.That(result.accessToken, Is.EqualTo(AccessToken), "Должен быть возвращен новый AccessToken.");
|
||||
});
|
||||
|
||||
_repositoryMock.Verify(r => r.UpdateAsync(It.Is<UserSession>(s =>
|
||||
s.Id == existingSessionId &&
|
||||
s.RefreshTokenHash == TokenHash &&
|
||||
s.IsRevoked == false &&
|
||||
s.ExpiresAt > DateTime.UtcNow
|
||||
)), Times.Once, "Должен быть вызван метод UpdateAsync с новыми данными.");
|
||||
|
||||
_repositoryMock.Verify(r => r.AddAsync(It.IsAny<UserSession>()), Times.Never);
|
||||
}
|
||||
|
||||
// CreateNewSessionAsync
|
||||
|
||||
[Test]
|
||||
public async Task OpenSessionAsync_ShouldCreateNewSession_IfNoneFound()
|
||||
{
|
||||
// Arrange
|
||||
_repositoryMock.Setup(r => r.GetByUserIdAsync(_user.Id)).ReturnsAsync(new List<UserSession>());
|
||||
|
||||
// Act
|
||||
var result = await _service.OpenSessionAsync(_user, DeviceInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result.refreshToken, Is.EqualTo(RefreshToken), "Должен быть возвращен новый RefreshToken.");
|
||||
Assert.That(result.accessToken, Is.EqualTo(AccessToken), "Должен быть возвращен новый AccessToken.");
|
||||
});
|
||||
|
||||
_repositoryMock.Verify(r => r.AddAsync(It.Is<UserSession>(s =>
|
||||
s.UserId == _user.Id &&
|
||||
s.DeviceInfo == DeviceInfo &&
|
||||
s.RefreshToken == GeneratedToken
|
||||
)), Times.Once);
|
||||
s.RefreshTokenHash == TokenHash &&
|
||||
s.IsRevoked == false
|
||||
)), Times.Once, "Должен быть вызван метод AddAsync для создания новой сессии.");
|
||||
|
||||
_repositoryMock.Verify(r => r.UpdateAsync(It.IsAny<UserSession>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task OpenSessionAsync_ShouldCreateNewSession_WhenNotFoundByKeyExceptionThrown()
|
||||
public async Task OpenSessionAsync_ShouldCreateNewSession_WhenRepositoryThrowsNotFoundByKeyException()
|
||||
{
|
||||
// Arrange
|
||||
_repositoryMock
|
||||
.Setup(r => r.GetByUserIdAsync(_user.Id))
|
||||
.ThrowsAsync(new Govor.Data.Repositories.Exceptions.NotFoundByKeyException<Guid>(_user.Id, "userId"));
|
||||
_jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user, It.IsAny<Guid>())).ReturnsAsync(NewAccessToken);
|
||||
.ThrowsAsync(new NotFoundByKeyException<Guid>(_user.Id, "userId"));
|
||||
|
||||
// Act
|
||||
var result = await _service.OpenSessionAsync(_user, DeviceInfo);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.refreshToken, Is.EqualTo(GeneratedToken));
|
||||
Assert.That(result.accessToken, Is.EqualTo(NewAccessToken));
|
||||
_repositoryMock.Verify(r => r.AddAsync(It.IsAny<UserSession>()), Times.Once);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(result.refreshToken, Is.EqualTo(RefreshToken), "Должен быть возвращен новый RefreshToken.");
|
||||
Assert.That(result.accessToken, Is.EqualTo(AccessToken), "Должен быть возвращен новый AccessToken.");
|
||||
});
|
||||
|
||||
_repositoryMock.Verify(r => r.AddAsync(It.Is<UserSession>(s =>
|
||||
s.RefreshTokenHash == TokenHash
|
||||
)), Times.Once, "Должен быть вызван AddAsync после перехвата исключения.");
|
||||
|
||||
_repositoryMock.Verify(r => r.UpdateAsync(It.IsAny<UserSession>()), Times.Never);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user