using Govor.Application.Services.UserSessions; using Govor.Core.Models; using Govor.Core.Models.Users; using Govor.Core.Repositories.UserSessionsRepository; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using Govor.Application.Interfaces.Authentication; using Govor.Application.Services.Authentication; namespace Govor.Application.Tests.Services.UserSessions; [TestFixture] public class UserSessionOpenerTests { private Mock _repositoryMock; private Mock _jwtServiceMock; private Mock> _loggerMock; private IOptions _options; private UserSessionOpener _service; private User _user; private const string DeviceInfo = "Chrome on Windows"; private const string GeneratedToken = "new-refresh-token"; private const string NewAccessToken = "new-access-token"; [SetUp] public void Setup() { _repositoryMock = new Mock(); _jwtServiceMock = new Mock(); _loggerMock = new Mock>(); _options = Options.Create(new JwtRefreshOption { RefreshTokenLifetimeDays = 30 }); // Инициализируем пользователя ДО моков, чтобы не было null в Setup _user = new User { Id = Guid.NewGuid(), Username = "test", PasswordHash = "hashed", IconId = Guid.NewGuid(), CreatedOn = DateOnly.FromDateTime(DateTime.UtcNow), WasOnline = DateTime.UtcNow, InviteId = Guid.NewGuid() }; _jwtServiceMock.Setup(j => j.GenerateRefreshTokenAsync(_user)).ReturnsAsync(GeneratedToken); _jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user)).ReturnsAsync(NewAccessToken); _service = new UserSessionOpener( _repositoryMock.Object, _jwtServiceMock.Object, _options, _loggerMock.Object ); } [Test] public async Task OpenSessionAsync_ShouldReturnExistingToken_IfSessionValid() { // Arrange var session = new UserSession { UserId = _user.Id, DeviceInfo = DeviceInfo, RefreshToken = "valid-token", CreatedAt = DateTime.UtcNow.AddDays(-10), ExpiresAt = DateTime.UtcNow.AddDays(10), IsRevoked = false }; _repositoryMock.Setup(r => r.GetByUserIdAsync(_user.Id)).ReturnsAsync(new List { 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(s => s.RefreshToken == GeneratedToken && s.IsRevoked == false)), Times.Once); } [Test] public async Task OpenSessionAsync_ShouldUpdateSession_IfExpiredOrRevoked() { // Arrange var session = new UserSession { 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 { 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(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()); // 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.Is(s => s.UserId == _user.Id && s.DeviceInfo == DeviceInfo && s.RefreshToken == GeneratedToken )), Times.Once); } [Test] public async Task OpenSessionAsync_ShouldCreateNewSession_WhenNotFoundByKeyExceptionThrown() { // Arrange _repositoryMock .Setup(r => r.GetByUserIdAsync(_user.Id)) .ThrowsAsync(new Govor.Data.Repositories.Exceptions.NotFoundByKeyException(_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()), Times.Once); } }